diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c index 6f45ba8de..3027a8caf 100644 --- a/coregrind/m_gdbserver/server.c +++ b/coregrind/m_gdbserver/server.c @@ -148,6 +148,30 @@ const char *wordn (const char *s, int n) return s; } +void VG_(print_all_stats) (Bool memory_stats, Bool tool_stats) +{ + if (memory_stats) { + VG_(message)(Vg_DebugMsg, "\n"); + VG_(message)(Vg_DebugMsg, + "------ Valgrind's internal memory use stats follow ------\n" ); + VG_(sanity_check_malloc_all)(); + VG_(message)(Vg_DebugMsg, "------\n" ); + VG_(print_all_arena_stats)(); + if (VG_(clo_profile_heap)) + VG_(print_arena_cc_analysis) (); + VG_(message)(Vg_DebugMsg, "\n"); + } + + VG_(print_translation_stats)(); + VG_(print_tt_tc_stats)(); + VG_(print_scheduler_stats)(); + VG_(print_ExeContext_stats)( False /* with_stacktraces */ ); + VG_(print_errormgr_stats)(); + if (tool_stats && VG_(needs).print_stats) { + VG_TDICT_CALL(tool_print_stats); + } +} + /* handle_gdb_valgrind_command handles the provided mon string command. If command is recognised, return 1 else return 0. Note that in case of ambiguous command, 1 is returned. @@ -333,18 +357,14 @@ int handle_gdb_valgrind_command (char *mon, OutputSink *sink_wanted_at_return) ret = 1; break; case 5: /* scheduler */ - VG_(show_sched_status) (); + VG_(show_sched_status) (True, // host_stacktrace + True, // valgrind_stack_usage + True); // exited_threads ret = 1; break; case 6: /* stats */ - VG_(print_translation_stats)(); - VG_(print_tt_tc_stats)(); - VG_(print_scheduler_stats)(); - VG_(print_ExeContext_stats)( False /* with_stacktraces */ ); - VG_(print_errormgr_stats)(); - if (VG_(needs).print_stats) { - VG_TDICT_CALL(tool_print_stats); - } + VG_(print_all_stats)(False, /* Memory stats */ + True /* Tool stats */); ret = 1; break; case 7: /* open_fds */ diff --git a/coregrind/m_libcassert.c b/coregrind/m_libcassert.c index 65a373e71..e88afb018 100644 --- a/coregrind/m_libcassert.c +++ b/coregrind/m_libcassert.c @@ -33,6 +33,7 @@ #include "pub_core_vkiscnums.h" #include "pub_core_libcsetjmp.h" // to keep threadstate.h happy #include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcprint.h" @@ -245,56 +246,89 @@ void VG_(exit)( Int status ) } // Print the scheduler status. -void VG_(show_sched_status) ( void ) +static void show_sched_status_wrk ( Bool host_stacktrace, + Bool valgrind_stack_usage, + Bool exited_threads, + UnwindStartRegs* startRegsIN) { Int i; + if (host_stacktrace) { + const Bool save_clo_xml = VG_(clo_xml); + Addr stacktop; + Addr ips[BACKTRACE_DEPTH]; + Int n_ips; + ThreadState *tst + = VG_(get_ThreadState)( VG_(lwpid_to_vgtid)( VG_(gettid)() ) ); + + // If necessary, fake up an ExeContext which is of our actual real CPU + // state. Could cause problems if we got the panic/exception within the + // execontext/stack dump/symtab code. But it's better than nothing. + UnwindStartRegs startRegs; + VG_(memset)(&startRegs, 0, sizeof(startRegs)); + + if (startRegsIN == NULL) { + GET_STARTREGS(&startRegs); + } else { + startRegs = *startRegsIN; + } + + stacktop = tst->os_state.valgrind_stack_init_SP; + + n_ips = + VG_(get_StackTrace_wrk)( + 0/*tid is unknown*/, + ips, BACKTRACE_DEPTH, + NULL/*array to dump SP values in*/, + NULL/*array to dump FP values in*/, + &startRegs, stacktop + ); + VG_(printf)("\nhost stacktrace:\n"); + VG_(clo_xml) = False; + VG_(pp_StackTrace) (ips, n_ips); + VG_(clo_xml) = save_clo_xml; + } + VG_(printf)("\nsched status:\n"); VG_(printf)(" running_tid=%d\n", VG_(get_running_tid)()); for (i = 1; i < VG_N_THREADS; i++) { - if (VG_(threads)[i].status == VgTs_Empty) continue; - VG_(printf)( "\nThread %d: status = %s\n", i, - VG_(name_of_ThreadStatus)(VG_(threads)[i].status) ); - VG_(get_and_pp_StackTrace)( i, BACKTRACE_DEPTH ); + VgStack* stack + = (VgStack*)VG_(threads)[i].os_state.valgrind_stack_base; + /* If a thread slot was never used (yet), valgrind_stack_base is 0. + If a thread slot is used by a thread or was used by a thread which + has exited, then valgrind_stack_base points to the stack base. */ + if (VG_(threads)[i].status == VgTs_Empty + && (!exited_threads || stack == 0)) continue; + VG_(printf)("\nThread %d: status = %s\n", i, + VG_(name_of_ThreadStatus)(VG_(threads)[i].status) ); + if (VG_(threads)[i].status != VgTs_Empty) + VG_(get_and_pp_StackTrace)( i, BACKTRACE_DEPTH ); + if (valgrind_stack_usage && stack != 0) + VG_(printf)("valgrind stack top usage: %ld of %ld\n", + VG_STACK_ACTIVE_SZB + - VG_(am_get_VgStack_unused_szB)(stack, VG_STACK_ACTIVE_SZB), + (SizeT) VG_STACK_ACTIVE_SZB); } VG_(printf)("\n"); } +void VG_(show_sched_status) ( Bool host_stacktrace, + Bool valgrind_stack_usage, + Bool exited_threads) +{ + show_sched_status_wrk (host_stacktrace, + valgrind_stack_usage, + exited_threads, + NULL); +} + __attribute__ ((noreturn)) static void report_and_quit ( const HChar* report, UnwindStartRegs* startRegsIN ) { - Addr stacktop; - Addr ips[BACKTRACE_DEPTH]; - Int n_ips; - ThreadState *tst - = VG_(get_ThreadState)( VG_(lwpid_to_vgtid)( VG_(gettid)() ) ); - - // If necessary, fake up an ExeContext which is of our actual real CPU - // state. Could cause problems if we got the panic/exception within the - // execontext/stack dump/symtab code. But it's better than nothing. - UnwindStartRegs startRegs; - VG_(memset)(&startRegs, 0, sizeof(startRegs)); - - if (startRegsIN == NULL) { - GET_STARTREGS(&startRegs); - } else { - startRegs = *startRegsIN; - } - - stacktop = tst->os_state.valgrind_stack_init_SP; - - n_ips = - VG_(get_StackTrace_wrk)( - 0/*tid is unknown*/, - ips, BACKTRACE_DEPTH, - NULL/*array to dump SP values in*/, - NULL/*array to dump FP values in*/, - &startRegs, stacktop - ); - VG_(clo_xml) = False; - VG_(pp_StackTrace) (ips, n_ips); - - VG_(show_sched_status)(); + show_sched_status_wrk (True, // host_stacktrace + False, // valgrind_stack_usage + False, // exited_threads + startRegsIN); VG_(printf)( "\n" "Note: see also the FAQ in the source distribution.\n" @@ -402,7 +436,9 @@ void VG_(unimplemented) ( const HChar* msg ) VG_(umsg)("\n"); VG_(umsg)("Valgrind has to exit now. Sorry. Bye!\n"); VG_(umsg)("\n"); - VG_(show_sched_status)(); + VG_(show_sched_status)(False, // host_stacktrace + False, // valgrind_stack_usage + False); // exited_threads VG_(exit)(1); } diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 82a5dde94..98192de8a 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -72,31 +72,6 @@ #endif -/*====================================================================*/ -/*=== Counters, for profiling purposes only ===*/ -/*====================================================================*/ - -static void print_all_stats ( void ) -{ - VG_(print_translation_stats)(); - VG_(print_tt_tc_stats)(); - VG_(print_scheduler_stats)(); - VG_(print_ExeContext_stats)( False /* with_stacktraces */ ); - VG_(print_errormgr_stats)(); - - // Memory stats - if (VG_(clo_verbosity) > 2) { - VG_(message)(Vg_DebugMsg, "\n"); - VG_(message)(Vg_DebugMsg, - "------ Valgrind's internal memory use stats follow ------\n" ); - VG_(sanity_check_malloc_all)(); - VG_(message)(Vg_DebugMsg, "------\n" ); - VG_(print_all_arena_stats)(); - VG_(message)(Vg_DebugMsg, "\n"); - } -} - - /*====================================================================*/ /*=== Command-line: variables, processing, etc ===*/ /*====================================================================*/ @@ -2489,7 +2464,8 @@ void shutdown_actions_NORETURN( ThreadId tid, VG_(sanity_check_general)( True /*include expensive checks*/ ); if (VG_(clo_stats)) - print_all_stats(); + VG_(print_all_stats)(VG_(clo_verbosity) > 2, /* Memory stats */ + False /* tool prints stats in the tool fini */); /* Show a profile of the heap(s) at shutdown. Optionally, first throw away all the debug info, as that makes it easy to spot diff --git a/coregrind/m_mallocfree.c b/coregrind/m_mallocfree.c index 43718c66c..95806b4f8 100644 --- a/coregrind/m_mallocfree.c +++ b/coregrind/m_mallocfree.c @@ -40,6 +40,7 @@ #include "pub_core_options.h" #include "pub_core_libcsetjmp.h" // to keep _threadstate.h happy #include "pub_core_threadstate.h" // For VG_INVALID_THREADID +#include "pub_core_gdbserver.h" #include "pub_core_transtab.h" #include "pub_core_tooliface.h" @@ -742,11 +743,18 @@ void VG_(out_of_memory_NORETURN) ( const HChar* who, SizeT szB ) if (outputTrial <= 1) { if (outputTrial == 0) { outputTrial++; + // First print the memory stats with the aspacemgr data. VG_(am_show_nsegments) (0, "out_of_memory"); VG_(print_all_arena_stats) (); if (VG_(clo_profile_heap)) VG_(print_arena_cc_analysis) (); - /* In case we are an inner valgrind, asks the outer to report + // And then print some other information that might help. + VG_(print_all_stats) (False, /* Memory stats */ + True /* Tool stats */); + VG_(show_sched_status) (True, // host_stacktrace + True, // valgrind_stack_usage + True); // exited_threads + /* In case we are an inner valgrind, asks the outer to report its memory state in its log output. */ INNER_REQUEST(VALGRIND_MONITOR_COMMAND("v.set log_output")); INNER_REQUEST(VALGRIND_MONITOR_COMMAND("v.info memory aspacemgr")); @@ -1289,11 +1297,13 @@ typedef struct { static AnCC anCCs[N_AN_CCS]; +/* Sorting by decreasing cost center nBytes, to have the biggest + cost centres at the top. */ static Int cmp_AnCC_by_vol ( const void* v1, const void* v2 ) { const AnCC* ancc1 = v1; const AnCC* ancc2 = v2; - if (ancc1->nBytes < ancc2->nBytes) return -1; - if (ancc1->nBytes > ancc2->nBytes) return 1; + if (ancc1->nBytes < ancc2->nBytes) return 1; + if (ancc1->nBytes > ancc2->nBytes) return -1; return 0; } diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 52b4d0f25..8359abaaa 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -2101,7 +2101,9 @@ void scheduler_sanity ( ThreadId tid ) lasttime = now; VG_(printf)("\n------------ Sched State at %d ms ------------\n", (Int)now); - VG_(show_sched_status)(); + VG_(show_sched_status)(True, // host_stacktrace + True, // valgrind_stack_usage + True); // exited_threads); } } diff --git a/coregrind/pub_core_libcassert.h b/coregrind/pub_core_libcassert.h index e08983a9e..0ca4e1f5d 100644 --- a/coregrind/pub_core_libcassert.h +++ b/coregrind/pub_core_libcassert.h @@ -71,8 +71,17 @@ extern void VG_(core_panic_at) ( const HChar* str, UnwindStartRegs* ); extern void VG_(unimplemented) ( const HChar* msg ) __attribute__((__noreturn__)); -/* Show the state of all threads. Mostly for debugging V. */ -extern void VG_(show_sched_status) ( void ); +/* Show the state of various threads related information, such + as the guest stacktrace for each thread. + Mostly for debugging V. + The following activates optional output: + host_stacktrace : shows the host stacktrace. + valgrind_stack_usage : shows how much of the valgrind stack was used. + exited_thread_slots : show information for thread slots that were used + but the thread has now exited. */ +extern void VG_(show_sched_status) ( Bool host_stacktrace, + Bool valgrind_stack_usage, + Bool exited_threads); #endif // __PUB_CORE_LIBCASSERT_H diff --git a/docs/xml/manual-core-adv.xml b/docs/xml/manual-core-adv.xml index 382b86a85..1e6e22eb9 100644 --- a/docs/xml/manual-core-adv.xml +++ b/docs/xml/manual-core-adv.xml @@ -1418,18 +1418,24 @@ problems or bugs. - v.info scheduler shows the state and - stack trace for all threads, as known by Valgrind. This allows to - compare the stack traces produced by the Valgrind unwinder with - the stack traces produced by GDB+Valgrind gdbserver. Pay attention - that GDB and Valgrind scheduler status have their own thread - numbering scheme. To make the link between the GDB thread - number and the corresponding Valgrind scheduler thread number, - use the GDB command info - threads. The output of this command shows the - GDB thread number and the valgrind 'tid'. The 'tid' is the thread number - output by v.info scheduler. - When using the callgrind tool, the callgrind monitor command + v.info scheduler shows various + information about threads. First, it outputs the host stack trace, + i.e. the Valgrind code being executed. Then, for each thread, it + outputs the thread state. For non terminated threads, the state is + followed by the guest (client) stack trace. Finally, for each + active thread or for each terminated thread slot not yet re-used, + it shows the max usage of the valgrind stack. + Showing the client stack traces allows to compare the stack + traces produced by the Valgrind unwinder with the stack traces + produced by GDB+Valgrind gdbserver. Pay attention that GDB and + Valgrind scheduler status have their own thread numbering + scheme. To make the link between the GDB thread number and the + corresponding Valgrind scheduler thread number, use the GDB + command info threads. The output + of this command shows the GDB thread number and the valgrind + 'tid'. The 'tid' is the thread number output + by v.info scheduler. When using + the callgrind tool, the callgrind monitor command status outputs internal callgrind information about the stack/call graph it maintains. diff --git a/include/pub_tool_gdbserver.h b/include/pub_tool_gdbserver.h index 0c2545f17..868ca0a7a 100644 --- a/include/pub_tool_gdbserver.h +++ b/include/pub_tool_gdbserver.h @@ -186,6 +186,10 @@ extern void VG_(strtok_get_address_and_size) (Addr* address, SizeT* szB, HChar **ssaveptr); +/* Print various statistics about Valgrind core, + and optionally tool and memory statistics. */ +extern void VG_(print_all_stats) (Bool memory_stats, Bool tool_stats); + #endif // __PUB_TOOL_GDBSERVER_H /*--------------------------------------------------------------------*/