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