pike.git / src / modules / _Debug / debug.cmod

version» Context lines:

pike.git/src/modules/_Debug/debug.cmod:1: + /* -*- c -*- + || This file is part of Pike. For copyright information see COPYRIGHT. + || Pike is distributed under GPL, LGPL and MPL. See the file COPYING + || for more information. + */    -  + #include "module.h" + #include "pike_error.h" + #include "interpret.h" + #include "pike_embed.h" + #include "module_support.h" + #include "builtin_functions.h" + #include "gc.h" + #include "opcodes.h" + #include "bignum.h" +  + DECLARATIONS +  + /*! @module Debug +  */ +  + /*! @decl int(0..) map_all_objects(function(object:void) cb) +  *! +  *! Call cb for all objects that currently exist. The callback will +  *! not be called with destructed objects as it's argument. +  *! +  *! Objects might be missed if @[cb] creates new objects or destroys +  *! old ones. +  *! +  *! This function is only intended to be used for debug purposes. +  *! +  *! @returns +  *! The total number of objects +  *! +  *! @seealso +  *! @[next_object()], @[find_all_clones()] +  */ + PIKEFUN int(0..) map_all_objects(function(object:void) cb) + { +  struct object *o = first_object; +  INT32 total = 0; +  +  while( o ) +  { +  struct object *next = o->next; +  if( o->prog ) +  { +  ref_push_object( o ); +  safe_apply_svalue( Pike_sp-2, 1, 1 ); +  pop_stack(); +  } +  total++; +  o = next; +  } +  RETURN total; + } +  + /*! @decl array(object) find_all_clones(program p, @ +  *! int(0..1)|void include_subclasses) +  *! +  *! Return an array with all objects that are clones of @[p]. +  *! +  *! @param p +  *! Program that the objects should be a clone of. +  *! +  *! @param include_subclasses +  *! If true, include also objects that are clones of programs +  *! that have inherited @[p]. Note that this adds significant +  *! overhead. +  *! +  *! This function is only intended to be used for debug purposes. +  *! +  *! @seealso +  *! @[map_all_objects()] +  */ + PIKEFUN array(object) find_all_clones(program|function prog, +  int(0..1)|void include_subclasses) + { +  struct object *o = first_object; +  struct program *p = program_from_svalue(prog); +  +  if (!p) { +  SIMPLE_ARG_TYPE_ERROR("Debug.find_all_clones", 1, "program"); +  } +  +  if (include_subclasses && !include_subclasses->u.integer) { +  include_subclasses = NULL; +  } +  +  BEGIN_AGGREGATE_ARRAY(10) { +  +  for (o = first_object; o; o = o->next) { +  if (o->prog == p) { +  ref_push_object(o); +  DO_AGGREGATE_ARRAY(120); +  continue; +  } +  if (include_subclasses && o->prog && +  (o->prog->num_inherits > p->num_inherits)) { +  int e; +  /* Check if o->prog has inherited p. */ +  if (o->prog->storage_needed < p->storage_needed) continue; +  for (e = o->prog->num_inherits + 1 - p->num_inherits; e-- > 1;) { +  if (o->prog->inherits[e].prog == p) { +  /* Found. */ +  ref_push_object(o); +  DO_AGGREGATE_ARRAY(120); +  break; +  } +  } +  } +  } +  +  } END_AGGREGATE_ARRAY; + } +  + /*! @decl int refs(string|array|mapping|multiset|function|object|program o) +  *! +  *! Return the number of references @[o] has. +  *! +  *! It is mainly meant for debugging the Pike runtime, but can also be +  *! used to control memory usage. +  *! +  *! @note +  *! Note that the number of references will always be at least one since +  *! the value is located on the stack when this function is executed. +  *! +  *! @seealso +  *! @[next()], @[prev()] +  */ + PIKEFUN int refs(string|array|mapping|multiset|function|object|program o) + { +  if(!REFCOUNTED_TYPE(TYPEOF(*o))) +  SIMPLE_ARG_TYPE_ERROR("refs", 1, +  "array|mapping|multiset|object|" +  "function|program|string|type"); +  RETURN o->u.refs[0]; + } +  + /*! @decl object next_object(object o) +  *! @decl object next_object() +  *! +  *! Returns the next object from the list of all objects. +  *! +  *! All objects are stored in a linked list. +  *! +  *! @returns +  *! If no arguments have been given @[next_object()] will return the first +  *! object from the list. +  *! +  *! If @[o] has been specified the object after @[o] on the list will be +  *! returned. +  *! +  *! @note +  *! This function is not recomended to use. +  *! +  *! @seealso +  *! @[destruct()] +  */ + PIKEFUN object next_object(void|object o) + { +  if (!o) +  o = first_object; +  else +  o = Pike_sp[-args].u.object->next; +  +  while(o && !o->prog) o=o->next; +  +  pop_n_elems(args); +  if(o) +  ref_push_object(o); +  else +  push_int(0); + } +  + /*! @decl mixed next(mixed x) +  *! +  *! Find the next object/array/mapping/multiset/program or string. +  *! +  *! All objects, arrays, mappings, multisets, programs and strings are +  *! stored in linked lists inside Pike. This function returns the next +  *! item on the corresponding list. It is mainly meant for debugging +  *! the Pike runtime, but can also be used to control memory usage. +  *! +  *! @seealso +  *! @[next_object()], @[prev()] +  */ + PIKEFUN mixed next(mixed x) + rawtype tOr6(tFunc(tStr,tStr), +  tFunc(tObj,tObj), +  tFunc(tMapping,tMapping), +  tFunc(tMultiset,tMultiset), +  tFunc(tPrg(tObj),tPrg(tObj)), +  tFunc(tArray,tArray)); + { +  struct svalue tmp = *x; +  switch(TYPEOF(tmp)) +  { +  case T_OBJECT: +  tmp.u.object=tmp.u.object->next; +  while(tmp.u.object && !tmp.u.object->prog) +  tmp.u.object=tmp.u.object->next; +  break; +  case T_ARRAY: tmp.u.array=tmp.u.array->next; break; +  case T_MAPPING: tmp.u.mapping=tmp.u.mapping->next; break; +  case T_MULTISET:tmp.u.multiset=tmp.u.multiset->next; break; +  case T_PROGRAM: tmp.u.program=tmp.u.program->next; break; +  case T_STRING: tmp.u.string=next_pike_string(tmp.u.string); break; +  default: +  SIMPLE_ARG_TYPE_ERROR("next", 1, +  "object|array|mapping|multiset|program|string"); +  } +  +  if(tmp.u.refs) +  assign_svalue(Pike_sp-1, &tmp); +  else +  { +  pop_stack(); +  push_int(0); +  } + } +  + /*! @decl mixed prev(mixed x) +  *! +  *! Find the previous object/array/mapping/multiset or program. +  *! +  *! All objects, arrays, mappings, multisets and programs are +  *! stored in linked lists inside Pike. This function returns the previous +  *! item on the corresponding list. It is mainly meant for debugging +  *! the Pike runtime, but can also be used to control memory usage. +  *! +  *! @note +  *! Unlike @[next()] this function does not work on strings. +  *! +  *! @seealso +  *! @[next_object()], @[next()] +  */ + PIKEFUN mixed prev(mixed x) + rawtype tOr5(tFunc(tObj,tObj), +  tFunc(tMapping,tMapping), +  tFunc(tMultiset,tMultiset), +  tFunc(tPrg(tObj),tPrg(tObj)), +  tFunc(tArray,tArray)); + { +  struct svalue tmp = *x; +  switch(TYPEOF(tmp)) +  { +  case T_OBJECT: +  tmp.u.object=tmp.u.object->prev; +  while(tmp.u.object && !tmp.u.object->prog) +  tmp.u.object=tmp.u.object->prev; +  break; +  case T_ARRAY: tmp.u.array=tmp.u.array->prev; break; +  case T_MAPPING: tmp.u.mapping=tmp.u.mapping->prev; break; +  case T_MULTISET:tmp.u.multiset=tmp.u.multiset->prev; break; +  case T_PROGRAM: tmp.u.program=tmp.u.program->prev; break; +  default: +  SIMPLE_ARG_TYPE_ERROR("prev", 1, "object|array|mapping|multiset|program"); +  } +  if(tmp.u.refs) +  assign_svalue(Pike_sp-1, &tmp); +  else +  { +  pop_stack(); +  push_int(0); +  } + } +  + #ifdef PIKE_DEBUG + /* This function is for debugging *ONLY* +  * do not document please. /Hubbe +  */ + PIKEFUN int leak(array|mapping|multiset|object|function|program|string|type val) +  export; + { +  INT32 i; +  +  if(!REFCOUNTED_TYPE(TYPEOF(*val))) +  SIMPLE_ARG_TYPE_ERROR("leak", 1, +  "array|mapping|multiset|object|" +  "function|program|string|type"); +  +  add_ref(val->u.dummy); +  i = val->u.refs[0]; +  RETURN i; + } +  + /*! @decl int(0..) debug(int(0..) level) +  *! +  *! Set the run-time debug level. +  *! +  *! @returns +  *! The old debug level will be returned. +  *! +  *! @note +  *! This function is only available if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN int(0..) debug(int(0..) d) +  export; + { +  pop_n_elems(args); +  push_int(d_flag); +  d_flag = d; + } +  + /*! @decl int(0..) optimizer_debug(int(0..) level) +  *! +  *! Set the optimizer debug level. +  *! +  *! @returns +  *! The old optimizer debug level will be returned. +  *! +  *! @note +  *! This function is only available if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN int(0..) optimizer_debug(int(0..) l) +  export; + { +  pop_n_elems(args); +  push_int(l_flag); +  l_flag = l; + } +  + /*! @decl int(0..) assembler_debug(int(0..) level) +  *! +  *! Set the assembler debug level. +  *! +  *! @returns +  *! The old assembler debug level will be returned. +  *! +  *! @note +  *! This function is only available if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN int(0..) assembler_debug(int(0..) l) +  export; + { +  pop_n_elems(args); +  push_int(a_flag); +  a_flag = l; + } +  + /*! @decl void dump_program_tables(program p, int(0..)|void indent) +  *! +  *! Dumps the internal tables for the program @[p] on stderr. +  *! +  *! @param p +  *! Program to dump. +  *! +  *! @param indent +  *! Number of spaces to indent the output. +  */ + PIKEFUN void dump_program_tables(program p, int(0..)|void indent) + { +  dump_program_tables(p, indent?indent->u.integer:0); + } +  + /*! @decl mixed locate_references(string|array|mapping| @ +  *! multiset|function|object| @ +  *! program|type o) +  *! @belongs Debug +  *! +  *! This function is mostly intended for debugging. It will search through +  *! all data structures in Pike looking for @[o] and print the +  *! locations on stderr. @[o] can be anything but @expr{int@} or +  *! @expr{float@}. +  *! +  *! @note +  *! This function only exists if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN mixed locate_references(mixed o) +  rawtype tFunc(tSetvar(1,tMix),tVar(1)); + { +  locate_references(o); + } +  + /*! @decl mixed describe(mixed x) +  *! @belongs Debug +  *! +  *! Prints out a description of the thing @[x] to standard error. +  *! The description contains various internal info associated with +  *! @[x]. +  *! +  *! @note +  *! This function only exists if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN mixed describe(mixed x) +  rawtype tFunc(tSetvar(1,tMix),tVar(1)); + { +  debug_describe_svalue(debug_malloc_pass(&Pike_sp[-1])); + } +  + /*! @decl void gc_set_watch(array|multiset|mapping|object|function|program|string x) +  *! @belongs Debug +  *! +  *! Sets a watch on the given thing, so that the gc will print a +  *! message whenever it's encountered. Intended to be used together +  *! with breakpoints to debug the garbage collector. +  *! +  *! @note +  *! This function only exists if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN void gc_set_watch(array|multiset|mapping|object|function|program|string x) + { +  gc_watch(&Pike_sp[-1]); + } +  + /*! @decl void dump_backlog() +  *! @belongs Debug +  *! +  *! Dumps the 1024 latest executed opcodes, along with the source +  *! code lines, to standard error. The backlog is only collected on +  *! debug level 1 or higher, set with @[_debug] or with the @tt{-d@} +  *! argument on the command line. +  *! +  *! @note +  *! This function only exists if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN void dump_backlog() + { +  dump_backlog(); + } +  + #ifdef YYDEBUG +  + /*! @decl int(0..) compiler_trace(int(0..) level) +  *! +  *! Set the compiler trace level. +  *! +  *! @returns +  *! The old compiler trace level will be returned. +  *! +  *! @note +  *! This function is only available if the Pike runtime has been compiled +  *! with RTL debug. +  */ + PIKEFUN int(0..) compiler_trace(int(0..) yyd) +  export; + { +  extern int yydebug; +  pop_n_elems(args); +  push_int(yydebug); +  yydebug = yyd; + } +  + #endif /* YYDEBUG */ +  + /*! @decl void disassemble(function fun) +  *! +  *! Disassemble a Pike function to @[Stdio.stderr]. +  *! +  *! @note +  *! This function is only available if the Pike runtime +  *! has been compiled with debug enabled. +  */ + PIKEFUN void disassemble(function fun) + { +  if ((TYPEOF(*fun) != T_FUNCTION) || +  (SUBTYPEOF(*fun) == FUNCTION_BUILTIN)) { +  WERR("Disassembly only supported for functions implemented in Pike.\n"); +  } else if (!fun->u.object->prog) { +  WERR("Function in destructed object.\n"); +  } else { +  int f = SUBTYPEOF(*fun); +  struct reference *ptr = PTR_FROM_INT(fun->u.object->prog, f); +  struct program *p = PROG_FROM_PTR(fun->u.object->prog, ptr); +  struct identifier *id = p->identifiers + ptr->identifier_offset; +  if (id->func.offset >= 0) { +  struct pike_string *tripples = +  p->strings[read_program_data(p->program + id->func.offset, -1)]; +  switch(tripples->size_shift) { + #define CASE(SHIFT) \ +  case SHIFT: \ +  { \ +  PIKE_CONCAT(p_wchar, SHIFT) *str = \ +  PIKE_CONCAT(STR, SHIFT)(tripples); \ +  int i=0; \ +  while(i < tripples->len) { \ +  WERR("@@@ %d: %s, %d, %d\n", \ +  i/3, \ +  instrs[*str - F_OFFSET]. \ +  name, \ +  str[1], str[2]); \ +  str += 3; \ +  i += 3; \ +  } \ +  } \ +  break +  CASE(0); +  CASE(1); +  CASE(2); + #undef CASE +  } +  } else { +  WERR("Prototype.\n"); +  } +  } + } +  + #endif /* PIKE_DEBUG */ +  + #ifdef DEBUG_MALLOC +  + /*! @decl void reset_dmalloc() +  *! +  *! @note +  *! Only available when compiled with dmalloc. +  */ + PIKEFUN void reset_dmalloc() + { +  reset_debug_malloc(); + } +  + extern char * dynamic_location(const char *file, INT_TYPE line); + extern char * dmalloc_default_location; +  + /*! @decl void dmalloc_set_name() +  *! +  *! @note +  *! Only available when compiled with dmalloc. +  */ + PIKEFUN void dmalloc_set_name() + { +  dmalloc_default_location=0; + } +  + /*! @decl void dmalloc_set_name(string filename, int(1..) linenumber) +  *! +  *! @note +  *! Only available when compiled with dmalloc. +  */ + PIKEFUN void dmalloc_set_name(string filename, int(1..) linenumber) + { +  dmalloc_default_location = dynamic_location(filename->str, linenumber); + } +  + extern void list_open_fds(void); +  + /*! @decl void list_open_fds() +  *! +  *! @note +  *! Only available when compiled with dmalloc. +  */ + PIKEFUN void list_open_fds() + { +  list_open_fds(); + } +  + /*! @decl void dump_dmalloc_locations(string|array|mapping| @ +  *! multiset|function|object| @ +  *! program|type o) +  *! +  *! @note +  *! Only available when compiled with dmalloc. +  */ + PIKEFUN void dump_dmalloc_locations(string|array|mapping|multiset|function|object|program|type o) + { +  if(!REFCOUNTED_TYPE(TYPEOF(*o))) +  SIMPLE_ARG_TYPE_ERROR("refs", 1, +  "array|mapping|multiset|object|" +  "function|program|string|type"); +  debug_malloc_dump_references (o->u.refs, 2, 1, 0); + } + #endif /* DEBUG_MALLOC */ +  + /*! @decl mapping(string:int) get_program_layout(program p) +  *! Returns a mapping which describes the layout of compiled machine +  *! code in the program @expr{p@}. The indices of the returned mapping +  *! are function names, the values the starting address of the compiled +  *! function. The total size of the program code is stored with index +  *! @expr{0@}. +  */ + PIKEFUN mapping get_program_layout(program p) { +  size_t i; +  struct mapping *m = allocate_mapping(64); +  +  if(p->flags & PROGRAM_FINISHED) { +  for (i = 0; i < p->num_identifier_references; i++) { +  struct reference *ref = p->identifier_references + i; +  struct identifier *id = ID_FROM_PTR(p, ref); +  +  if ((id->identifier_flags & IDENTIFIER_TYPE_MASK) +  == IDENTIFIER_PIKE_FUNCTION +  && !ref->inherit_offset) { +  PIKE_OPCODE_T *pc = p->program + id->func.offset; +  struct svalue sv; +  +  ulongest_to_svalue_no_free(&sv, (UINT64)PTR_TO_INT(pc)); +  +  mapping_string_insert(m, id->name, &sv); +  free_svalue(&sv); +  } +  } +  +  { +  struct svalue key, value; +  +  SET_SVAL(key, PIKE_T_INT, NUMBER_NUMBER, integer, 0); +  SET_SVAL(value, PIKE_T_INT, NUMBER_NUMBER, integer, +  p->num_program * sizeof(PIKE_OPCODE_T)); +  mapping_insert(m, &key, &value); +  } +  } +  +  RETURN m; + } +  + /*! @decl int(0..) map_all_programs(function(program:void) cb) +  *! +  *! Call cb for all programs that currently exist. +  *! +  *! Programs might be missed if @[cb] creates new programs. +  *! +  *! This function is only intended to be used for debug purposes. +  *! +  *! @returns +  *! The total number of programs +  *! +  *! @seealso +  *! @[map_all_objects()] +  */ + PIKEFUN int(0..) map_all_programs(function(program:void) cb) + { +  struct program *p = first_program; +  INT32 total = 0; +  +  +  while( p ) +  { +  struct program *next; +  +  if (p->flags & PROGRAM_FINISHED) { +  add_ref(p); +  ref_push_program( p ); +  safe_apply_svalue( Pike_sp-2, 1, 1 ); +  pop_stack(); +  total++; +  next = p->next; +  sub_ref(p); +  } else next = p->next; +  p = next; +  } +  RETURN total; + } +  + /*! @endmodule +  */ +  + PIKE_MODULE_INIT + { +  INIT; + #ifdef PIKE_DEBUG +  ADD_INT_CONSTANT("HAVE_DEBUG", 1, 0); + #endif + } +  + PIKE_MODULE_EXIT + { +  EXIT; + }   Newline at end of file added.