blob: c5b018856d692c5bf08673bf066d2c7a956c822b [file] [log] [blame]
Carl Worth4c5f6fa2011-11-14 14:50:07 -08001/**************************************************************************
2 *
3 * Copyright 2010 VMware, Inc.
4 * Copyright 2011 Intel corporation
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 **************************************************************************/
26
Carl Worth33b92632012-08-14 14:11:19 -070027#include <sstream>
Carl Worth4c5f6fa2011-11-14 14:50:07 -080028#include <string.h>
José Fonsecab682b672012-02-15 07:13:31 +000029#include <limits.h> // for CHAR_MAX
30#include <getopt.h>
Carl Worth4c5f6fa2011-11-14 14:50:07 -080031
Carl Worth163b22c2012-08-10 10:15:30 -070032#include <set>
33
Carl Worth4c5f6fa2011-11-14 14:50:07 -080034#include "cli.hpp"
35
36#include "os_string.hpp"
37
Carl Worth8efbe012012-08-17 14:15:54 -070038#include "trace_analyzer.hpp"
José Fonsecad3c00132012-01-27 22:43:53 +000039#include "trace_callset.hpp"
Carl Worth4c5f6fa2011-11-14 14:50:07 -080040#include "trace_parser.hpp"
José Fonseca630471a2012-01-27 22:06:51 +000041#include "trace_writer.hpp"
Carl Worth4c5f6fa2011-11-14 14:50:07 -080042
43static const char *synopsis = "Create a new trace by trimming an existing trace.";
44
45static void
46usage(void)
47{
48 std::cout
José Fonsecab682b672012-02-15 07:13:31 +000049 << "usage: apitrace trim [OPTIONS] TRACE_FILE...\n"
José Fonsecad3c00132012-01-27 22:43:53 +000050 << synopsis << "\n"
51 "\n"
Carl Worth86464432012-08-11 11:46:54 -070052 " -h, --help Show detailed help for trim options and exit\n"
53 " --calls=CALLSET Include specified calls in the trimmed output.\n"
Carl Worth46abfd12012-08-14 22:32:29 -070054 " --frames=FRAMESET Include specified frames in the trimmed output.\n"
Carl Worth86464432012-08-11 11:46:54 -070055 " --deps Include additional calls to satisfy dependencies\n"
Carl Worth86464432012-08-11 11:46:54 -070056 " --prune Omit uninteresting calls from the trace output\n"
José Fonseca6e4768b2012-11-22 08:31:47 +000057 " -a, --auto Trim automatically to calls specified in --calls/--frames\n"
58 " Equivalent to both --deps and --prune\n"
Carl Worthab82b3b2012-08-15 16:35:38 -070059 " --print-callset Print the final set of calls included in output\n"
Carl Worth86464432012-08-11 11:46:54 -070060 " --thread=THREAD_ID Only retain calls from specified thread\n"
61 " -o, --output=TRACE_FILE Output trace file\n"
62 ;
63}
64
65static void
66help()
67{
68 std::cout
69 << "usage: apitrace trim [OPTIONS] TRACE_FILE...\n"
70 << synopsis << "\n"
71 "\n"
Carl Worth163b22c2012-08-10 10:15:30 -070072 " -h, --help Show this help message and exit\n"
Carl Worth16b18db2012-08-11 11:33:12 -070073 "\n"
74 " --calls=CALLSET Include specified calls in the trimmed output.\n"
Carl Worth46abfd12012-08-14 22:32:29 -070075 " --frames=FRAMESET Include specified frames in the trimmed output.\n"
Carl Worth16b18db2012-08-11 11:33:12 -070076 "\n"
Carl Worth163b22c2012-08-10 10:15:30 -070077 " --deps Perform dependency analysis and include dependent\n"
Carl Worth16b18db2012-08-11 11:33:12 -070078 " calls as needed, (even if those calls were not\n"
Carl Worth46abfd12012-08-14 22:32:29 -070079 " explicitly requested with --calls or --frames).\n"
Carl Worth16b18db2012-08-11 11:33:12 -070080 "\n"
Carl Worth46abfd12012-08-14 22:32:29 -070081 " --prune Omit calls with no side effects, even if the call\n"
82 " is within the range specified by --calls/--frames.\n"
Carl Worth16b18db2012-08-11 11:33:12 -070083 "\n"
José Fonseca6e4768b2012-11-22 08:31:47 +000084 " -a, --auto Use dependency analysis and pruning\n"
85 " of uninteresting calls the resulting trace may\n"
86 " include more and less calls than specified.\n"
87 " This option is equivalent\n"
88 " to passing both --deps and --prune.\n"
Carl Worth16b18db2012-08-11 11:33:12 -070089 "\n"
Carl Worthab82b3b2012-08-15 16:35:38 -070090 " --print-callset Print to stdout the final set of calls included\n"
91 " in the trim output. This can be useful for\n"
José Fonseca6e4768b2012-11-22 08:31:47 +000092 " tweaking trimmed callset from --auto on the\n"
93 " command-line.\n"
94 " Use --calls=@FILE to read callset from a file.\n"
Carl Worthab82b3b2012-08-15 16:35:38 -070095 "\n"
Carl Worth163b22c2012-08-10 10:15:30 -070096 " --thread=THREAD_ID Only retain calls from specified thread\n"
Carl Worth16b18db2012-08-11 11:33:12 -070097 "\n"
Carl Worth163b22c2012-08-10 10:15:30 -070098 " -o, --output=TRACE_FILE Output trace file\n"
José Fonsecad3c00132012-01-27 22:43:53 +000099 "\n"
100 ;
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800101}
102
José Fonsecab682b672012-02-15 07:13:31 +0000103enum {
Imre Deak6f07a842012-05-08 15:20:43 +0300104 CALLS_OPT = CHAR_MAX + 1,
Carl Worth46abfd12012-08-14 22:32:29 -0700105 FRAMES_OPT,
Carl Worth163b22c2012-08-10 10:15:30 -0700106 DEPS_OPT,
Carl Worth16b18db2012-08-11 11:33:12 -0700107 PRUNE_OPT,
Imre Deak6f07a842012-05-08 15:20:43 +0300108 THREAD_OPT,
Carl Worthab82b3b2012-08-15 16:35:38 -0700109 PRINT_CALLSET_OPT,
José Fonsecab682b672012-02-15 07:13:31 +0000110};
111
112const static char *
Carl Worth54300eb2013-01-28 23:37:17 +1100113shortOptions = "aho:x";
José Fonsecab682b672012-02-15 07:13:31 +0000114
115const static struct option
116longOptions[] = {
117 {"help", no_argument, 0, 'h'},
118 {"calls", required_argument, 0, CALLS_OPT},
Carl Worth46abfd12012-08-14 22:32:29 -0700119 {"frames", required_argument, 0, FRAMES_OPT},
Carl Worth163b22c2012-08-10 10:15:30 -0700120 {"deps", no_argument, 0, DEPS_OPT},
Carl Worth16b18db2012-08-11 11:33:12 -0700121 {"prune", no_argument, 0, PRUNE_OPT},
José Fonseca6e4768b2012-11-22 08:31:47 +0000122 {"auto", no_argument, 0, 'a'},
Imre Deak6f07a842012-05-08 15:20:43 +0300123 {"thread", required_argument, 0, THREAD_OPT},
124 {"output", required_argument, 0, 'o'},
Carl Worthab82b3b2012-08-15 16:35:38 -0700125 {"print-callset", no_argument, 0, PRINT_CALLSET_OPT},
José Fonsecab682b672012-02-15 07:13:31 +0000126 {0, 0, 0, 0}
127};
128
Carl Worth163b22c2012-08-10 10:15:30 -0700129struct stringCompare {
130 bool operator() (const char *a, const char *b) const {
131 return strcmp(a, b) < 0;
132 }
133};
134
Carl Worth163b22c2012-08-10 10:15:30 -0700135struct trim_options {
136 /* Calls to be included in trace. */
137 trace::CallSet calls;
138
Carl Worth46abfd12012-08-14 22:32:29 -0700139 /* Frames to be included in trace. */
140 trace::CallSet frames;
141
Carl Worth163b22c2012-08-10 10:15:30 -0700142 /* Whether dependency analysis should be performed. */
143 bool dependency_analysis;
144
Carl Worth16b18db2012-08-11 11:33:12 -0700145 /* Whether uninteresting calls should be pruned.. */
146 bool prune_uninteresting;
147
Carl Worth163b22c2012-08-10 10:15:30 -0700148 /* Output filename */
149 std::string output;
150
151 /* Emit only calls from this thread (-1 == all threads) */
152 int thread;
Carl Worthab82b3b2012-08-15 16:35:38 -0700153
154 /* Print resulting callset */
155 int print_callset;
Carl Worth163b22c2012-08-10 10:15:30 -0700156};
157
158static int
159trim_trace(const char *filename, struct trim_options *options)
160{
161 trace::ParseBookmark beginning;
162 trace::Parser p;
163 TraceAnalyzer analyzer;
164 std::set<unsigned> *required;
Carl Worth46abfd12012-08-14 22:32:29 -0700165 unsigned frame;
Carl Worthab82b3b2012-08-15 16:35:38 -0700166 int call_range_first, call_range_last;
Carl Worth163b22c2012-08-10 10:15:30 -0700167
168 if (!p.open(filename)) {
169 std::cerr << "error: failed to open " << filename << "\n";
170 return 1;
171 }
172
173 /* Mark the beginning so we can return here for pass 2. */
174 p.getBookmark(beginning);
175
176 /* In pass 1, analyze which calls are needed. */
Carl Worth46abfd12012-08-14 22:32:29 -0700177 frame = 0;
Carl Worth163b22c2012-08-10 10:15:30 -0700178 trace::Call *call;
179 while ((call = p.parse_call())) {
Carl Worth5b827e12012-08-12 20:41:50 -0700180
Carl Worth46abfd12012-08-14 22:32:29 -0700181 /* There's no use doing any work past the last call or frame
182 * requested by the user. */
183 if (call->no > options->calls.getLast() ||
184 frame > options->frames.getLast()) {
185
Carl Worth42249012012-08-14 10:26:11 -0700186 delete call;
Carl Worth5b827e12012-08-12 20:41:50 -0700187 break;
Carl Worth42249012012-08-14 10:26:11 -0700188 }
Carl Worth5b827e12012-08-12 20:41:50 -0700189
Carl Worth163b22c2012-08-10 10:15:30 -0700190 /* If requested, ignore all calls not belonging to the specified thread. */
Carl Worth42249012012-08-14 10:26:11 -0700191 if (options->thread != -1 && call->thread_id != options->thread) {
Carl Worth46abfd12012-08-14 22:32:29 -0700192 goto NEXT;
Carl Worth42249012012-08-14 10:26:11 -0700193 }
Carl Worth163b22c2012-08-10 10:15:30 -0700194
Carl Worth16b18db2012-08-11 11:33:12 -0700195 /* Also, prune if uninteresting (unless the user asked for no pruning. */
José Fonseca28f65142012-11-22 08:12:37 +0000196 if (options->prune_uninteresting && call->flags & trace::CALL_FLAG_VERBOSE) {
Carl Worth46abfd12012-08-14 22:32:29 -0700197 goto NEXT;
Carl Worth16b18db2012-08-11 11:33:12 -0700198 }
199
Carl Worthcc6e51c2012-08-13 14:35:43 -0700200 /* If this call is included in the user-specified call set,
201 * then require it (and all dependencies) in the trimmed
202 * output. */
Carl Worth46abfd12012-08-14 22:32:29 -0700203 if (options->calls.contains(*call) ||
204 options->frames.contains(frame, call->flags)) {
205
Carl Worth163b22c2012-08-10 10:15:30 -0700206 analyzer.require(call);
Carl Worthcc6e51c2012-08-13 14:35:43 -0700207 }
208
209 /* Regardless of whether we include this call or not, we do
210 * some dependency tracking (unless disabled by the user). We
211 * do this even for calls we have included in the output so
212 * that any state updates get performed. */
213 if (options->dependency_analysis) {
214 analyzer.analyze(call);
Carl Worth163b22c2012-08-10 10:15:30 -0700215 }
Carl Worth42249012012-08-14 10:26:11 -0700216
Carl Worth46abfd12012-08-14 22:32:29 -0700217 NEXT:
218 if (call->flags & trace::CALL_FLAG_END_FRAME)
219 frame++;
220
Carl Worth42249012012-08-14 10:26:11 -0700221 delete call;
Carl Worth163b22c2012-08-10 10:15:30 -0700222 }
223
224 /* Prepare output file and writer for output. */
225 if (options->output.empty()) {
226 os::String base(filename);
227 base.trimExtension();
228
229 options->output = std::string(base.str()) + std::string("-trim.trace");
230 }
231
232 trace::Writer writer;
233 if (!writer.open(options->output.c_str())) {
234 std::cerr << "error: failed to create " << filename << "\n";
235 return 1;
236 }
237
238 /* Reset bookmark for pass 2. */
239 p.setBookmark(beginning);
240
241 /* In pass 2, emit the calls that are required. */
242 required = analyzer.get_required();
243
Carl Worth46abfd12012-08-14 22:32:29 -0700244 frame = 0;
Carl Worthab82b3b2012-08-15 16:35:38 -0700245 call_range_first = -1;
246 call_range_last = -1;
Carl Worth163b22c2012-08-10 10:15:30 -0700247 while ((call = p.parse_call())) {
Carl Worth5b827e12012-08-12 20:41:50 -0700248
Carl Worth46abfd12012-08-14 22:32:29 -0700249 /* There's no use doing any work past the last call or frame
250 * requested by the user. */
251 if (call->no > options->calls.getLast() ||
252 frame > options->frames.getLast()) {
253
Carl Worth5b827e12012-08-12 20:41:50 -0700254 break;
Carl Worth46abfd12012-08-14 22:32:29 -0700255 }
Carl Worth5b827e12012-08-12 20:41:50 -0700256
Carl Worth163b22c2012-08-10 10:15:30 -0700257 if (required->find(call->no) != required->end()) {
258 writer.writeCall(call);
Carl Worthab82b3b2012-08-15 16:35:38 -0700259
260 if (options->print_callset) {
261 if (call_range_first < 0) {
262 call_range_first = call->no;
263 printf ("%d", call_range_first);
264 } else if (call->no != call_range_last + 1) {
265 if (call_range_last != call_range_first)
266 printf ("-%d", call_range_last);
267 call_range_first = call->no;
268 printf (",%d", call_range_first);
269 }
270 call_range_last = call->no;
271 }
Carl Worth163b22c2012-08-10 10:15:30 -0700272 }
Carl Worth46abfd12012-08-14 22:32:29 -0700273
274 if (call->flags & trace::CALL_FLAG_END_FRAME) {
275 frame++;
276 }
277
Carl Worth163b22c2012-08-10 10:15:30 -0700278 delete call;
279 }
280
Carl Worthab82b3b2012-08-15 16:35:38 -0700281 if (options->print_callset) {
282 if (call_range_last != call_range_first)
283 printf ("-%d\n", call_range_last);
284 }
285
Carl Worth1a800a12012-08-17 14:23:13 -0700286 std::cerr << "Trimmed trace is available as " << options->output << "\n";
Carl Worth163b22c2012-08-10 10:15:30 -0700287
288 return 0;
289}
290
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800291static int
292command(int argc, char *argv[])
293{
Carl Worth163b22c2012-08-10 10:15:30 -0700294 struct trim_options options;
295
Carl Worth46abfd12012-08-14 22:32:29 -0700296 options.calls = trace::CallSet(trace::FREQUENCY_NONE);
297 options.frames = trace::CallSet(trace::FREQUENCY_NONE);
José Fonseca6e4768b2012-11-22 08:31:47 +0000298 options.dependency_analysis = false;
299 options.prune_uninteresting = false;
Carl Worth163b22c2012-08-10 10:15:30 -0700300 options.output = "";
301 options.thread = -1;
Carl Worthab82b3b2012-08-15 16:35:38 -0700302 options.print_callset = 0;
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800303
José Fonsecab682b672012-02-15 07:13:31 +0000304 int opt;
305 while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
306 switch (opt) {
307 case 'h':
Carl Worth86464432012-08-11 11:46:54 -0700308 help();
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800309 return 0;
José Fonsecab682b672012-02-15 07:13:31 +0000310 case CALLS_OPT:
Carl Worth163b22c2012-08-10 10:15:30 -0700311 options.calls = trace::CallSet(optarg);
312 break;
Carl Worth46abfd12012-08-14 22:32:29 -0700313 case FRAMES_OPT:
314 options.frames = trace::CallSet(optarg);
315 break;
Carl Worth163b22c2012-08-10 10:15:30 -0700316 case DEPS_OPT:
317 options.dependency_analysis = true;
318 break;
Carl Worth16b18db2012-08-11 11:33:12 -0700319 case PRUNE_OPT:
320 options.prune_uninteresting = true;
321 break;
José Fonseca6e4768b2012-11-22 08:31:47 +0000322 case 'a':
323 options.dependency_analysis = true;
324 options.prune_uninteresting = true;
Carl Worth16b18db2012-08-11 11:33:12 -0700325 break;
Imre Deak6f07a842012-05-08 15:20:43 +0300326 case THREAD_OPT:
Carl Worth163b22c2012-08-10 10:15:30 -0700327 options.thread = atoi(optarg);
Imre Deak6f07a842012-05-08 15:20:43 +0300328 break;
José Fonsecab682b672012-02-15 07:13:31 +0000329 case 'o':
Carl Worth163b22c2012-08-10 10:15:30 -0700330 options.output = optarg;
José Fonsecab682b672012-02-15 07:13:31 +0000331 break;
Carl Worthab82b3b2012-08-15 16:35:38 -0700332 case PRINT_CALLSET_OPT:
333 options.print_callset = 1;
334 break;
José Fonsecab682b672012-02-15 07:13:31 +0000335 default:
336 std::cerr << "error: unexpected option `" << opt << "`\n";
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800337 usage();
338 return 1;
339 }
340 }
341
Carl Worth46abfd12012-08-14 22:32:29 -0700342 /* If neither of --calls nor --frames was set, default to the
343 * entire set of calls. */
344 if (options.calls.empty() && options.frames.empty()) {
345 options.calls = trace::CallSet(trace::FREQUENCY_ALL);
346 }
347
José Fonsecab682b672012-02-15 07:13:31 +0000348 if (optind >= argc) {
349 std::cerr << "error: apitrace trim requires a trace file as an argument.\n";
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800350 usage();
351 return 1;
352 }
353
Carl Worthf630d9d2012-09-04 16:48:00 -0700354 if (argc > optind + 1) {
355 std::cerr << "error: extraneous arguments:";
356 for (int i = optind + 1; i < argc; i++) {
357 std::cerr << " " << argv[i];
358 }
359 std::cerr << "\n";
360 usage();
361 return 1;
362 }
363
Carl Worth83ec3f62012-08-17 14:22:30 -0700364 if (options.dependency_analysis) {
365 std::cerr <<
366 "Note: The dependency analysis in \"apitrace trim\" is still experimental.\n"
367 " We hope that it will be useful, but it may lead to incorrect results.\n"
368 " If you find a trace that misbehaves while trimming, please share that\n"
369 " by sending email to apitrace@lists.freedesktop.org, cworth@cworth.org\n";
370 }
371
Carl Worth163b22c2012-08-10 10:15:30 -0700372 return trim_trace(argv[optind], &options);
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800373}
374
375const Command trim_command = {
376 "trim",
377 synopsis,
Carl Worth86464432012-08-11 11:46:54 -0700378 help,
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800379 command
380};