blob: c54565f8e983ec1d5173aa6c221506cd4b6432fb [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
27#include <string.h>
José Fonsecab682b672012-02-15 07:13:31 +000028#include <limits.h> // for CHAR_MAX
29#include <getopt.h>
Carl Worth4c5f6fa2011-11-14 14:50:07 -080030
Carl Worth163b22c2012-08-10 10:15:30 -070031#include <set>
32
Carl Worth4c5f6fa2011-11-14 14:50:07 -080033#include "cli.hpp"
34
35#include "os_string.hpp"
36
José Fonsecad3c00132012-01-27 22:43:53 +000037#include "trace_callset.hpp"
Carl Worth4c5f6fa2011-11-14 14:50:07 -080038#include "trace_parser.hpp"
José Fonseca630471a2012-01-27 22:06:51 +000039#include "trace_writer.hpp"
Carl Worth4c5f6fa2011-11-14 14:50:07 -080040
41static const char *synopsis = "Create a new trace by trimming an existing trace.";
42
43static void
44usage(void)
45{
46 std::cout
José Fonsecab682b672012-02-15 07:13:31 +000047 << "usage: apitrace trim [OPTIONS] TRACE_FILE...\n"
José Fonsecad3c00132012-01-27 22:43:53 +000048 << synopsis << "\n"
49 "\n"
Carl Worth163b22c2012-08-10 10:15:30 -070050 " -h, --help Show this help message and exit\n"
51 " --calls=CALLSET Include specified calls in the trimmed output\n"
52 " --deps Perform dependency analysis and include dependent\n"
53 " calls as needed. This is the default behavior.\n"
54 " --no-deps Do not perform dependency analysis. Include only\n"
55 " those calls explicitly listed in --calls\n"
56 " --thread=THREAD_ID Only retain calls from specified thread\n"
57 " -o, --output=TRACE_FILE Output trace file\n"
José Fonsecad3c00132012-01-27 22:43:53 +000058 "\n"
59 ;
Carl Worth4c5f6fa2011-11-14 14:50:07 -080060}
61
José Fonsecab682b672012-02-15 07:13:31 +000062enum {
Imre Deak6f07a842012-05-08 15:20:43 +030063 CALLS_OPT = CHAR_MAX + 1,
Carl Worth163b22c2012-08-10 10:15:30 -070064 DEPS_OPT,
65 NO_DEPS_OPT,
Imre Deak6f07a842012-05-08 15:20:43 +030066 THREAD_OPT,
José Fonsecab682b672012-02-15 07:13:31 +000067};
68
69const static char *
Kenneth Graunke5245ded2012-04-05 10:51:26 -070070shortOptions = "ho:";
José Fonsecab682b672012-02-15 07:13:31 +000071
72const static struct option
73longOptions[] = {
74 {"help", no_argument, 0, 'h'},
75 {"calls", required_argument, 0, CALLS_OPT},
Carl Worth163b22c2012-08-10 10:15:30 -070076 {"deps", no_argument, 0, DEPS_OPT},
77 {"no-deps", no_argument, 0, NO_DEPS_OPT},
Imre Deak6f07a842012-05-08 15:20:43 +030078 {"thread", required_argument, 0, THREAD_OPT},
79 {"output", required_argument, 0, 'o'},
José Fonsecab682b672012-02-15 07:13:31 +000080 {0, 0, 0, 0}
81};
82
Carl Worth163b22c2012-08-10 10:15:30 -070083struct stringCompare {
84 bool operator() (const char *a, const char *b) const {
85 return strcmp(a, b) < 0;
86 }
87};
88
89class TraceAnalyzer {
90 /* Map for tracking resource dependencies between calls. */
91 std::map<const char *, std::set<unsigned>, stringCompare > resources;
92
93 /* The final set of calls required. This consists of calls added
94 * explicitly with the require() method as well as all calls
95 * implicitly required by those through resource dependencies. */
96 std::set<unsigned> required;
97
98public:
99 TraceAnalyzer() {}
100 ~TraceAnalyzer() {}
101
102 /* Compute and record all the resources provided by this call. */
103 void analyze(trace::Call *call) {
104 resources["state"].insert(call->no);
105 }
106
107 /* Require this call and all of its dependencies to be included in
108 * the final trace. */
109 void require(trace::Call *call) {
110 std::set<unsigned> *dependencies;
111 std::set<unsigned>::iterator i;
112
113 /* First, find and insert all calls that this call depends on. */
114 dependencies = &resources["state"];
115 for (i = dependencies->begin(); i != dependencies->end(); i++) {
116 required.insert(*i);
117 }
118 resources["state"].clear();
119
120 /* Then insert this call itself. */
121 required.insert(call->no);
122 }
123
124 /* Return a set of all the required calls, (both those calls added
125 * explicitly with require() and those implicitly depended
126 * upon. */
127 std::set<unsigned> *get_required(void) {
128 return &required;
129 }
130};
131
132struct trim_options {
133 /* Calls to be included in trace. */
134 trace::CallSet calls;
135
136 /* Whether dependency analysis should be performed. */
137 bool dependency_analysis;
138
139 /* Output filename */
140 std::string output;
141
142 /* Emit only calls from this thread (-1 == all threads) */
143 int thread;
144};
145
146static int
147trim_trace(const char *filename, struct trim_options *options)
148{
149 trace::ParseBookmark beginning;
150 trace::Parser p;
151 TraceAnalyzer analyzer;
152 std::set<unsigned> *required;
153
154 if (!p.open(filename)) {
155 std::cerr << "error: failed to open " << filename << "\n";
156 return 1;
157 }
158
159 /* Mark the beginning so we can return here for pass 2. */
160 p.getBookmark(beginning);
161
162 /* In pass 1, analyze which calls are needed. */
163 trace::Call *call;
164 while ((call = p.parse_call())) {
165 /* If requested, ignore all calls not belonging to the specified thread. */
166 if (options->thread != -1 && call->thread_id != options->thread)
167 continue;
168
169 /* If this call is included in the user-specified call
170 * set, then we don't need to perform any analysis on
171 * it. We know it must be included. */
172 if (options->calls.contains(*call)) {
173 analyzer.require(call);
174 } else {
175 if (options->dependency_analysis)
176 analyzer.analyze(call);
177 }
178 }
179
180 /* Prepare output file and writer for output. */
181 if (options->output.empty()) {
182 os::String base(filename);
183 base.trimExtension();
184
185 options->output = std::string(base.str()) + std::string("-trim.trace");
186 }
187
188 trace::Writer writer;
189 if (!writer.open(options->output.c_str())) {
190 std::cerr << "error: failed to create " << filename << "\n";
191 return 1;
192 }
193
194 /* Reset bookmark for pass 2. */
195 p.setBookmark(beginning);
196
197 /* In pass 2, emit the calls that are required. */
198 required = analyzer.get_required();
199
200 while ((call = p.parse_call())) {
201 if (required->find(call->no) != required->end()) {
202 writer.writeCall(call);
203 }
204 delete call;
205 }
206
207 std::cout << "Trimmed trace is available as " << options->output << "\n";
208
209 return 0;
210}
211
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800212static int
213command(int argc, char *argv[])
214{
Carl Worth163b22c2012-08-10 10:15:30 -0700215 struct trim_options options;
216
217 options.calls = trace::CallSet(trace::FREQUENCY_ALL);
218 options.dependency_analysis = true;
219 options.output = "";
220 options.thread = -1;
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800221
José Fonsecab682b672012-02-15 07:13:31 +0000222 int opt;
223 while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
224 switch (opt) {
225 case 'h':
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800226 usage();
227 return 0;
José Fonsecab682b672012-02-15 07:13:31 +0000228 case CALLS_OPT:
Carl Worth163b22c2012-08-10 10:15:30 -0700229 options.calls = trace::CallSet(optarg);
230 break;
231 case DEPS_OPT:
232 options.dependency_analysis = true;
233 break;
234 case NO_DEPS_OPT:
235 options.dependency_analysis = false;
José Fonsecab682b672012-02-15 07:13:31 +0000236 break;
Imre Deak6f07a842012-05-08 15:20:43 +0300237 case THREAD_OPT:
Carl Worth163b22c2012-08-10 10:15:30 -0700238 options.thread = atoi(optarg);
Imre Deak6f07a842012-05-08 15:20:43 +0300239 break;
José Fonsecab682b672012-02-15 07:13:31 +0000240 case 'o':
Carl Worth163b22c2012-08-10 10:15:30 -0700241 options.output = optarg;
José Fonsecab682b672012-02-15 07:13:31 +0000242 break;
243 default:
244 std::cerr << "error: unexpected option `" << opt << "`\n";
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800245 usage();
246 return 1;
247 }
248 }
249
José Fonsecab682b672012-02-15 07:13:31 +0000250 if (optind >= argc) {
251 std::cerr << "error: apitrace trim requires a trace file as an argument.\n";
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800252 usage();
253 return 1;
254 }
255
Carl Worth163b22c2012-08-10 10:15:30 -0700256 return trim_trace(argv[optind], &options);
Carl Worth4c5f6fa2011-11-14 14:50:07 -0800257}
258
259const Command trim_command = {
260 "trim",
261 synopsis,
262 usage,
263 command
264};