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