1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <limits.h>
6#include <stdlib.h>
7#include <iostream>
8#include <string>
9#include <vector>
10
11#include "base/command_line.h"
12#include "base/strings/string_split.h"
13#include "third_party/re2/re2/re2.h"
14#include "tools/ipc_fuzzer/message_lib/message_file.h"
15#include "tools/ipc_fuzzer/message_lib/message_names.h"
16
17namespace {
18
19const char kDumpSwitch[] = "dump";
20const char kDumpSwitchHelp[] =
21    "dump human-readable form to stdout instead of copying.";
22
23const char kEndSwitch[] = "end";
24const char kEndSwitchHelp[] =
25    "output messages before |m|th message in file (exclusive).";
26
27const char kHelpSwitch[] = "help";
28const char kHelpSwitchHelp[] =
29    "display this message.";
30
31const char kInvertSwitch[] = "invert";
32const char kInvertSwitchHelp[] =
33    "output messages NOT meeting above criteria.";
34
35const char kRegexpSwitch[] = "regexp";
36const char kRegexpSwitchHelp[] =
37    "output messages matching regular expression |x|.";
38
39const char kStartSwitch[] = "start";
40const char kStartSwitchHelp[] =
41    "output messages after |n|th message in file (inclusive).";
42
43void usage() {
44  std::cerr << "ipc_message_util: Concatenate all |infile| message files and "
45            << "copy a subset of the result to |outfile|.\n";
46
47  std::cerr << "Usage:\n"
48            << "  ipc_message_util"
49            << " [--" << kStartSwitch << "=n]"
50            << " [--" << kEndSwitch << "=m]"
51            << " [--" << kRegexpSwitch << "=x]"
52            << " [--" << kInvertSwitch << "]"
53            << " [--" << kDumpSwitch << "]"
54            << " [--" << kHelpSwitch << "]"
55            << " infile,infile,... [outfile]\n";
56
57  std::cerr << "    --" << kStartSwitch << "  - " << kStartSwitchHelp << "\n"
58            << "    --" << kEndSwitch << "    - " << kEndSwitchHelp << "\n"
59            << "    --" << kRegexpSwitch << " - " << kRegexpSwitchHelp << "\n"
60            << "    --" << kInvertSwitch << " - " << kInvertSwitchHelp << "\n"
61            << "    --" << kDumpSwitch << "   - " << kDumpSwitchHelp << "\n"
62            << "    --" << kHelpSwitch << "   - " << kHelpSwitchHelp << "\n";
63}
64
65std::string MessageName(const IPC::Message* msg) {
66  return ipc_fuzzer::MessageNames::GetInstance()->TypeToName(msg->type());
67}
68
69bool MessageMatches(const IPC::Message* msg, const RE2& pattern) {
70  return RE2::FullMatch(MessageName(msg), pattern);
71}
72
73}  // namespace
74
75int main(int argc, char** argv) {
76  CommandLine::Init(argc, argv);
77  CommandLine* cmd = CommandLine::ForCurrentProcess();
78  CommandLine::StringVector args = cmd->GetArgs();
79
80  if (args.size() < 1 || args.size() > 2 || cmd->HasSwitch(kHelpSwitch)) {
81    usage();
82    return EXIT_FAILURE;
83  }
84
85  size_t start_index = 0;
86  if (cmd->HasSwitch(kStartSwitch)) {
87    int temp = atoi(cmd->GetSwitchValueASCII(kStartSwitch).c_str());
88    if (temp > 0 )
89      start_index = static_cast<size_t>(temp);
90  }
91
92  size_t end_index = INT_MAX;
93  if (cmd->HasSwitch(kEndSwitch)) {
94    int temp = atoi(cmd->GetSwitchValueASCII(kEndSwitch).c_str());
95    if (temp > 0)
96      end_index = static_cast<size_t>(temp);
97  }
98
99  bool has_regexp = cmd->HasSwitch(kRegexpSwitch);
100  RE2 filter_pattern(cmd->GetSwitchValueASCII(kRegexpSwitch));
101
102  bool invert = cmd->HasSwitch(kInvertSwitch);
103  bool perform_dump = cmd->HasSwitch(kDumpSwitch);
104
105  std::vector<std::string> input_file_names;
106  std::string output_file_name;
107  base::SplitString(args[0], ',', &input_file_names);
108
109  if (!perform_dump) {
110    if (args.size() < 2) {
111      usage();
112      return EXIT_FAILURE;
113    }
114    output_file_name = args[1];
115  }
116
117  ipc_fuzzer::MessageVector input_message_vector;
118  for (std::vector<std::string>::iterator it = input_file_names.begin();
119      it != input_file_names.end(); ++it) {
120    ipc_fuzzer::MessageVector message_vector;
121    if (!ipc_fuzzer::MessageFile::Read(base::FilePath(*it), &message_vector))
122      return EXIT_FAILURE;
123    input_message_vector.insert(input_message_vector.end(),
124                                message_vector.begin(), message_vector.end());
125    message_vector.weak_clear();
126  }
127
128  ipc_fuzzer::MessageVector output_message_vector;
129  std::vector<size_t> remap_vector;
130
131  for (size_t i = 0; i < input_message_vector.size(); ++i) {
132    bool valid = (i >= start_index && i < end_index);
133    if (valid && has_regexp) {
134      valid = MessageMatches(input_message_vector[i], filter_pattern);
135    }
136    if (valid != invert) {
137      output_message_vector.push_back(input_message_vector[i]);
138      remap_vector.push_back(i);
139      input_message_vector[i] = NULL;
140    }
141  }
142
143  if (perform_dump) {
144    for (size_t i = 0; i < output_message_vector.size(); ++i) {
145      std::cout << remap_vector[i] << ". "
146                << MessageName(output_message_vector[i]) << "\n";
147    }
148  } else {
149    if (!ipc_fuzzer::MessageFile::Write(
150            base::FilePath(output_file_name), output_message_vector)) {
151      return EXIT_FAILURE;
152    }
153  }
154
155  return EXIT_SUCCESS;
156}
157