1 /**************************************************************************
3 * Copyright 2010 VMware, Inc.
4 * Copyright 2011 Intel corporation
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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
25 **************************************************************************/
28 #include <limits.h> // for CHAR_MAX
35 #include "os_string.hpp"
37 #include "trace_callset.hpp"
38 #include "trace_parser.hpp"
39 #include "trace_writer.hpp"
41 static const char *synopsis = "Create a new trace by trimming an existing trace.";
47 << "usage: apitrace trim [OPTIONS] TRACE_FILE...\n"
50 " -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"
63 CALLS_OPT = CHAR_MAX + 1,
72 const static struct option
74 {"help", no_argument, 0, 'h'},
75 {"calls", required_argument, 0, CALLS_OPT},
76 {"deps", no_argument, 0, DEPS_OPT},
77 {"no-deps", no_argument, 0, NO_DEPS_OPT},
78 {"thread", required_argument, 0, THREAD_OPT},
79 {"output", required_argument, 0, 'o'},
83 struct stringCompare {
84 bool operator() (const char *a, const char *b) const {
85 return strcmp(a, b) < 0;
90 /* Map for tracking resource dependencies between calls. */
91 std::map<const char *, std::set<unsigned>, stringCompare > resources;
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;
102 /* Compute and record all the resources provided by this call. */
103 void analyze(trace::Call *call) {
104 resources["state"].insert(call->no);
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;
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++) {
118 resources["state"].clear();
120 /* Then insert this call itself. */
121 required.insert(call->no);
124 /* Return a set of all the required calls, (both those calls added
125 * explicitly with require() and those implicitly depended
127 std::set<unsigned> *get_required(void) {
132 struct trim_options {
133 /* Calls to be included in trace. */
134 trace::CallSet calls;
136 /* Whether dependency analysis should be performed. */
137 bool dependency_analysis;
139 /* Output filename */
142 /* Emit only calls from this thread (-1 == all threads) */
147 trim_trace(const char *filename, struct trim_options *options)
149 trace::ParseBookmark beginning;
151 TraceAnalyzer analyzer;
152 std::set<unsigned> *required;
154 if (!p.open(filename)) {
155 std::cerr << "error: failed to open " << filename << "\n";
159 /* Mark the beginning so we can return here for pass 2. */
160 p.getBookmark(beginning);
162 /* In pass 1, analyze which calls are needed. */
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)
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);
175 if (options->dependency_analysis)
176 analyzer.analyze(call);
180 /* Prepare output file and writer for output. */
181 if (options->output.empty()) {
182 os::String base(filename);
183 base.trimExtension();
185 options->output = std::string(base.str()) + std::string("-trim.trace");
188 trace::Writer writer;
189 if (!writer.open(options->output.c_str())) {
190 std::cerr << "error: failed to create " << filename << "\n";
194 /* Reset bookmark for pass 2. */
195 p.setBookmark(beginning);
197 /* In pass 2, emit the calls that are required. */
198 required = analyzer.get_required();
200 while ((call = p.parse_call())) {
201 if (required->find(call->no) != required->end()) {
202 writer.writeCall(call);
207 std::cout << "Trimmed trace is available as " << options->output << "\n";
213 command(int argc, char *argv[])
215 struct trim_options options;
217 options.calls = trace::CallSet(trace::FREQUENCY_ALL);
218 options.dependency_analysis = true;
223 while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
229 options.calls = trace::CallSet(optarg);
232 options.dependency_analysis = true;
235 options.dependency_analysis = false;
238 options.thread = atoi(optarg);
241 options.output = optarg;
244 std::cerr << "error: unexpected option `" << opt << "`\n";
250 if (optind >= argc) {
251 std::cerr << "error: apitrace trim requires a trace file as an argument.\n";
256 return trim_trace(argv[optind], &options);
259 const Command trim_command = {