From c848fdb2c704268d9a9412f5153f2531dae02daa Mon Sep 17 00:00:00 2001 From: Per Hedbor <ph@opera.com> Date: Fri, 13 Sep 2013 13:01:04 +0200 Subject: [PATCH] Added an _object_size() function. It tries to aproximate how large an object is in RAM. --- src/builtin_functions.c | 129 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/src/builtin_functions.c b/src/builtin_functions.c index 15dcd69efc..40f92347b4 100644 --- a/src/builtin_functions.c +++ b/src/builtin_functions.c @@ -7609,6 +7609,132 @@ PMOD_EXPORT void f__memory_usage(INT32 args) f_aggregate_mapping(DO_NOT_WARN(Pike_sp - ss)); } +/* estimate the size of an svalue, not including objects. + this is used from size_object +*/ +static unsigned int rec_size_svalue( struct svalue *s, struct mapping **m ) +{ + unsigned int res = 0; + int i; + ptrdiff_t node_ref; + INT32 e; + struct keypair *k; + + switch( s->type ) + { + case PIKE_T_STRING: + /* FIXME: This makes assumptions about the threshold for short strings. */ + if( s->u.string->flags & STRING_IS_SHORT ) + return 16 / s->u.string->refs; + return ((s->u.string->len << s->u.string->size_shift) + + sizeof(struct pike_string)) / s->u.string->refs; + case PIKE_T_INT: + case PIKE_T_OBJECT: + case PIKE_T_FLOAT: + case PIKE_T_FUNCTION: + case PIKE_T_TYPE: + return 0; + } + + if( !*m ) + *m = allocate_mapping( 10 ); + else if( low_mapping_lookup( *m, s ) ) + return 0; // already counted. + + low_mapping_insert( *m, s, &svalue_int_one, 0 ); + switch( s->type ) + { + case PIKE_T_ARRAY: + res = sizeof( struct array ); + for( i=0; i<s->u.array->size; i++ ) + res += sizeof(struct svalue) + rec_size_svalue( s->u.array->item+i, m ); + break; + + case PIKE_T_MULTISET: + res = sizeof(struct multiset); + node_ref = multiset_last( s->u.multiset ); + while( node_ref != -1 ) + { + res += rec_size_svalue( get_multiset_value (s->u.multiset, node_ref), m ) + + sizeof(struct svalue); + node_ref = multiset_prev( s->u.multiset, node_ref ); + } + break; + + case PIKE_T_MAPPING: + res = sizeof(struct mapping) + sizeof(struct mapping_data); + NEW_MAPPING_LOOP( s->u.mapping->data ) + { + res += rec_size_svalue( &k->ind, m ); + res += rec_size_svalue( &k->val, m ); + res += sizeof( struct keypair ); + } + break; + } + return res / *s->u.refs; +} + +/*! @decl int size_object(object o) + *! @belongs Debug + *! + *! Return the aproximate size of the object, in bytes. + *! This might not work very well for native objects + *! + *! + *! The function tries to estimate the memory usage of variables + *! belonging to the object. + *! + *! It will not, however, include the size of objects assigned to + *! variables in the object. + */ +static void f__size_object( INT32 args ) +{ + size_t sum; + unsigned int i; + struct object *o; + struct program *p; + struct mapping *map = NULL; + if( Pike_sp[-1].type != PIKE_T_OBJECT ) + Pike_error("Expected an object as argument\n"); + o = Pike_sp[-1].u.object; + + if( !(p=o->prog) ) + { + pop_stack(); + push_int(0); + return; + } + sum = sizeof(struct object); + sum += p->storage_needed; + + Pike_sp++; + for (i = 0; i < p->num_identifier_references; i++) + { + struct reference *ref = PTR_FROM_INT(p, i); + struct identifier *id = ID_FROM_PTR(p, ref); + struct inherit *inh = p->inherits; + if (!IDENTIFIER_IS_VARIABLE(id->identifier_flags) || + id->run_time_type == PIKE_T_GET_SET) + { + continue; + } + + /* NOTE: makes the assumption that a variable saved in an + * object has at least one reference. + */ + low_object_index_no_free(Pike_sp-1, o, i + inh->identifier_level); + if (REFCOUNTED_TYPE(TYPEOF(Pike_sp[-1]))) + sub_ref( Pike_sp[-1].u.dummy ); + sum += rec_size_svalue(Pike_sp-1, &map); + } + Pike_sp--; + if( map ) free_mapping(map); + + pop_stack(); + push_int(sum); +} + + /*! @decl mixed _next(mixed x) *! *! Find the next object/array/mapping/multiset/program or string. @@ -10029,6 +10155,9 @@ void init_builtin_efuns(void) ADD_EFUN("_memory_usage",f__memory_usage, tFunc(tNone,tMap(tStr,tInt)),OPT_EXTERNAL_DEPEND); + ADD_EFUN("_size_object",f__size_object, + tFunc(tObj,tInt),OPT_EXTERNAL_DEPEND); + /* function(:int) */ ADD_EFUN("gc",f_gc,tFunc(tNone,tInt),OPT_SIDE_EFFECT); -- GitLab