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 <stddef.h>
7 #include <stdlib.h>
8
9 #include <iostream>
10 #include <iterator>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/command_line.h"
16 #include "base/strings/string_split.h"
17 #include "third_party/re2/src/re2/re2.h"
18 #include "tools/ipc_fuzzer/message_lib/message_file.h"
19 #include "tools/ipc_fuzzer/message_lib/message_names.h"
20
21 namespace {
22
23 const char kDumpSwitch[] = "dump";
24 const char kDumpSwitchHelp[] =
25 "dump human-readable form to stdout instead of copying.";
26
27 const char kEndSwitch[] = "end";
28 const char kEndSwitchHelp[] =
29 "output messages before |m|th message in file (exclusive).";
30
31 const char kHelpSwitch[] = "help";
32 const char kHelpSwitchHelp[] =
33 "display this message.";
34
35 const char kInSwitch[] = "in";
36 const char kInSwitchHelp[] =
37 "output only the messages at the specified positions in the file.";
38
39 const char kInvertSwitch[] = "invert";
40 const char kInvertSwitchHelp[] =
41 "output messages NOT meeting above criteria.";
42
43 const char kRegexpSwitch[] = "regexp";
44 const char kRegexpSwitchHelp[] =
45 "output messages matching regular expression |x|.";
46
47 const char kStartSwitch[] = "start";
48 const char kStartSwitchHelp[] =
49 "output messages after |n|th message in file (inclusive).";
50
usage()51 void usage() {
52 std::cerr << "ipc_message_util: Concatenate all |infile| message files and "
53 << "copy a subset of the result to |outfile|.\n";
54
55 std::cerr << "Usage:\n"
56 << " ipc_message_util"
57 << " [--" << kStartSwitch << "=n]"
58 << " [--" << kEndSwitch << "=m]"
59 << " [--" << kInSwitch << "=i[,j,...]]"
60 << " [--" << kRegexpSwitch << "=x]"
61 << " [--" << kInvertSwitch << "]"
62 << " [--" << kDumpSwitch << "]"
63 << " [--" << kHelpSwitch << "]"
64 << " infile,infile,... [outfile]\n";
65
66 std::cerr << " --" << kStartSwitch << " - " << kStartSwitchHelp << "\n"
67 << " --" << kEndSwitch << " - " << kEndSwitchHelp << "\n"
68 << " --" << kInSwitch << " - " << kInSwitchHelp << "\n"
69 << " --" << kRegexpSwitch << " - " << kRegexpSwitchHelp << "\n"
70 << " --" << kInvertSwitch << " - " << kInvertSwitchHelp << "\n"
71 << " --" << kDumpSwitch << " - " << kDumpSwitchHelp << "\n"
72 << " --" << kHelpSwitch << " - " << kHelpSwitchHelp << "\n";
73 }
74
MessageName(const IPC::Message * msg)75 std::string MessageName(const IPC::Message* msg) {
76 return ipc_fuzzer::MessageNames::GetInstance()->TypeToName(msg->type());
77 }
78
MessageMatches(const IPC::Message * msg,const RE2 & pattern)79 bool MessageMatches(const IPC::Message* msg, const RE2& pattern) {
80 return RE2::FullMatch(MessageName(msg), pattern);
81 }
82
83 } // namespace
84
main(int argc,char ** argv)85 int main(int argc, char** argv) {
86 base::CommandLine::Init(argc, argv);
87 base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
88 base::CommandLine::StringVector args = cmd->GetArgs();
89
90 if (args.size() < 1 || args.size() > 2 || cmd->HasSwitch(kHelpSwitch)) {
91 usage();
92 return EXIT_FAILURE;
93 }
94
95 size_t start_index = 0;
96 if (cmd->HasSwitch(kStartSwitch)) {
97 int temp = atoi(cmd->GetSwitchValueASCII(kStartSwitch).c_str());
98 if (temp > 0)
99 start_index = static_cast<size_t>(temp);
100 }
101
102 size_t end_index = INT_MAX;
103 if (cmd->HasSwitch(kEndSwitch)) {
104 int temp = atoi(cmd->GetSwitchValueASCII(kEndSwitch).c_str());
105 if (temp > 0)
106 end_index = static_cast<size_t>(temp);
107 }
108
109 bool has_regexp = cmd->HasSwitch(kRegexpSwitch);
110 RE2 filter_pattern(cmd->GetSwitchValueASCII(kRegexpSwitch));
111
112 bool invert = cmd->HasSwitch(kInvertSwitch);
113 bool perform_dump = cmd->HasSwitch(kDumpSwitch);
114
115 base::FilePath::StringType output_file_name;
116
117 if (!perform_dump) {
118 if (args.size() < 2) {
119 usage();
120 return EXIT_FAILURE;
121 }
122 output_file_name = args[1];
123 }
124
125 ipc_fuzzer::MessageVector input_message_vector;
126 for (const base::FilePath::StringType& name : base::SplitString(
127 args[0], base::FilePath::StringType(1, ','),
128 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
129 ipc_fuzzer::MessageVector message_vector;
130 if (!ipc_fuzzer::MessageFile::Read(base::FilePath(name), &message_vector))
131 return EXIT_FAILURE;
132 input_message_vector.insert(input_message_vector.end(),
133 std::make_move_iterator(message_vector.begin()),
134 std::make_move_iterator(message_vector.end()));
135 }
136
137 bool has_indices = cmd->HasSwitch(kInSwitch);
138 std::vector<bool> indices;
139
140 if (has_indices) {
141 indices.resize(input_message_vector.size(), false);
142 for (const std::string& cur : base::SplitString(
143 cmd->GetSwitchValueASCII(kInSwitch), ",", base::TRIM_WHITESPACE,
144 base::SPLIT_WANT_ALL)) {
145 int index = atoi(cur.c_str());
146 if (index >= 0 && static_cast<size_t>(index) < indices.size())
147 indices[index] = true;
148 }
149 }
150
151 ipc_fuzzer::MessageVector output_message_vector;
152 std::vector<size_t> remap_vector;
153
154 for (size_t i = 0; i < input_message_vector.size(); ++i) {
155 bool valid = (i >= start_index && i < end_index);
156 if (valid && has_regexp) {
157 valid = MessageMatches(input_message_vector[i].get(), filter_pattern);
158 }
159 if (valid && has_indices) {
160 valid = indices[i];
161 }
162 if (valid != invert) {
163 output_message_vector.push_back(std::move(input_message_vector[i]));
164 remap_vector.push_back(i);
165 }
166 }
167
168 if (perform_dump) {
169 for (size_t i = 0; i < output_message_vector.size(); ++i) {
170 std::cout << remap_vector[i] << ". "
171 << MessageName(output_message_vector[i].get()) << "\n";
172 }
173 } else {
174 if (!ipc_fuzzer::MessageFile::Write(
175 base::FilePath(output_file_name), output_message_vector)) {
176 return EXIT_FAILURE;
177 }
178 }
179
180 return EXIT_SUCCESS;
181 }
182