Files
binutils-gdb/gdbsupport/run-time-clock.cc
Simon Marchi 0b79576c9d gdb: add scoped_time_it
New in v2:

 - actually use m_enabled in the constructor and destructor
 - output using gdb_stdlog->write_async_safe instead of gdb_printf

scoped_time_it is a small utility that measures and prints how much time
a given thread spent in a given scope.  Similar to the time(1) command,
it prints the time spent in user mode, system mode, and the wall clock
time.  It also prints the CPU utilization percentage, which is:

  (user + sys) / wall

This can help spot cases where the workload is not well balanced between
workers, or the CPU utilization is not optimal (perhaps due to
contention around a lock for example).

To use it, just add it in some scope.  For instance, a subsequent patch
adds it here:

      workers.add_task ([this, task_count, iter, last] ()
	{
	  scoped_time_it time_it ("DWARF indexing worker");
	  process_cus (task_count, iter, last);
	});

On destruction, if enabled, it prints a line showing the time spent by
that thread, similar to what time(1) prints.

The example above prints this (one line for each worker thread):

    Time for "DWARF indexing worker": wall 0.173, user 0.120, sys 0.034, user+sys 0.154, 89.0 % CPU
    Time for "DWARF indexing worker": wall 0.211, user 0.144, sys 0.047, user+sys 0.191, 90.5 % CPU
    Time for "DWARF indexing worker": wall 0.368, user 0.295, sys 0.057, user+sys 0.352, 95.7 % CPU
    Time for "DWARF indexing worker": wall 0.445, user 0.361, sys 0.072, user+sys 0.433, 97.3 % CPU
    Time for "DWARF indexing worker": wall 0.592, user 0.459, sys 0.113, user+sys 0.572, 96.6 % CPU
    Time for "DWARF indexing worker": wall 0.739, user 0.608, sys 0.115, user+sys 0.723, 97.8 % CPU
    Time for "DWARF indexing worker": wall 0.831, user 0.677, sys 0.140, user+sys 0.817, 98.3 % CPU
    Time for "DWARF indexing worker": wall 0.949, user 0.789, sys 0.144, user+sys 0.933, 98.3 % CPU

The object is only enabled if per_command_time (controlled by "maint set
per-command time") is true at construction time.  I wanted to avoid
adding a new command for now, but eventually if there are too many
scoped_time_it around the code base and we want to be able to enabled
them selectively (e.g. just the ones in the DWARF reader, or in the
symbol searching functions, etc), we could have a dedicated command for
that.

I added this functionality to GDB because it relies on gdb_printf and
per_command_time, but if we ever need it in gdbsupport, I'm sure we
could find a way to put it there.

Change-Id: I5416ac1448f960f44d85f8449943d994198a271e
Approved-By: Tom Tromey <tom@tromey.com>
2025-04-29 15:10:11 -04:00

74 lines
2.1 KiB
C++

/* User/system CPU time clocks that follow the std::chrono interface.
Copyright (C) 2016-2025 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "run-time-clock.h"
#if defined HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
using namespace std::chrono;
run_time_clock::time_point
run_time_clock::now () noexcept
{
return time_point (microseconds (get_run_time ()));
}
#ifdef HAVE_GETRUSAGE
static std::chrono::microseconds
timeval_to_microseconds (struct timeval *tv)
{
return (seconds (tv->tv_sec) + microseconds (tv->tv_usec));
}
#endif
void
get_run_time (user_cpu_time_clock::time_point &user,
system_cpu_time_clock::time_point &system,
run_time_scope scope) noexcept
{
#ifdef HAVE_GETRUSAGE
struct rusage rusage;
int who;
switch (scope)
{
case run_time_scope::thread:
who = RUSAGE_THREAD;
break;
case run_time_scope::process:
who = RUSAGE_SELF;
break;
default:
gdb_assert_not_reached ("invalid run_time_scope value");
}
getrusage (who, &rusage);
microseconds utime = timeval_to_microseconds (&rusage.ru_utime);
microseconds stime = timeval_to_microseconds (&rusage.ru_stime);
user = user_cpu_time_clock::time_point (utime);
system = system_cpu_time_clock::time_point (stime);
#else
user = user_cpu_time_clock::time_point (microseconds (get_run_time ()));
system = system_cpu_time_clock::time_point (microseconds::zero ());
#endif
}