1 #include <errno.h>
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <sysprof.h>
8 #include <gio/gio.h>
9 
10 typedef struct {
11   const char *mark;
12   const char *detail;
13   gboolean do_start;
14   gint64 start_time;
15   gint64 value;
16 } Data;
17 
18 static bool
callback(const SysprofCaptureFrame * frame,gpointer user_data)19 callback (const SysprofCaptureFrame *frame,
20           gpointer                   user_data)
21 {
22   Data *data = user_data;
23 
24   if (frame->type == SYSPROF_CAPTURE_FRAME_MARK)
25     {
26       SysprofCaptureMark *mark = (SysprofCaptureMark *)frame;
27       if (strcmp (mark->group, "gtk") == 0 &&
28           strcmp (mark->name, data->mark) == 0 &&
29           (data->detail == NULL || strcmp (mark->message, data->detail) == 0))
30         {
31           if (data->do_start)
32             data->value = frame->time - data->start_time;
33           else
34             data->value = mark->duration;
35           return FALSE;
36         }
37     }
38 
39   return TRUE;
40 }
41 
42 #define MILLISECONDS(v) ((v) / (1000.0 * G_TIME_SPAN_MILLISECOND))
43 
44 static int opt_rep = 10;
45 static char *opt_mark;
46 static char *opt_detail;
47 static char *opt_name;
48 static char *opt_output;
49 static gboolean opt_start_time;
50 static GMainLoop *main_loop;
51 static GError *failure;
52 
53 static GOptionEntry options[] = {
54   { "mark", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_mark, "Name of the mark", "NAME" },
55   { "detail", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_detail, "Detail of the mark", "DETAIL" },
56   { "start", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_start_time, "Measure the start time", NULL },
57   { "runs", '0', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &opt_rep, "Number of runs", "COUNT" },
58   { "name", '0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_name, "Name of this test", "NAME" },
59   { "output", '0', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_output, "Directory to save syscap files", "DIRECTORY" },
60   { NULL, }
61 };
62 
63 static gboolean
start_in_main(gpointer data)64 start_in_main (gpointer data)
65 {
66   SysprofProfiler *profiler = data;
67   sysprof_profiler_start (profiler);
68   return G_SOURCE_REMOVE;
69 }
70 
71 static void
on_failure_cb(SysprofProfiler * profiler,const GError * error)72 on_failure_cb (SysprofProfiler *profiler,
73                const GError    *error)
74 {
75   g_propagate_error (&failure, g_error_copy (error));
76   g_main_loop_quit (main_loop);
77 }
78 
79 static void
on_stopped_cb(SysprofProfiler * profiler)80 on_stopped_cb (SysprofProfiler *profiler)
81 {
82   g_clear_error (&failure);
83   g_main_loop_quit (main_loop);
84 }
85 
86 int
main(int argc,char * argv[])87 main (int argc, char *argv[])
88 {
89   GOptionContext *context;
90   GError *error = NULL;
91   Data data;
92   SysprofCaptureFrameType type;
93   gint64 *values;
94   gint64 min, max, total;
95   int count;
96   char *output_dir = NULL;
97   char **spawn_env;
98   char *workdir;
99   int i, j;
100 
101   context = g_option_context_new ("COMMANDLINE");
102   g_option_context_add_main_entries (context, options, NULL);
103   if (!g_option_context_parse (context, &argc, &argv, &error))
104     g_error ("Parsing options: %s", error->message);
105 
106   if (argc < 2)
107     {
108       g_print ("Usage: %s [OPTIONS] COMMANDLINE\n", argv[0]);
109       exit (1);
110     }
111 
112   if (opt_rep < 1)
113     g_error ("COUNT must be a positive number");
114 
115   main_loop = g_main_loop_new (NULL, FALSE);
116   workdir = g_get_current_dir ();
117 
118   spawn_env = g_get_environ ();
119   spawn_env = g_environ_setenv (spawn_env, "GTK_DEBUG_AUTO_QUIT", "1", TRUE);
120 
121   if (opt_output)
122     {
123       GError *err = NULL;
124       GFile *file;
125       const char *subdir;
126 
127       file = g_file_new_for_commandline_arg (opt_output);
128 
129       subdir = g_getenv ("TEST_OUTPUT_SUBDIR");
130       if (subdir)
131         {
132           GFile *child = g_file_get_child (file, subdir);
133           g_object_unref (file);
134           file = child;
135         }
136 
137       if (!g_file_make_directory_with_parents (file, NULL, &err))
138         {
139           if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_EXISTS))
140             g_error ("%s", err->message);
141         }
142 
143       output_dir = g_file_get_path (file);
144       g_object_unref (file);
145     }
146 
147   opt_rep++;
148 
149   values = g_new (gint64, opt_rep);
150 
151   for (i = 0; i < opt_rep; i++)
152     {
153       SysprofProfiler *profiler;
154       int fd;
155       char *name;
156       SysprofCaptureWriter *writer;
157       SysprofCaptureReader *reader;
158       SysprofCaptureCursor *cursor;
159       SysprofCaptureCondition *condition;
160       char **child_argv;
161 
162       fd = g_file_open_tmp ("gtk.XXXXXX.syscap", &name, &error);
163       if (error)
164         g_error ("Create syscap file: %s", error->message);
165 
166       child_argv = g_new (char *, argc);
167       for (j = 0; j + 1 < argc; j++)
168         child_argv[j] = argv[j + 1];
169       child_argv[argc - 1] = NULL;
170 
171       writer = sysprof_capture_writer_new_from_fd (fd, 0);
172       if (!writer)
173         g_error ("Failed to create capture writer: %s", g_strerror (errno));
174 
175       profiler = sysprof_local_profiler_new ();
176       sysprof_profiler_set_whole_system (profiler, FALSE);
177       sysprof_profiler_set_spawn (profiler, TRUE);
178       sysprof_profiler_set_spawn_argv (profiler, (const char * const *)child_argv);
179       sysprof_profiler_set_spawn_cwd (profiler, workdir);
180       sysprof_profiler_set_spawn_env (profiler, (const char * const *)spawn_env);
181       sysprof_profiler_set_writer (profiler, writer);
182 
183       sysprof_capture_writer_unref (writer);
184       g_free (child_argv);
185 
186       g_signal_connect_swapped (profiler, "failed", G_CALLBACK (on_failure_cb), NULL);
187       g_signal_connect_swapped (profiler, "stopped", G_CALLBACK (on_stopped_cb), NULL);
188 
189       g_idle_add (start_in_main, profiler);
190       g_main_loop_run (main_loop);
191 
192       if (failure)
193         g_error ("Run child: %s", failure->message);
194 
195       reader = sysprof_capture_writer_create_reader (writer);
196       if (!reader)
197         g_error ("Opening syscap file: %s", g_strerror (errno));
198 
199       sysprof_capture_writer_unref (writer);
200 
201       data.mark = opt_mark ? opt_mark : "css validation";
202       data.detail = opt_detail ? opt_detail : NULL;
203       data.do_start = opt_start_time;
204       data.start_time = sysprof_capture_reader_get_start_time (reader);
205       data.value = 0;
206 
207       cursor = sysprof_capture_cursor_new (reader);
208 
209       type = SYSPROF_CAPTURE_FRAME_MARK;
210       condition = sysprof_capture_condition_new_where_type_in (1, &type);
211       sysprof_capture_cursor_add_condition (cursor, condition);
212 
213       sysprof_capture_cursor_foreach (cursor, callback, &data);
214 
215       values[i] = data.value;
216 
217       sysprof_capture_cursor_unref (cursor);
218       sysprof_capture_reader_unref (reader);
219 
220       if (output_dir)
221         {
222           GFile *src, *dest;
223           char * save_to;
224 
225           save_to = g_strdup_printf ("%s/%s.%d.syscap", output_dir, opt_name ? opt_name : "gtk", i);
226 
227           src = g_file_new_for_path (name);
228           dest = g_file_new_for_path (save_to);
229           if (!g_file_copy (src, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error))
230             g_error ("%s", error->message);
231 
232           g_free (save_to);
233           g_object_unref (src);
234           g_object_unref (dest);
235         }
236       else
237         remove (name);
238 
239       g_free (name);
240 
241       /* A poor mans way to try and isolate the runs from each other */
242       g_usleep (300000);
243     }
244 
245   g_free (workdir);
246 
247   min = G_MAXINT64;
248   max = 0;
249   count = 0;
250   total = 0;
251 
252   /* Ignore the first run, to avoid cache effects */
253   for (i = 1; i < opt_rep; i++)
254     {
255       if (min > values[i])
256         min = values[i];
257       if (max < values[i])
258         max = values[i];
259       count++;
260       total += values[i];
261     }
262 
263   g_print ("%d runs, min %g, max %g, avg %g\n",
264            count,
265            MILLISECONDS (min),
266            MILLISECONDS (max),
267            MILLISECONDS (total / count));
268 }
269