# # %CopyrightBegin% # # Copyright Ericsson AB 2005-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in # compliance with the License. You should have received a copy of the # Erlang Public License along with this software. If not, it can be # retrieved online at http://www.erlang.org/. # # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. # # %CopyrightEnd% # ############################################################################ # Help commands # define etp-help help etp-help end document etp-help %--------------------------------------------------------------------------- % etp-help % % Same as "help etp-help" % % Emulator Toolbox for Pathologists % - GDB command toolbox for analyzing core dumps from the % Erlang emulator (BEAM). % % Should work for 32-bit erts-5.2/R9B, ... % % The commands are prefixed with: % etp: Acronym for erts-term-print % etpf: Acronym for erts-term-print-flat % % User commands (these have help themselves): % % Most useful: % etp, etpf % % Useful for doing step-by-step traversal of lists and tuples after % calling the toplevel command etpf: % etpf-cons, etpf-boxed, % % Special commands for not really terms: % etp-mfa, etp-cp, % etp-msgq, etpf-msgq, % etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump % etp-offheapdump, etpf-offheapdump, % etp-print-procs, etp-search-heaps, etp-search-alloc, % etp-ets-tables, etp-ets-tabledump % % Complex commands that use the Erlang support module. % etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end % % Erlang support module handling commands: % etp-run % % Parameter handling commands: % etp-show, etp-set-max-depth, etp-set-max-string-length % % Other commands you may find in this toolbox are suffixed -1, -2, ... % and are internal; not for the console user. % % The Erlang support module requires `erl' and `erlc' in the path. % The compiled "erl_commands.beam" file is stored in the current % working directory, so it is thereby in the search path of `erl'. % % These are just helpful commands when analyzing core dumps, but % you will not get away without knowing the gory details of the % tag bits. Do not forget about the e.g p, p/x, x and x/4x commands. % % Execution speed of user defined gdb commands is not lightning fast. % It may well take half a minute to dump a complex term with the default % max depth values on our old Sparc Ultra-10's. % % To use the Erlang support module, the environment variable ROOTDIR % must be set to the toplevel installation directory of Erlang/OTP, % so the etp-commands file becomes: % $ROOTDIR/erts/etc/unix/etp-commands % Also, erl and erlc must be in the path. %--------------------------------------------------------------------------- end ############################################################################ # Toplevel commands # define etp # Args: Eterm # # Reentrant # etp-1 ((Eterm)($arg0)) 0 printf ".\n" end document etp %--------------------------------------------------------------------------- % etp Eterm % % Takes a toplevel Erlang term and prints the whole deep term % very much as in Erlang itself. Up to a max depth. See etp-show. %--------------------------------------------------------------------------- end define etp-1 # Args: Eterm, int depth # # Reentrant # if (($arg0) & 0x3) == 1 # Cons pointer if $etp_flat printf "", ($arg0) else etp-list-1 ($arg0) ($arg1) end else if (($arg0) & 0x3) == 2 if $etp_flat printf "", ($arg0) else etp-boxed-1 ($arg0) ($arg1) end else if (($arg0) & 0x3) == 3 etp-immediate-1 ($arg0) else # (($arg0) & 0x3) == 0 if (($arg0) == 0x0) printf "" else if (($arg0) == 0x4) printf "" else etp-cp-1 ($arg0) end end end end end end define etpf # Args: Eterm # # Non-reentrant set $etp_flat = 1 etp-1 ((Eterm)($arg0)) set $etp_flat = 0 printf ".\n" end document etpf %--------------------------------------------------------------------------- % etpf Eterm % % Takes a toplevel Erlang term and prints it is. If it is a deep term % print which command to use to traverse down one level. %--------------------------------------------------------------------------- end ############################################################################ # Commands for nested terms. Some are recursive. # define etp-list-1 # Args: Eterm cons_cell, int depth # # Reentrant # if (($arg0) & 0x3) != 0x1 printf "#NotCons<%#x>", ($arg0) else # Cons pointer if $etp_chart etp-chart-entry-1 ($arg0) ($arg1) 2 end etp-list-printable-1 ($arg0) ($arg1) if !$etp_list_printable # Print normal list printf "[" etp-list-2 ($arg0) (($arg1)+1) end end end define etp-list-printable-1 # Args: Eterm list, int depth # # Non-reentrant # # Returns: $etp_list_printable # if (($arg0) & 0x3) != 0x1 printf "#NotCons<%#x>", ($arg0) else # Loop to check if it is a printable string set $etp_list_p = ($arg0) set $etp_list_printable = ($etp_list_p != $etp_nil) set $etp_list_i = 0 while ($etp_list_p != $etp_nil) && \ ($etp_list_i < $etp_max_string_length) && \ $etp_list_printable if ($etp_list_p & 0x3) == 0x1 # Cons pointer set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] if ($etp_list_n & 0xF) == 0xF etp-ct-printable-1 ($etp_list_n>>4) if $etp_ct_printable # Printable set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] set $etp_list_i++ else set $etp_list_printable = 0 end else set $etp_list_printable = 0 end else set $etp_list_printable = 0 end end # if $etp_list_printable # Print printable string printf "\"" set $etp_list_p = ($arg0) set $etp_list_i = 0 while $etp_list_p != $etp_nil set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] etp-char-1 ($etp_list_n>>4) '"' set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] set $etp_list_i++ if $etp_list_p == $etp_nil printf "\"" else if $etp_list_i >= $etp_max_string_length set $etp_list_p = $etp_nil printf "\"++[...]" else if $etp_chart etp-chart-entry-1 ($arg0) (($arg1)+$etp_list_i) 2 end end end end end end end define etp-list-2 # Args: Eterm cons_cell, int depth # # Reentrant # if (($arg0) & 0x3) != 0x1 printf "#NotCons<%#x>", ($arg0) else # Cons pointer if ($arg1) >= $etp_max_depth printf "...]" else etp-1 (((Eterm*)(($arg0)&~0x3))[0]) (($arg1)+1) if ((Eterm*)(($arg0) & ~0x3))[1] == $etp_nil # Tail is [] printf "]" else if $etp_chart etp-chart-entry-1 ($arg0) ($arg1) 2 end if (((Eterm*)(($arg0)&~0x3))[1]&0x3) == 0x1 # Tail is cons cell printf "," etp-list-2 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) else # Tail is other term printf "|" etp-1 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) printf "]" end end end end end define etpf-cons # Args: Eterm # # Reentrant capable # if ((Eterm)($arg0) & 0x3) != 0x1 printf "#NotCons<%#x>", ($arg0) else # Cons pointer set $etp_flat = 1 printf "[" etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[0]) printf "|" etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[1]) printf "]\n" set $etp_flat = 0 end end document etpf-cons %--------------------------------------------------------------------------- % etpf-cons Eterm % % Takes a Cons ptr and prints the Car and Cdr cells with etpf (flat). %--------------------------------------------------------------------------- end define etp-boxed-1 # Args: Eterm, int depth # # Reentrant # if (($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", ($arg0) else if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 if $etp_chart etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1 end printf "#BoxedError<%#x>", ($arg0) else if $etp_chart etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \ ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) end if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3f) == 0x0 printf "{" etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \ 1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}' else etp-boxed-immediate-1 ($arg0) end end end end define etp-boxed-immediate-1 # Args: Eterm, int depth # # Non-reentrant # if (($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", ($arg0) else if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 printf "#BoxedError<%#x>", ($arg0) else set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3) set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF if $etp_boxed_immediate_h == 0xC etp-extpid-1 ($arg0) else if $etp_boxed_immediate_h == 0xD etp-extport-1 ($arg0) else if ($etp_boxed_immediate_h == 0x2) || \ ($etp_boxed_immediate_h == 0x3) etp-bignum-1 ($arg0) else if ($etp_boxed_immediate_h == 0x6) etp-float-1 ($arg0) else if ($etp_boxed_immediate_h == 0x4) etp-ref-1 ($arg0) else if ($etp_boxed_immediate_h == 0xE) etp-extref-1 ($arg0) else # Hexdump the rest if ($etp_boxed_immediate_h == 0x5) printf "#Fun<" else if ($etp_boxed_immediate_h == 0x8) printf "#RefcBinary<" else if ($etp_boxed_immediate_h == 0x9) printf "#HeapBinary<" else if ($etp_boxed_immediate_h == 0xA) printf "#SubBinary<" else printf "#Header%X<", $etp_boxed_immediate_h end end end end set $etp_boxed_immediate_arity = $etp_boxed_immediate_p[0]>>6 while $etp_boxed_immediate_arity > 0 set $etp_boxed_immediate_p++ if $etp_boxed_immediate_arity > 1 printf "%#x,", *$etp_boxed_immediate_p else printf "%#x", *$etp_boxed_immediate_p if ($etp_boxed_immediate_h == 0xA) set $etp_boxed_immediate_p++ printf ":%#x", *$etp_boxed_immediate_p end printf ">" end set $etp_boxed_immediate_arity-- end # End of hexdump end end end end end end end end end define etpf-boxed # Args: Eterm # # Non-reentrant # set $etp_flat = 1 etp-boxed-1 ((Eterm)($arg0)) 0 set $etp_flat = 0 printf ".\n" end document etpf-boxed %--------------------------------------------------------------------------- % etpf-boxed Eterm % % Take a Boxed ptr and print the contents in one level using etpf (flat). %--------------------------------------------------------------------------- end define etp-array-1 # Args: Eterm* p, int depth, int width, int pos, int size, int end_char # # Reentrant # if ($arg3) < ($arg4) if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth) etp-1 (($arg0)[($arg3)]) (($arg1)+1) if (($arg3) + 1) != ($arg4) printf "," end etp-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) ($arg4) ($arg5) else printf "...%c", ($arg5) end else printf "%c", ($arg5) end end #define etpa-1 ## Args: Eterm, int depth, int index, int arity ## ## Reentrant ## # if ($arg1) >= $etp_max_depth+$etp_max_string_length # printf "%% Max depth for term %d\n", $etp_chart_id # else # if ($arg2) < ($arg3) # etp-1 (((Eterm*)(($arg0)&~0x3))[$arg2]) (($arg1)+1) # etpa-1 ($arg0) (($arg1)+1) (($arg2)+1) ($arg3) # end # end #end ############################################################################ # Commands for non-nested terms. Recursion leaves. Some call other leaves. # define etp-immediate-1 # Args: Eterm # # Reentrant capable # if (($arg0) & 0x3) != 0x3 printf "#NotImmediate<%#x>", ($arg0) else if (($arg0) & 0xF) == 0x3 etp-pid-1 ($arg0) else if (($arg0) & 0xF) == 0x7 etp-port-1 ($arg0) else if (($arg0) & 0xF) == 0xf # Fixnum printf "%ld", (long)((Sint)($arg0)>>4) else # Immediate2 - 0xB if (($arg0) & 0x3f) == 0x0b etp-atom-1 ($arg0) else if (($arg0) & 0x3f) == 0x1b printf "#Catch<%d>", ($arg0)>>6 else if (($arg0) == $etp_nil) printf "[]" else printf "#UnknownImmediate<%#x>", ($arg0) end end end end end end end end define etp-atom-1 # Args: Eterm atom # # Non-reentrant # if ((Eterm)($arg0) & 0x3f) != 0xb printf "#NotAtom<%#x>", ($arg0) else set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] set $etp_atom_1_i = ($etp_atom_1_ap)->len set $etp_atom_1_p = ($etp_atom_1_ap)->name set $etp_atom_1_quote = 1 # Check if atom has to be quoted if ($etp_atom_1_i > 0) etp-ct-atom-1 (*$etp_atom_1_p) if $etp_ct_atom # Atom start character set $etp_atom_1_p++ set $etp_atom_1_i-- set $etp_atom_1_quote = 0 else set $etp_atom_1_i = 0 end end while $etp_atom_1_i > 0 etp-ct-name-1 (*$etp_atom_1_p) if $etp_ct_name # Name character set $etp_atom_1_p++ set $etp_atom_1_i-- else set $etp_atom_1_quote = 1 set $etp_atom_1_i = 0 end end # Print the atom if $etp_atom_1_quote printf "'" end set $etp_atom_1_i = ($etp_atom_1_ap)->len set $etp_atom_1_p = ($etp_atom_1_ap)->name while $etp_atom_1_i > 0 etp-char-1 (*$etp_atom_1_p) '\'' set $etp_atom_1_p++ set $etp_atom_1_i-- end if $etp_atom_1_quote printf "'" end end end define etp-char-1 # Args: int char, int quote_char # # Non-reentrant # if (($arg0) < 0) || (0377 < ($arg0)) printf "#NotChar<%#x>", ($arg0) else if ($arg0) == ($arg1) printf "\\%c", ($arg0) else etp-ct-printable-1 ($arg0) if $etp_ct_printable if $etp_ct_printable < 0 printf "%c", ($arg0) else printf "\\%c", $etp_ct_printable end else printf "\\%03o", ($arg0) end end end end define etp-ct-printable-1 # Args: int # # Determines if integer is a printable character # # Non-reentrant # Returns: $etp_ct_printable # escape alias char, or -1 if no escape alias if ($arg0) == 010 set $etp_ct_printable = 'b' else if ($arg0) == 011 set $etp_ct_printable = 't' else if ($arg0) == 012 set $etp_ct_printable = 'n' else if ($arg0) == 013 set $etp_ct_printable = 'v' else if ($arg0) == 014 set $etp_ct_printable = 'f' else if ($arg0) == 033 set $etp_ct_printable = 'e' else if ((040 <= ($arg0)) && (($arg0) <= 0176)) || \ ((0240 <= ($arg0)) && (($arg0) <= 0377)) # Other printable character set $etp_ct_printable = -1 else set $etp_ct_printable = 0 end end end end end end end end define etp-ct-atom-1 # Args: int # # Determines if integer is a atom first character # # Non-reentrant # Returns: $etp_ct_atom if ((0141 <= ($arg0)) && (($arg0) <= 0172)) || \ ((0337 <= ($arg0)) && (($arg0) != 0367) && (($arg0) <= 0377)) # Atom start character set $etp_ct_atom = 1 else set $etp_ct_atom = 0 end end define etp-ct-variable-1 # Args: int # # Determines if integer is a variable first character # # Non-reentrant # Returns: $etp_ct_variable if ((056 == ($arg0)) || \ (0101 <= ($arg0)) && (($arg0) <= 0132)) || \ (0137 == ($arg0)) || \ ((0300 <= ($arg0)) && (($arg0) != 0327) && (($arg0) <= 0336)) # Variable start character set $etp_ct_variable = 1 else set $etp_ct_variable = 0 end end define etp-ct-name-1 # Args: int # # Determines if integer is a name character, # i.e non-first atom or variable character. # # Non-reentrant # Returns: $etp_ct_variable if (($arg0) == 0100 || \ (060 <= ($arg0)) && (($arg0) <= 071)) set $etp_ct_name = 1 else etp-ct-atom-1 ($arg0) if $etp_ct_atom set $etp_ct_name = 1 else etp-ct-variable-1 ($arg0) set $etp_ct_name = $etp_ct_variable end end end define etp-pid-1 # Args: Eterm pid # # Non-reentrant # set $etp_pid_1 = (Eterm)($arg0) if ($etp_pid_1 & 0xF) == 0x3 # Internal pid printf "<0.%u.%u>", (unsigned) ($etp_pid_1>>4)&0x7fff, \ (unsigned) ($etp_pid_1>>19)&0x1fff else printf "#NotPid<%#x>", ($arg0) end end define etp-extpid-1 # Args: Eterm extpid # # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", (Eterm)($arg0) else set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extpid_1_p->header & 0x3f) != 0x30 printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header else ## External pid set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff set $etp_extpid_1_serial = ($etp_extpid_1_p->data.ui[0]>>15)&0x1fff set $etp_extpid_1_np = $etp_extpid_1_p->node set $etp_extpid_1_creation = $etp_extpid_1_np->creation set $etp_extpid_1_dep = $etp_extpid_1_np->dist_entry set $etp_extpid_1_node = $etp_extpid_1_np->sysname if ($etp_extpid_1_node & 0x3f) != 0xb # Should be an atom printf "#ExternalPidError<%#x>", ($arg0) else if $etp_extpid_1_dep == erts_this_dist_entry printf "<0:" else printf "<%u:", $etp_extpid_1_node>>6 end etp-atom-1 ($etp_extpid_1_node) printf "/%u.%u.%u>", $etp_extpid_1_creation, \ $etp_extpid_1_number, $etp_extpid_1_serial end end end end define etp-port-1 # Args: Eterm port # # Non-reentrant # set $etp_port_1 = (Eterm)($arg0) if ($etp_port_1 & 0xF) == 0x7 # Internal port printf "#Port<0.%u>", (unsigned) ($etp_port_1>>4)&0x3ffff else printf "#NotPort<%#x>", ($arg0) end end define etp-extport-1 # Args: Eterm extport # # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", (Eterm)($arg0) else set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extport_1_p->header & 0x3F) != 0x34 printf "#NotExternalPort<%#x>", $etp_extport_1->header else ## External port set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff set $etp_extport_1_np = $etp_extport_1_p->node set $etp_extport_1_creation = $etp_extport_1_np->creation set $etp_extport_1_dep = $etp_extport_1_np->dist_entry set $etp_extport_1_node = $etp_extport_1_np->sysname if ($etp_extport_1_node & 0x3f) != 0xb # Should be an atom printf "#ExternalPortError<%#x>", ($arg0) else if $etp_extport_1_dep == erts_this_dist_entry printf "#Port<0:" else printf "#Port<%u:", $etp_extport_1_node>>6 end etp-atom-1 ($etp_extport_1_node) printf "/%u.%u>", $etp_extport_1_creation, $etp_extport_1_number end end end end define etp-bignum-1 # Args: Eterm bignum # # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", (Eterm)($arg0) else set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) if ($etp_bignum_1_p[0] & 0x3b) != 0x08 printf "#NotBignum<%#x>", $etp_bignum_1_p[0] else set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6) if $etp_bignum_1_i < 1 printf "#BignumError<%#x>", (Eterm)($arg0) else if $etp_bignum_1_p[0] & 0x04 printf "-" end set $etp_bignum_1_p = (ErtsDigit *)($etp_bignum_1_p + 1) printf "16#" if $etp_arch64 while $etp_bignum_1_i > 0 set $etp_bignum_1_i-- printf "%016lx", $etp_bignum_1_p[$etp_bignum_1_i] end else while $etp_bignum_1_i > 0 set $etp_bignum_1_i-- printf "%08x", $etp_bignum_1_p[$etp_bignum_1_i] end end end end end end define etp-float-1 # Args: Eterm float # # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", (Eterm)($arg0) else set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) if ($etp_float_1_p[0] & 0x3f) != 0x18 printf "#NotFloat<%#x>", $etp_float_1_p[0] else printf "%f", *(double*)($etp_float_1_p+1) end end end define etp-ref-1 # Args: Eterm ref # # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", (Eterm)($arg0) else set $etp_ref_1_p = (RefThing *)((Eterm)($arg0) & ~0x3) if ($etp_ref_1_p->header & 0x3b) != 0x10 printf "#NotRef<%#x>", $etp_ref_1_p->header else set $etp_ref_1_nump = (Uint32 *) 0 set $etp_ref_1_error = 0 if ($etp_ref_1_p->header >> 6) == 0 set $etp_ref_1_error = 1 else if $etp_arch64 set $etp_ref_1_i = (int) $etp_ref_1_p->data.ui32[0] if (($etp_ref_1_i + 1) > (2 * ($etp_ref_1_p->header >> 6))) set $etp_ref_1_error = 1 else set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[1] end else set $etp_ref_1_i = (int) ($etp_ref_1_p->header >> 6) set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[0] end end if $etp_ref_1_error printf "#InternalRefError<%#x>", ($arg0) else printf "#Ref<0" set $etp_ref_1_i-- while $etp_ref_1_i >= 0 printf ".%u", (unsigned) $etp_ref_1_nump[$etp_ref_1_i] set $etp_ref_1_i-- end printf ">" end end end end define etp-extref-1 # Args: Eterm extref # # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 printf "#NotBoxed<%#x>", (Eterm)($arg0) else set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extref_1_p->header & 0x3F) != 0x38 printf "#NotExternalRef<%#x>", $etp_extref_1->header else ## External ref set $etp_extref_1_nump = (Uint32 *) 0 set $etp_extref_1_error = 0 set $etp_extref_1_i = (int) ($etp_extref_1_p->header >> 6) set $etp_extref_1_np = $etp_extref_1_p->node set $etp_extref_1_creation = $etp_extref_1_np->creation set $etp_extref_1_dep = $etp_extref_1_np->dist_entry set $etp_extref_1_node = $etp_extref_1_np->sysname if ($etp_extref_1_node & 0x3f) != 0xb || $etp_extref_1_i < 3 # Node should be an atom set $etp_extref_1_error = 1 else ## $etp_extref_1_i now equals data (Uint) words set $etp_extref_1_i -= 2 if $etp_arch64 if ((((int) $etp_extref_1_p->data.ui32[0]) + 1) \ > (2 * $etp_extref_1_i)) set $etp_extref_1_error = 1 else set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[1] set $etp_extref_1_i = (int) $etp_extref_1_p->data.ui32[0] end else set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[0] end ## $etp_extref_1_i now equals no of ref num (Uint32) words if !$etp_extref_1_error if $etp_extref_1_dep == erts_this_dist_entry printf "#Ref<0:" else printf "#Ref<%u:", $etp_extref_1_node>>6 end etp-atom-1 ($etp_extref_1_node) printf "/%u", $etp_extref_1_creation end end if $etp_extref_1_error printf "#ExternalRefError<%#x>", ($arg0) else set $etp_extref_1_i-- while $etp_extref_1_i >= 0 printf ".%u", (unsigned) $etp_extref_1_nump[$etp_extref_1_i] set $etp_extref_1_i-- end printf ">" end end end end define etp-mfa-1 # Args: Eterm*, int offset # # Reentrant # printf "<" etp-atom-1 (((Eterm*)($arg0))[0]) printf ":" etp-atom-1 (((Eterm*)($arg0))[1]) printf "/%d", ((Eterm*)($arg0))[2] if ($arg1) > 0 printf "+%#x>", ($arg1) else printf ">" end end define etp-mfa # Args: Eterm* # # Reentrant capable # etp-mfa-1 ($arg0) 0 printf ".\n" end document etp-mfa %--------------------------------------------------------------------------- % etp-mfa Eterm* % % Take an Eterm* to an MFA function name entry and print it. % These can be found e.g in the process structure; % process_tab[i]->current and process_tab[i]->initial. %--------------------------------------------------------------------------- end define etp-cp-1 # Args: Eterm cp # # Non-reentrant # set $etp_cp = (Eterm)($arg0) set $etp_cp_low = modules set $etp_cp_high = $etp_cp_low + num_loaded_modules set $etp_cp_mid = mid_module set $etp_cp_p = 0 # while $etp_cp_low < $etp_cp_high if $etp_cp < $etp_cp_mid->start set $etp_cp_high = $etp_cp_mid else if $etp_cp > $etp_cp_mid->end set $etp_cp_low = $etp_cp_mid + 1 else set $etp_cp_p = $etp_cp_low = $etp_cp_high = $etp_cp_mid end end set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 end if $etp_cp_p set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8) set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0] set $etp_cp_p = 0 while $etp_cp_low < $etp_cp_high set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 if $etp_cp < $etp_cp_mid[0] set $etp_cp_high = $etp_cp_mid else if $etp_cp < $etp_cp_mid[1] set $etp_cp_p = $etp_cp_mid[0]+2 set $etp_cp_low = $etp_cp_high = $etp_cp_mid else set $etp_cp_low = $etp_cp_mid + 1 end end end end if $etp_cp_p printf "#Cp" etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2))) else if $etp_cp == beam_apply+1 printf "#Cp" else if *(Eterm*)($etp_cp) == beam_return_trace[0] if ($etp_cp) == beam_exception_trace printf "#Cp" else printf "#Cp" end else if *(Eterm*)($etp_cp) == beam_return_to_trace[0] printf "#Cp" else printf "#Cp<%#x>", $etp_cp end end end end end define etp-cp # Args: Eterm cp # # Reentrant capable # etp-cp-1 ($arg0) printf ".\n" end document etp-cp %--------------------------------------------------------------------------- % etp-cp Eterm % % Take a code continuation pointer and print % module, function, arity and offset. % % Code continuation pointers can be found in the process structure e.g % process_tab[i]->cp and process_tab[i]->i, the second is the % program counter, which is the same thing as a continuation pointer. %--------------------------------------------------------------------------- end ############################################################################ # Commands for special term bunches. # define etp-msgq # Args: ErlMessageQueue* # # Non-reentrant # set $etp_msgq = ($arg0) set $etp_msgq_p = $etp_msgq->first set $etp_msgq_i = $etp_msgq->len set $etp_msgq_prev = $etp_msgq->last printf "%% Message queue (%d):", $etp_msgq_i if ($etp_msgq_i > 0) && $etp_msgq_p printf "\n[" else printf "\n" end while ($etp_msgq_i > 0) && $etp_msgq_p set $etp_msgq_i-- set $etp_msgq_next = $etp_msgq_p->next # Msg etp-1 ($etp_msgq_p->m[0]) 0 if ($etp_msgq_i > 0) && $etp_msgq_next printf ", %% " else printf "]. %% " end # Seq_trace token etp-1 ($etp_msgq_p->m[1]) 0 if $etp_msgq_p == $etp_msgq->save printf ", <=\n" else printf "\n" end if ($etp_msgq_i > 0) && $etp_msgq_next printf " " end # set $etp_msgq_prev = $etp_msgq_p set $etp_msgq_p = $etp_msgq_next end if $etp_msgq_i != 0 printf "#MsgQShort<%d>\n", $etp_msgq_i end if $etp_msgq_p != 0 printf "#MsgQLong<%#lx%p>\n", (unsigned long)$etp_msgq_p end if $etp_msgq_prev != $etp_msgq->last printf "#MsgQEndError<%#lx%p>\n", (unsigned long)$etp_msgq_prev end end document etp-msgq %--------------------------------------------------------------------------- % etp-msgq ErlMessageQueue* % % Take an ErlMessageQueue* and print the contents of the message queue. % Sequential trace tokens are included in comments and % the current match position in the queue is marked '<='. % % A process's message queue is process_tab[i]->msg. %--------------------------------------------------------------------------- end define etpf-msgq # Args: Process* # # Non-reentrant # set $etp_flat = 1 etp-msgq ($arg0) set $etp_flat = 0 end document etpf-msgq %--------------------------------------------------------------------------- % etpf-msgq ErlMessageQueue* % % Same as 'etp-msgq' but print the messages using etpf (flat). %--------------------------------------------------------------------------- end define etp-stacktrace # Args: Process* # # Non-reentrant # set $etp_stacktrace_p = ($arg0)->stop set $etp_stacktrace_end = ($arg0)->hend printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p etp ($arg0)->cp while $etp_stacktrace_p < $etp_stacktrace_end if ($etp_stacktrace_p[0] & 0x3) == 0x0 # Continuation pointer etp $etp_stacktrace_p[0] end set $etp_stacktrace_p++ end end document etp-stacktrace %--------------------------------------------------------------------------- % etp-stacktrace Process* % % Take an Process* and print a stactrace for the process. % The stacktrace consists just of the pushed code continuation % pointers on the stack, the most recently pushed first. %--------------------------------------------------------------------------- end define etp-stackdump # Args: Process* # # Non-reentrant # set $etp_stackdump_p = ($arg0)->stop set $etp_stackdump_end = ($arg0)->hend printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p etp ($arg0)->cp while $etp_stackdump_p < $etp_stackdump_end etp $etp_stackdump_p[0] set $etp_stackdump_p++ end end document etp-stackdump %--------------------------------------------------------------------------- % etp-stackdump Process* % % Take an Process* and print a stackdump for the process. % The stackdump consists of all pushed values on the stack. % All code continuation pointers are preceeded with a line % of dashes to make the stack frames more visible. %--------------------------------------------------------------------------- end define etpf-stackdump # Args: Process* # # Non-reentrant # set $etp_flat = 1 etp-stackdump ($arg0) set $etp_flat = 0 end document etpf-stackdump %--------------------------------------------------------------------------- % etpf-stackdump Process* % % Same as etp-stackdump but print the values using etpf (flat). %--------------------------------------------------------------------------- end define etp-dictdump # Args: ProcDict* # # Non-reentrant # set $etp_dictdump = ($arg0) if $etp_dictdump set $etp_dictdump_n = \ $etp_dictdump->homeSize + $etp_dictdump->splitPosition set $etp_dictdump_i = 0 set $etp_dictdump_written = 0 if $etp_dictdump_n > $etp_dictdump->size set $etp_dictdump_n = $etp_dictdump->size end set $etp_dictdump_cnt = $etp_dictdump->numElements printf "%% Dictionary (%d):\n[", $etp_dictdump_cnt while $etp_dictdump_i < $etp_dictdump_n && \ $etp_dictdump_cnt > 0 set $etp_dictdump_p = $etp_dictdump->data[$etp_dictdump_i] if $etp_dictdump_p != $etp_nil if ((Eterm)$etp_dictdump_p & 0x3) == 0x2 # Boxed if $etp_dictdump_written printf ",\n " else set $etp_dictdump_written = 1 end etp-1 $etp_dictdump_p 0 set $etp_dictdump_cnt-- else while ((Eterm)$etp_dictdump_p & 0x3) == 0x1 && \ $etp_dictdump_cnt > 0 # Cons ptr if $etp_dictdump_written printf ",\n " else set $etp_dictdump_written = 1 end etp-1 (((Eterm*)((Eterm)$etp_dictdump_p&~0x3))[0]) 0 set $etp_dictdump_cnt-- set $etp_dictdump_p = ((Eterm*)((Eterm)$etp_dictdump_p & ~0x3))[1] end if $etp_dictdump_p != $etp_nil printf "#DictSlotError<%d>:", $etp_dictdump_i set $etp_dictdump_flat = $etp_flat set $etp_flat = 1 etp-1 ((Eterm)$etp_dictdump_p) 0 set $etp_flat = $etp_dictdump_flat end end end set $etp_dictdump_i++ end if $etp_dictdump_cnt != 0 printf "#DictCntError<%d>, ", $etp_dictdump_cnt end else printf "%% Dictionary (0):\n[" end printf "].\n" end document etp-dictdump %--------------------------------------------------------------------------- % etp-dictdump ErlProcDict* % % Take an ErlProcDict* and print all entries in the process dictionary. %--------------------------------------------------------------------------- end define etpf-dictdump # Args: ErlProcDict* # # Non-reentrant # set $etp_flat = 1 etp-dictdump ($arg0) set $etp_flat = 0 end document etpf-dictdump %--------------------------------------------------------------------------- % etpf-dictdump ErlProcDict* % % Same as etp-dictdump but print the values using etpf (flat). %--------------------------------------------------------------------------- end define etp-offheapdump # Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) # # Non-reentrant # set $etp_offheapdump_p = ($arg0) set $etp_offheapdump_i = 0 set $etp_offheapdump_ printf "%% Offheap dump:\n[" while ($etp_offheapdump_p != 0) && ($etp_offheapdump_i < $etp_max_depth) if ((Eterm)$etp_offheapdump_p & 0x3) == 0x0 if $etp_offheapdump_i > 0 printf ",\n " end etp-1 ((Eterm)$etp_offheapdump_p|0x2) 0 set $etp_offheapdump_p = $etp_offheapdump_p->next set $etp_offheapdump_i++ else printf "#TaggedPtr<%#x>", $etp_offheapdump_p set $etp_offheapdump_p = 0 end end printf "].\n" end document etp-offheapdump %--------------------------------------------------------------------------- % etp-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) % % Take an pointer to a linked list and print the terms in the list % up to the max depth. %--------------------------------------------------------------------------- end define etpf-offheapdump # Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) # # Non-reentrant # set $etp_flat = 1 etp-offheapdump ($arg0) set $etp_flat = 0 end document etpf-offheapdump %--------------------------------------------------------------------------- % etpf-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) % % Same as etp-offheapdump but print the values using etpf (flat). %--------------------------------------------------------------------------- end define etp-print-procs # Args: Eterm # # Non-reentrant # etp-print-procs-1 end define etp-print-procs-1 # Args: Eterm* # # Non-reentrant # set $etp_print_procs_q = erts_max_processes / 10 set $etp_print_procs_r = erts_max_processes % 10 set $etp_print_procs_t = 10 set $etp_print_procs_m = $etp_print_procs_q if $etp_print_procs_r > 0 set $etp_print_procs_m++ set $etp_print_procs_r-- end set $etp_print_procs_i = 0 set $etp_print_procs_found = 0 while $etp_print_procs_i < erts_max_processes if process_tab[$etp_print_procs_i] printf "%d: ", $etp_print_procs_i etp-1 process_tab[$etp_print_procs_i]->id printf " " etp-1 ((Eterm)(process_tab[$etp_print_procs_i]->i)) printf " heap=%d/%d(%d)", process_tab[$etp_print_procs_i]->htop - process_tab[$etp_print_procs_i]->heap, \ process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->heap, \ process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->stop printf " old=%d/%d ", process_tab[$etp_print_procs_i]->old_htop - process_tab[$etp_print_procs_i]->old_heap, \ process_tab[$etp_print_procs_i]->old_hend - process_tab[$etp_print_procs_i]->old_heap printf " mbuf_sz=%d ", process_tab[$etp_print_procs_i]->mbuf_sz printf " min=%d ", process_tab[$etp_print_procs_i]->min_heap_size printf " flags=%x ", process_tab[$etp_print_procs_i]->flags printf " msgs=%d ", process_tab[$etp_print_procs_i]->msg.len printf "\n" end set $etp_print_procs_i++ if $etp_print_procs_i > $etp_print_procs_m printf "%% %d%%...\n", $etp_print_procs_t set $etp_print_procs_t += 10 set $etp_print_procs_m += $etp_print_procs_q if $etp_print_procs_r > 0 set $etp_print_procs_m++ set $etp_print_procs_r-- end end end printf "%% 100%%.\n" end document etp-print-procs %--------------------------------------------------------------------------- % etp-print-procs Eterm % % Print some information about ALL processes. %--------------------------------------------------------------------------- end define etp-search-heaps # Args: Eterm # # Non-reentrant # printf "%% Search all (<%u) process heaps for ", erts_max_processes set $etp_flat = 1 etp-1 ($arg0) 0 set $etp_flat = 0 printf ":...\n" etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3)) end define etp-search-heaps-1 # Args: Eterm* # # Non-reentrant # set $etp_search_heaps_q = erts_max_processes / 10 set $etp_search_heaps_r = erts_max_processes % 10 set $etp_search_heaps_t = 10 set $etp_search_heaps_m = $etp_search_heaps_q if $etp_search_heaps_r > 0 set $etp_search_heaps_m++ set $etp_search_heaps_r-- end set $etp_search_heaps_i = 0 set $etp_search_heaps_found = 0 while $etp_search_heaps_i < erts_max_processes if process_tab[$etp_search_heaps_i] if (process_tab[$etp_search_heaps_i]->heap <= ($arg0)) && \ (($arg0) < process_tab[$etp_search_heaps_i]->hend) printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \ ($arg0)-process_tab[$etp_search_heaps_i]->heap end if (process_tab[$etp_search_heaps_i]->old_heap <= ($arg0)) && \ (($arg0) <= process_tab[$etp_search_heaps_i]->old_hend) printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \ ($arg0)-process_tab[$etp_search_heaps_i]->old_heap end set $etp_search_heaps_cnt = 0 set $etp_search_heaps_p = process_tab[$etp_search_heaps_i]->mbuf while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth) set $etp_search_heaps_cnt++ if (&($etp_search_heaps_p->mem) <= ($arg0)) && \ (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size) printf "process_tab[%d]->mbuf(%d)+%d\n", \ $etp_search_heaps_i, $etp_search_heaps_cnt, \ ($arg0)-&($etp_search_heaps_p->mem) end set $etp_search_heaps_p = $etp_search_heaps_p->next end if $etp_search_heaps_p printf "process_tab[%d] %% Too many HeapFragments\n", \ $etp_search_heaps_i end end set $etp_search_heaps_i++ if $etp_search_heaps_i > $etp_search_heaps_m printf "%% %d%%...\n", $etp_search_heaps_t set $etp_search_heaps_t += 10 set $etp_search_heaps_m += $etp_search_heaps_q if $etp_search_heaps_r > 0 set $etp_search_heaps_m++ set $etp_search_heaps_r-- end end end printf "%% 100%%.\n" end document etp-search-heaps %--------------------------------------------------------------------------- % etp-search-heaps Eterm % % Search all process heaps in process_tab[], including the heap fragments % (process_tab[]->mbuf) for the specified Eterm. %--------------------------------------------------------------------------- end define etp-search-alloc # Args: Eterm # # Non-reentrant # printf "%% Search allocated memory blocks for " set $etp_flat = 1 etp-1 ($arg0) 0 set $etp_flat = 0 printf ":...\n" set $etp_search_alloc_n = sizeof(erts_allctrs) / sizeof(*erts_allctrs) set $etp_search_alloc_i = 0 while $etp_search_alloc_i < $etp_search_alloc_n if erts_allctrs[$etp_search_alloc_i].alloc set $etp_search_alloc_f = (erts_allctrs+$etp_search_alloc_i) while ($etp_search_alloc_f->alloc == debug_alloc) || \ ($etp_search_alloc_f->alloc == stat_alloc) || \ ($etp_search_alloc_f->alloc == map_stat_alloc) set $etp_search_alloc_f = \ (ErtsAllocatorFunctions_t*)$etp_search_alloc_f->extra end if ($etp_search_alloc_f->alloc != erts_sys_alloc) && \ ($etp_search_alloc_f->alloc != erts_fix_alloc) if ($etp_search_alloc_f->alloc == erts_alcu_alloc) || \ ($etp_search_alloc_f->alloc == erts_alcu_alloc_ts) # alcu alloc set $etp_search_alloc_e = (Allctr_t*)$etp_search_alloc_f->extra # mbc_list set $etp_search_alloc_p = $etp_search_alloc_e->mbc_list.first set $etp_search_alloc_cnt = 0 while $etp_search_alloc_p && \ ($etp_search_alloc_cnt < $etp_max_depth) set $etp_search_alloc_cnt++ if $etp_search_alloc_p <= ($arg0) && \ ($arg0) < (char*)$etp_search_alloc_p + \ ($etp_search_alloc_p->chdr & (Uint)~7) printf "erts_allctrs[%d] %% %salloc: mbc_list: %d\n", \ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ $etp_search_alloc_cnt end if $etp_search_alloc_p == $etp_search_alloc_e->mbc_list.last if $etp_search_alloc_p->next printf \ "erts_allctrs[%d] %% %salloc: mbc_list.last error %p\n",\ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ $etp_search_alloc_p end set $etp_search_alloc_p = 0 else set $etp_search_alloc_p = $etp_search_alloc_p->next end end if $etp_search_alloc_p printf "erts_allctrs[%d] %% %salloc: too large mbc_list %p\n", \ $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, $ept_search_alloc_p end # sbc_list set $etp_search_alloc_p = $etp_search_alloc_e->sbc_list.first set $etp_search_alloc_cnt = 0 while $etp_search_alloc_p && \ ($etp_search_alloc_cnt < $etp_max_depth) set $etp_search_alloc_cnt++ if $etp_search_alloc_p <= ($arg0) && \ ($arg0) < (char*)$etp_search_alloc_p + \ ($etp_search_alloc_p->chdr & (Uint)~7) printf "erts_allctrs[%d] %% %salloc: sbc_list: %d\n", \ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ $etp_search_alloc_cnt end if $etp_search_alloc_p == $etp_search_alloc_e->sbc_list.last if $etp_search_alloc_p->next printf \ "erts_allctrs[%d] %% %salloc: sbc_list.last error %p",\ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ $etp_search_alloc_p end set $etp_search_alloc_p = 0 else set $etp_search_alloc_p = $etp_search_alloc_p->next end end if $etp_search_alloc_p printf "erts_allctrs[%d] %% %salloc: too large sbc_list %p\n", \ $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, $ept_search_alloc_p end else printf "erts_allctrs[%d] %% %s: unknown allocator\n", \ $etp_search_alloc_i, erts_alc_a2ad[$etp_search_alloc_i] end end end set $etp_search_alloc_i++ end end document etp-search-alloc %--------------------------------------------------------------------------- % etp-search-heaps Eterm % % Search all internal allocator memory blocks for for the specified Eterm. %--------------------------------------------------------------------------- end define etp-overlapped-heaps # Args: # # Non-reentrant # printf "%% Dumping heap addresses to \"etp-commands.bin\"\n" set $etp_overlapped_heaps_q = erts_max_processes / 10 set $etp_overlapped_heaps_r = erts_max_processes % 10 set $etp_overlapped_heaps_t = 10 set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q if $etp_overlapped_heaps_r > 0 set $etp_overlapped_heaps_m++ set $etp_overlapped_heaps_r-- end set $etp_overlapped_heaps_i = 0 set $etp_overlapped_heaps_found = 0 dump binary value etp-commands.bin 'o' append binary value etp-commands.bin 'v' append binary value etp-commands.bin 'e' append binary value etp-commands.bin 'r' append binary value etp-commands.bin 'l' append binary value etp-commands.bin 'a' append binary value etp-commands.bin 'p' append binary value etp-commands.bin 'p' append binary value etp-commands.bin 'e' append binary value etp-commands.bin 'd' append binary value etp-commands.bin '-' append binary value etp-commands.bin 'h' append binary value etp-commands.bin 'e' append binary value etp-commands.bin 'a' append binary value etp-commands.bin 'p' append binary value etp-commands.bin 's' append binary value etp-commands.bin '\0' while $etp_overlapped_heaps_i < erts_max_processes if process_tab[$etp_overlapped_heaps_i] append binary value etp-commands.bin \ (Eterm)$etp_overlapped_heaps_i append binary value etp-commands.bin \ (Eterm)process_tab[$etp_overlapped_heaps_i]->heap append binary value etp-commands.bin \ (Eterm)process_tab[$etp_overlapped_heaps_i]->hend append binary value etp-commands.bin \ (Eterm)process_tab[$etp_overlapped_heaps_i]->old_heap append binary value etp-commands.bin \ (Eterm)process_tab[$etp_overlapped_heaps_i]->old_hend set $etp_overlapped_heaps_p = process_tab[$etp_overlapped_heaps_i]->mbuf set $etp_overlapped_heaps_cnt = 0 while $etp_overlapped_heaps_p && \ ($etp_overlapped_heaps_cnt < $etp_max_depth) set $etp_overlapped_heaps_cnt++ append binary value etp-commands.bin \ (Eterm)$etp_overlapped_heaps_p append binary value etp-commands.bin \ (Eterm)(&($etp_overlapped_heaps_p->mem)+$etp_overlapped_heaps_p->size) set $etp_overlapped_heaps_p = $etp_overlapped_heaps_p->next end if $etp_overlapped_heaps_p printf "process_tab[%d] %% Too many HeapFragments\n", \ $etp_overlapped_heaps_i end append binary value etp-commands.bin (Eterm)0x0 append binary value etp-commands.bin (Eterm)0x0 end set $etp_overlapped_heaps_i++ if $etp_overlapped_heaps_i > $etp_overlapped_heaps_m printf "%% %d%%...\n", $etp_overlapped_heaps_t set $etp_overlapped_heaps_t += 10 set $etp_overlapped_heaps_m += $etp_overlapped_heaps_q if $etp_overlapped_heaps_r > 0 set $etp_overlapped_heaps_m++ set $etp_overlapped_heaps_r-- end end end etp-run end document etp-overlapped-heaps %--------------------------------------------------------------------------- % etp-overlapped-heaps % % Dump all process heap addresses in process_tab[], including % the heap fragments in binary format on the file etp-commands.bin. % Then call etp_commands:file/1 to analyze if any heaps overlap. % % Requires 'erl' in the path and 'etp_commands.beam' in 'erl's search path. %--------------------------------------------------------------------------- end define etp-chart # Args: Process* # # Non-reentrant etp-chart-start ($arg0) set ($arg0) = ($arg0) etp-msgq (($arg0)->msg) etp-stackdump ($arg0) etp-dictdump (($arg0)->dictionary) etp-dictdump (($arg0)->debug_dictionary) printf "%% Dumping other process data...\n" etp ($arg0)->seq_trace_token etp ($arg0)->fvalue printf "%% Dumping done.\n" etp-chart-print end document etp-chart %--------------------------------------------------------------------------- % etp-chart Process* % % Dump all process data to the file "etp-commands.bin" and then use % the Erlang support module to print a memory chart of all terms. %--------------------------------------------------------------------------- end define etp-chart-start # Args: Process* # # Non-reentrant set $etp_chart = 1 set $etp_chart_id = 0 set $etp_chart_start_p = ($arg0) dump binary value etp-commands.bin 'c' append binary value etp-commands.bin 'h' append binary value etp-commands.bin 'a' append binary value etp-commands.bin 'r' append binary value etp-commands.bin 't' append binary value etp-commands.bin '\0' append binary value etp-commands.bin (Eterm)($etp_chart_start_p->heap) append binary value etp-commands.bin (Eterm)($etp_chart_start_p->high_water) append binary value etp-commands.bin (Eterm)($etp_chart_start_p->hend) append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_heap) append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_hend) set $etp_chart_start_cnt = 0 set $etp_chart_start_p = $etp_chart_start_p->mbuf while $etp_chart_start_p && ($etp_chart_start_cnt < $etp_max_depth) set $etp_chart_start_cnt++ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->mem) append binary value etp-commands.bin (Eterm)($etp_chart_start_p->size) set $etp_chart_start_p = $etp_chart_start_p->next end append binary value etp-commands.bin (Eterm)(0) append binary value etp-commands.bin (Eterm)(0) if $etp_chart_start_p printf "%% Too many HeapFragments\n" end end document etp-chart-start %--------------------------------------------------------------------------- % etp-chart-start Process* % % Dump a chart head to the file "etp-commands.bin". %--------------------------------------------------------------------------- end define etp-chart-entry-1 # Args: Eterm, int depth, int words # # Reentrant capable if ($arg1) == 0 set $etp_chart_id++ printf "#%d:", $etp_chart_id end append binary value etp-commands.bin ($arg0)&~0x3 append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm)) append binary value etp-commands.bin (Eterm)$etp_chart_id append binary value etp-commands.bin (Eterm)($arg1) # printf "", ($arg0)&~0x3, \ # (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1) end define etp-chart-print set $etp_chart = 0 etp-run end document etp-chart-print %--------------------------------------------------------------------------- % etp-chart-print Process* % % Print a memory chart of the dumped data in "etp-commands.bin", and stop % chart recording. %--------------------------------------------------------------------------- end ############################################################################ # ETS table debug # define etp-ets-tables # Args: # # Non-reentrant printf "%% Dumping < %lu ETS tables\n", (unsigned long)db_max_tabs while $etp_ets_tables_i < db_max_tabs if (meta_main_tab[$etp_ets_tables_i].u.next_free & 3) == 0 printf "%% %d:", $etp_ets_tables_i etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.id)) 0 printf " " etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.owner)) 0 printf "\n" end set $etp_ets_tables_i++ end set $etp_ets_tables_i = 0 end document etp-ets-tables %--------------------------------------------------------------------------- % etp-ets-tables % % Dump all ETS table names and their indexies. %--------------------------------------------------------------------------- end define etp-ets-obj # Args: DbTerm* # set $etp_ets_obj_i = 1 while $etp_ets_obj_i <= (($arg0)->tpl[0] >> 6) if $etp_ets_obj_i == 1 printf "{" else printf ", " end set $etp_ets_elem = ($arg0)->tpl[$etp_ets_obj_i] if ($etp_ets_elem & 3) == 0 printf "" else etp-1 $etp_ets_elem 0 end set $etp_ets_obj_i++ end printf "}" end define etp-ets-tabledump # Args: int tableindex # # Non-reentrant printf "%% Dumping ETS table %d:", ($arg0) set $etp_ets_tabledump_n = 0 set $etp_ets_tabledump_t = meta_main_tab[($arg0)].u.tb set $etp_ets_tabledump_i = 0 etp-1 ($etp_ets_tabledump_t->common.the_name) 0 printf " status=%#x\n", $etp_ets_tabledump_t->common.status if $etp_ets_tabledump_t->common.status & 0x130 # Hash table set $etp_ets_tabledump_h = $etp_ets_tabledump_t->hash printf "%% nitems=%d\n", (long) $etp_ets_tabledump_t->common.nitems while $etp_ets_tabledump_i < (long) $etp_ets_tabledump_h->nactive set $etp_ets_tabledump_seg = ((struct segment**)$etp_ets_tabledump_h->segtab)[$etp_ets_tabledump_i>>8] set $etp_ets_tabledump_l = $etp_ets_tabledump_seg->buckets[$etp_ets_tabledump_i&0xFF] if $etp_ets_tabledump_l printf "%% Slot %d:\n", $etp_ets_tabledump_i while $etp_ets_tabledump_l if $etp_ets_tabledump_n printf "," else printf "[" end set $etp_ets_tabledump_n++ etp-ets-obj &($etp_ets_tabledump_l->dbterm) if $etp_ets_tabledump_l->hvalue == ((unsigned long)-1) printf "% *\n" else printf "\n" end set $etp_ets_tabledump_l = $etp_ets_tabledump_l->next if $etp_ets_tabledump_n >= $etp_max_depth set $etp_ets_tabledump_l = 0 end end end set $etp_ets_tabledump_i++ end if $etp_ets_tabledump_n printf "].\n" end else printf "%% Not a hash table\n" end end document etp-ets-tabledump %--------------------------------------------------------------------------- % etp-ets-tabledump Slot % % Dump an ETS table with a specified slot index. %--------------------------------------------------------------------------- end ############################################################################ # Erlang support module handling # define etp-run shell make -f "${ROOTDIR:?}/erts/etc/unix/etp_commands.mk" \ ROOTDIR="${ROOTDIR:?}" ETP_DATA="etp-commands.bin" end document etp-run %--------------------------------------------------------------------------- % etp-run % % Make and run the Erlang support module on the input file % "erl-commands.bin". The environment variable ROOTDIR must % be set to find $ROOTDIR/erts/etc/unix/etp_commands.mk. % % Also, erl and erlc must be in the path. %--------------------------------------------------------------------------- end ############################################################################ # Toolbox parameter handling # define etp-set-max-depth if ($arg0) > 0 set $etp_max_depth = ($arg0) else echo %%%Error: max-depth <= 0 %%%\n end end document etp-set-max-depth %--------------------------------------------------------------------------- % etp-set-max-depth Depth % % Set the max term depth to use for etp. The term dept limit % works in both depth and width, so if you set the max depth to 10, % an 11 element flat tuple will be truncated. %--------------------------------------------------------------------------- end define etp-set-max-string-length if ($arg0) > 0 set $etp_max_string_length = ($arg0) else echo %%%Error: max-string-length <= 0 %%%\n end end document etp-set-max-string-length %--------------------------------------------------------------------------- % etp-set-max-strint-length Length % % Set the max string length to use for ept when printing lists % that can be shown as printable strings. Printable strings % that are longer will be truncated, and not even checked if % they really are printable all the way to the end. %--------------------------------------------------------------------------- end define etp-show printf "etp-set-max-depth %d\n", $etp_max_depth printf "etp-set-max-string-length %d\n", $etp_max_string_length end document etp-show %--------------------------------------------------------------------------- % etp-show % % Show the commands needed to set all etp parameters % to their current value. %--------------------------------------------------------------------------- end ############################################################################ # Init # define etp-init set $etp_arch64 = (sizeof(void *) == 8) if $etp_arch64 set $etp_nil = 0xfffffffffffffffb else set $etp_nil = 0xfffffffb end set $etp_flat = 0 set $etp_chart_id = 0 set $etp_chart = 0 set $etp_max_depth = 20 set $etp_max_string_length = 100 set $etp_ets_tables_i = 0 end document etp-init %--------------------------------------------------------------------------- % Use etp-help for a command overview and general help. % % To use the Erlang support module, the environment variable ROOTDIR % must be set to the toplevel installation directory of Erlang/OTP, % so the etp-commands file becomes: % $ROOTDIR/erts/etc/unix/etp-commands % Also, erl and erlc must be in the path. %--------------------------------------------------------------------------- end etp-init help etp-init etp-show