2 * Copyright (C) 2013 Stefan Sauer <ensonic@users.sf.net>
4 * gstrusage.c: tracing module that logs resource usage stats
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * @short_description: log resource usage stats
25 * A tracing module that take rusage() snapshots and logs them.
32 #include "gstrusage.h"
34 #ifdef HAVE_SYS_RESOURCE_H
35 #include <sys/resource.h>
38 GST_DEBUG_CATEGORY_STATIC (gst_rusage_debug);
39 #define GST_CAT_DEFAULT gst_rusage_debug
42 GST_DEBUG_CATEGORY_INIT (gst_rusage_debug, "rusage", 0, "rusage tracer");
43 #define gst_rusage_tracer_parent_class parent_class
44 G_DEFINE_TYPE_WITH_CODE (GstRUsageTracer, gst_rusage_tracer, GST_TYPE_TRACER,
47 /* for ts calibration */
48 static gpointer main_thread_id = NULL;
49 static guint64 tproc_base = G_GINT64_CONSTANT (0);
53 /* time spend in this thread */
57 static void gst_rusage_tracer_invoke (GstTracer * self, GstTracerHookId id,
58 GstTracerMessageId mid, va_list var_args);
63 free_thread_stats (gpointer data)
65 g_slice_free (GstThreadStats, data);
69 gst_rusage_tracer_class_init (GstRUsageTracerClass * klass)
71 GstTracerClass *gst_tracer_class = GST_TRACER_CLASS (klass);
73 gst_tracer_class->invoke = gst_rusage_tracer_invoke;
77 gst_rusage_tracer_init (GstRUsageTracer * self)
79 g_object_set (self, "mask", GST_TRACER_HOOK_ALL, NULL);
80 self->threads = g_hash_table_new_full (NULL, NULL, NULL, free_thread_stats);
82 main_thread_id = g_thread_self ();
83 GST_DEBUG ("rusage: main thread=%p", main_thread_id);
87 gst_rusage_tracer_invoke (GstTracer * obj, GstTracerHookId hid,
88 GstTracerMessageId mid, va_list var_args)
90 GstRUsageTracer *self = GST_RUSAGE_TRACER_CAST (obj);
91 guint64 ts = va_arg (var_args, guint64);
92 GstThreadStats *stats;
93 gpointer thread_id = g_thread_self ();
96 GstClockTime tproc = G_GUINT64_CONSTANT (0);
98 // FIXME(ensonic): not threadsafe
99 static GstClockTime last_ts = G_GUINT64_CONSTANT (0);
101 getrusage (RUSAGE_SELF, &ru);
103 /* get stats record for current thread */
104 if (!(stats = g_hash_table_lookup (self->threads, thread_id))) {
105 stats = g_slice_new0 (GstThreadStats);
106 g_hash_table_insert (self->threads, thread_id, stats);
108 #ifdef HAVE_CLOCK_GETTIME
112 if (!clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &now)) {
113 tproc = GST_TIMESPEC_TO_TIME (now);
115 GST_WARNING ("clock_gettime (CLOCK_PROCESS_CPUTIME_ID,...) failed.");
117 GST_TIMEVAL_TO_TIME (ru.ru_utime) + GST_TIMEVAL_TO_TIME (ru.ru_stime);
120 /* cpu time per thread */
121 if (!clock_gettime (CLOCK_THREAD_CPUTIME_ID, &now)) {
122 stats->tthread = GST_TIMESPEC_TO_TIME (now);
124 GST_WARNING ("clock_gettime (CLOCK_THREAD_CPUTIME_ID,...) failed.");
125 stats->tthread += GST_CLOCK_DIFF (last_ts, ts);
129 tproc = GST_TIMEVAL_TO_TIME (ru.ru_utime) + GST_TIMEVAL_TO_TIME (ru.ru_stime);
130 /* crude way to meassure time spend in which thread */
131 stats->tthread += GST_CLOCK_DIFF (last_ts, ts);
134 /* remember last timestamp for fallback calculations */
137 /* Calibrate ts for the process and main thread. For tthread[main] and tproc
138 * the time is larger than ts, as our base-ts is taken after the process has
141 if (G_UNLIKELY (thread_id == main_thread_id)) {
142 main_thread_id = NULL;
143 /* when the registry gets updated, the tproc is less than the debug time ? */
144 /* TODO(ensonic): we still see cases where tproc overtakes ts, especially
145 * when with sync=false, can this be due to multiple cores in use? */
147 tproc_base = tproc - ts;
148 GST_DEBUG ("rusage: calibrating by %" G_GUINT64_FORMAT ", thread: %"
149 G_GUINT64_FORMAT ", proc: %" G_GUINT64_FORMAT,
150 tproc_base, stats->tthread, tproc);
151 stats->tthread -= tproc_base;
154 /* we always need to corect proc time */
157 /* FIXME: how can we take cpu-frequency scaling into account?
158 * - looking at /sys/devices/system/cpu/cpu0/cpufreq/
159 * scale_factor=scaling_max_freq/scaling_cur_freq
160 * - as a workaround we can switch it via /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
161 * cpufreq-selector -g performance
162 * cpufreq-selector -g ondemand
164 cpuload = (guint) gst_util_uint64_scale (tproc, G_GINT64_CONSTANT (100), ts);
165 gst_tracer_log_trace (gst_structure_new ("rusage", "ts", G_TYPE_UINT64, ts, "thread-id", G_TYPE_UINT, GPOINTER_TO_UINT (thread_id), "cpuload", G_TYPE_UINT, cpuload, /* cpu usage per thread - FIXME: not used in gst-stats */
166 "thread-time", G_TYPE_UINT64, stats->tthread, /* time spent in thread */
167 "proc-time", G_TYPE_UINT64, tproc, /* time spent in process */