+struct stringCompare {
+ bool operator() (const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+ }
+};
+
+class TraceAnalyzer {
+ /* Map for tracking resource dependencies between calls. */
+ std::map<const char *, std::set<unsigned>, stringCompare > resources;
+
+ /* The final set of calls required. This consists of calls added
+ * explicitly with the require() method as well as all calls
+ * implicitly required by those through resource dependencies. */
+ std::set<unsigned> required;
+
+public:
+ TraceAnalyzer() {}
+ ~TraceAnalyzer() {}
+
+ /* Compute and record all the resources provided by this call. */
+ void analyze(trace::Call *call) {
+ resources["state"].insert(call->no);
+ }
+
+ /* Require this call and all of its dependencies to be included in
+ * the final trace. */
+ void require(trace::Call *call) {
+ std::set<unsigned> *dependencies;
+ std::set<unsigned>::iterator i;
+
+ /* First, find and insert all calls that this call depends on. */
+ dependencies = &resources["state"];
+ for (i = dependencies->begin(); i != dependencies->end(); i++) {
+ required.insert(*i);
+ }
+ resources["state"].clear();
+
+ /* Then insert this call itself. */
+ required.insert(call->no);
+ }
+
+ /* Return a set of all the required calls, (both those calls added
+ * explicitly with require() and those implicitly depended
+ * upon. */
+ std::set<unsigned> *get_required(void) {
+ return &required;
+ }
+};
+
+struct trim_options {
+ /* Calls to be included in trace. */
+ trace::CallSet calls;
+
+ /* Whether dependency analysis should be performed. */
+ bool dependency_analysis;
+
+ /* Output filename */
+ std::string output;
+
+ /* Emit only calls from this thread (-1 == all threads) */
+ int thread;
+};
+
+static int
+trim_trace(const char *filename, struct trim_options *options)
+{
+ trace::ParseBookmark beginning;
+ trace::Parser p;
+ TraceAnalyzer analyzer;
+ std::set<unsigned> *required;
+
+ if (!p.open(filename)) {
+ std::cerr << "error: failed to open " << filename << "\n";
+ return 1;
+ }
+
+ /* Mark the beginning so we can return here for pass 2. */
+ p.getBookmark(beginning);
+
+ /* In pass 1, analyze which calls are needed. */
+ trace::Call *call;
+ while ((call = p.parse_call())) {
+ /* If requested, ignore all calls not belonging to the specified thread. */
+ if (options->thread != -1 && call->thread_id != options->thread)
+ continue;
+
+ /* If this call is included in the user-specified call
+ * set, then we don't need to perform any analysis on
+ * it. We know it must be included. */
+ if (options->calls.contains(*call)) {
+ analyzer.require(call);
+ } else {
+ if (options->dependency_analysis)
+ analyzer.analyze(call);
+ }
+ }
+
+ /* Prepare output file and writer for output. */
+ if (options->output.empty()) {
+ os::String base(filename);
+ base.trimExtension();
+
+ options->output = std::string(base.str()) + std::string("-trim.trace");
+ }
+
+ trace::Writer writer;
+ if (!writer.open(options->output.c_str())) {
+ std::cerr << "error: failed to create " << filename << "\n";
+ return 1;
+ }
+
+ /* Reset bookmark for pass 2. */
+ p.setBookmark(beginning);
+
+ /* In pass 2, emit the calls that are required. */
+ required = analyzer.get_required();
+
+ while ((call = p.parse_call())) {
+ if (required->find(call->no) != required->end()) {
+ writer.writeCall(call);
+ }
+ delete call;
+ }
+
+ std::cout << "Trimmed trace is available as " << options->output << "\n";
+
+ return 0;
+}
+