Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: tools/battor_agent/battor_agent_bin.cc

Issue 1949673003: [battor agent] Makes the main() thread also be the UI thread (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // This file provides a thin binary wrapper around the BattOr Agent 5 // This file provides a thin binary wrapper around the BattOr Agent
6 // library. This binary wrapper provides a means for non-C++ tracing 6 // library. This binary wrapper provides a means for non-C++ tracing
7 // controllers, such as Telemetry and Android Systrace, to issue high-level 7 // controllers, such as Telemetry and Android Systrace, to issue high-level
8 // tracing commands to the BattOr through an interactive shell. 8 // tracing commands to the BattOr through an interactive shell.
9 // 9 //
10 // Example usage of how an external trace controller might use this binary: 10 // Example usage of how an external trace controller might use this binary:
(...skipping 24 matching lines...) Expand all
35 35
36 #include <fstream> 36 #include <fstream>
37 #include <iostream> 37 #include <iostream>
38 38
39 #include "base/at_exit.h" 39 #include "base/at_exit.h"
40 #include "base/bind.h" 40 #include "base/bind.h"
41 #include "base/bind_helpers.h" 41 #include "base/bind_helpers.h"
42 #include "base/command_line.h" 42 #include "base/command_line.h"
43 #include "base/location.h" 43 #include "base/location.h"
44 #include "base/logging.h" 44 #include "base/logging.h"
45 #include "base/run_loop.h"
45 #include "base/strings/string_tokenizer.h" 46 #include "base/strings/string_tokenizer.h"
46 #include "base/strings/utf_string_conversions.h" 47 #include "base/strings/utf_string_conversions.h"
48 #include "base/thread_task_runner_handle.h"
47 #include "base/threading/thread.h" 49 #include "base/threading/thread.h"
48 #include "tools/battor_agent/battor_agent.h" 50 #include "tools/battor_agent/battor_agent.h"
49 #include "tools/battor_agent/battor_error.h" 51 #include "tools/battor_agent/battor_error.h"
50 #include "tools/battor_agent/battor_finder.h" 52 #include "tools/battor_agent/battor_finder.h"
51 53
52 using std::endl; 54 using std::endl;
53 55
54 namespace battor { 56 namespace battor {
55 57
56 namespace { 58 namespace {
57 59
58 const char kIoThreadName[] = "BattOr IO Thread"; 60 const char kIoThreadName[] = "BattOr IO Thread";
59 const char kFileThreadName[] = "BattOr File Thread"; 61 const char kFileThreadName[] = "BattOr File Thread";
60 const char kUiThreadName[] = "BattOr UI Thread";
61 62
62 const char kUsage[] = 63 const char kUsage[] =
63 "Start the battor_agent shell with:\n" 64 "Start the battor_agent shell with:\n"
64 "\n" 65 "\n"
65 " battor_agent <switches>\n" 66 " battor_agent <switches>\n"
66 "\n" 67 "\n"
67 "Switches: \n" 68 "Switches: \n"
68 " --battor-path=<path> Uses the specified BattOr path.\n" 69 " --battor-path=<path> Uses the specified BattOr path.\n"
69 "\n" 70 "\n"
70 "Once in the shell, you can issue the following commands:\n" 71 "Once in the shell, you can issue the following commands:\n"
(...skipping 29 matching lines...) Expand all
100 tokens.push_back(tokenizer.token()); 101 tokens.push_back(tokenizer.token());
101 return tokens; 102 return tokens;
102 } 103 }
103 104
104 } // namespace 105 } // namespace
105 106
106 // Wrapper class containing all state necessary for an independent binary to 107 // Wrapper class containing all state necessary for an independent binary to
107 // use a BattOrAgent to communicate with a BattOr. 108 // use a BattOrAgent to communicate with a BattOr.
108 class BattOrAgentBin : public BattOrAgent::Listener { 109 class BattOrAgentBin : public BattOrAgent::Listener {
109 public: 110 public:
110 BattOrAgentBin() 111 BattOrAgentBin() : io_thread_(kIoThreadName), file_thread_(kFileThreadName) {}
111 : done_(false, false),
112 io_thread_(kIoThreadName),
113 file_thread_(kFileThreadName),
114 ui_thread_(kUiThreadName) {}
115 112
116 ~BattOrAgentBin() { DCHECK(!agent_); } 113 ~BattOrAgentBin() { DCHECK(!agent_); }
117 114
118 // Starts the interactive BattOr agent shell and eventually returns an exit 115 // Starts the interactive BattOr agent shell and eventually returns an exit
119 // code. 116 // code.
120 int Run(int argc, char* argv[]) { 117 int Run(int argc, char* argv[]) {
121 // If we don't have any BattOr to use, exit. 118 // If we don't have any BattOr to use, exit.
122 std::string path = BattOrFinder::FindBattOr(); 119 std::string path = BattOrFinder::FindBattOr();
123 if (path.empty()) { 120 if (path.empty()) {
124 std::cout << "Unable to find a BattOr." << endl; 121 std::cout << "Unable to find a BattOr." << endl;
125 exit(1); 122 exit(1);
126 } 123 }
127 124
128 SetUp(path); 125 SetUp(path);
129 126
130 std::string cmd; 127 base::ThreadTaskRunnerHandle::Get()->PostTask(
131 for (;;) { 128 FROM_HERE,
132 std::getline(std::cin, cmd); 129 base::Bind(&BattOrAgentBin::RunNextCommand, base::Unretained(this)));
133 130 ui_thread_run_loop_.Run();
134 if (cmd == "StartTracing") {
135 StartTracing();
136 } else if (cmd.find("StopTracing") != std::string::npos) {
137 std::vector<std::string> tokens = TokenizeString(cmd);
138 if (tokens[0] != "StopTracing" || tokens.size() > 2) {
139 std::cout << "Invalid StopTracing command." << endl;
140 std::cout << kUsage << endl;
141 continue;
142 }
143
144 std::string trace_output_file =
145 tokens.size() == 2 : tokens[1] : std::string();
146
147 StopTracing(trace_output_file);
148 break;
149 } else if (cmd == "SupportsExplicitClockSync") {
150 PrintSupportsExplicitClockSync();
151 } else if (cmd.find("RecordClockSyncMarker") != std::string::npos) {
152 std::vector<std::string> tokens = TokenizeString(cmd);
153 if (tokens.size() != 2 || tokens[0] != "RecordClockSyncMarker") {
154 std::cout << "Invalid RecordClockSyncMarker command." << endl;
155 std::cout << kUsage << endl;
156 continue;
157 }
158
159 RecordClockSyncMarker(tokens[1]);
160 } else if (cmd == "Exit") {
161 break;
162 } else {
163 std::cout << kUsage << endl;
164 }
165 }
166 131
167 TearDown(); 132 TearDown();
168 return 0; 133 return 0;
169 } 134 }
170 135
171 // Performs any setup necessary for the BattOr binary to run. 136 // Performs any setup necessary for the BattOr binary to run.
172 void SetUp(const std::string& path) { 137 void SetUp(const std::string& path) {
173 // TODO(charliea): Investigate whether it's possible to replace this
174 // separate thread with a combination of MessageLoopForIO and RunLoop.
175 base::Thread::Options io_thread_options; 138 base::Thread::Options io_thread_options;
176 io_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_IO; 139 io_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_IO;
177 if (!io_thread_.StartWithOptions(io_thread_options)) { 140 if (!io_thread_.StartWithOptions(io_thread_options)) {
178 ExitFromThreadStartFailure(kIoThreadName); 141 ExitFromThreadStartFailure(kIoThreadName);
179 } 142 }
180 143
144 base::WaitableEvent done(false, false);
oystein (OOO til 10th of July) 2016/05/04 16:52:11 Is this actually needed, if all interaction with t
charliea (OOO until 10-5) 2016/05/04 17:48:21 Nope. Done.
181 io_thread_.task_runner()->PostTask( 145 io_thread_.task_runner()->PostTask(
182 FROM_HERE, 146 FROM_HERE,
183 base::Bind(&BattOrAgentBin::CreateAgent, base::Unretained(this), path)); 147 base::Bind(&BattOrAgentBin::CreateAgent, base::Unretained(this), path,
184 done_.Wait(); 148 base::ThreadTaskRunnerHandle::Get(), &done));
149 done.Wait();
185 } 150 }
186 151
187 // Performs any cleanup necessary after the BattOr binary is done running. 152 // Performs any cleanup necessary after the BattOr binary is done running.
188 void TearDown() { 153 void TearDown() {
154 base::WaitableEvent done(false, false);
189 io_thread_.task_runner()->PostTask( 155 io_thread_.task_runner()->PostTask(
156 FROM_HERE, base::Bind(&BattOrAgentBin::DeleteAgent,
157 base::Unretained(this), &done));
158 done.Wait();
159 }
160
161 void RunNextCommand() {
162 std::string cmd;
163 std::getline(std::cin, cmd);
164
165 if (cmd == "StartTracing") {
166 StartTracing();
167 } else if (cmd.find("StopTracing") != std::string::npos) {
168 std::vector<std::string> tokens = TokenizeString(cmd);
169
170 if (tokens[0] != "StopTracing" || tokens.size() > 2) {
171 std::cout << "Invalid StopTracing command." << endl;
172 std::cout << kUsage << endl;
173 PostRunNextCommand();
174 return;
175 }
176
177 // tokens[1] contains the optional output file argument, which allows
178 // users to dump the trace to a file instead instead of to STDOUT.
179 std::string trace_output_file =
180 tokens.size() == 2 ? tokens[1] : std::string();
181
182 StopTracing(trace_output_file);
183 } else if (cmd == "SupportsExplicitClockSync") {
184 PrintSupportsExplicitClockSync();
185 PostRunNextCommand();
186 } else if (cmd.find("RecordClockSyncMarker") != std::string::npos) {
187 std::vector<std::string> tokens = TokenizeString(cmd);
188 if (tokens.size() != 2 || tokens[0] != "RecordClockSyncMarker") {
189 std::cout << "Invalid RecordClockSyncMarker command." << endl;
190 std::cout << kUsage << endl;
191 PostRunNextCommand();
192 return;
193 }
194
195 RecordClockSyncMarker(tokens[1]);
196 } else if (cmd == "Exit") {
197 ui_thread_message_loop_.task_runner()->PostTask(
198 FROM_HERE, ui_thread_run_loop_.QuitClosure());
199 } else {
200 std::cout << kUsage << endl;
201 PostRunNextCommand();
202 }
203 }
204
205 void PostRunNextCommand() {
206 ui_thread_message_loop_.task_runner()->PostTask(
190 FROM_HERE, 207 FROM_HERE,
191 base::Bind(&BattOrAgentBin::DeleteAgent, base::Unretained(this))); 208 base::Bind(&BattOrAgentBin::RunNextCommand, base::Unretained(this)));
192 done_.Wait();
193 } 209 }
194 210
195 void StartTracing() { 211 void StartTracing() {
196 io_thread_.task_runner()->PostTask( 212 io_thread_.task_runner()->PostTask(
197 FROM_HERE, 213 FROM_HERE,
198 base::Bind(&BattOrAgent::StartTracing, base::Unretained(agent_.get()))); 214 base::Bind(&BattOrAgent::StartTracing, base::Unretained(agent_.get())));
199 done_.Wait();
200 } 215 }
201 216
202 void OnStartTracingComplete(BattOrError error) override { 217 void OnStartTracingComplete(BattOrError error) override {
203 if (error == BATTOR_ERROR_NONE) 218 if (error == BATTOR_ERROR_NONE)
204 std::cout << "Done." << endl; 219 std::cout << "Done." << endl;
205 else 220 else
206 HandleError(error); 221 HandleError(error);
207 222
208 done_.Signal(); 223 PostRunNextCommand();
209 } 224 }
210 225
211 void StopTracing(const std::string& trace_output_file) { 226 void StopTracing(const std::string& trace_output_file) {
212 trace_output_file_ = trace_output_file; 227 trace_output_file_ = trace_output_file;
213 io_thread_.task_runner()->PostTask( 228 io_thread_.task_runner()->PostTask(
214 FROM_HERE, 229 FROM_HERE,
215 base::Bind(&BattOrAgent::StopTracing, base::Unretained(agent_.get()))); 230 base::Bind(&BattOrAgent::StopTracing, base::Unretained(agent_.get())));
216 done_.Wait();
217 trace_output_file_ = std::string();
218 } 231 }
219 232
220 void OnStopTracingComplete(const std::string& trace, 233 void OnStopTracingComplete(const std::string& trace,
221 BattOrError error) override { 234 BattOrError error) override {
222 if (error == BATTOR_ERROR_NONE) { 235 if (error == BATTOR_ERROR_NONE) {
223 if (trace_output_file_.empty()) { 236 if (trace_output_file_.empty()) {
224 std::cout << trace; 237 std::cout << trace;
225 } else { 238 } else {
226 std::ofstream trace_stream(trace_output_file_); 239 std::ofstream trace_stream(trace_output_file_);
227 if (!trace_stream.is_open()) { 240 if (!trace_stream.is_open()) {
228 std::cout << "Tracing output file could not be opened." << endl; 241 std::cout << "Tracing output file could not be opened." << endl;
229 exit(1); 242 exit(1);
230 } 243 }
231 trace_stream << trace; 244 trace_stream << trace;
232 trace_stream.close(); 245 trace_stream.close();
233 } 246 }
234 std::cout << "Done." << endl; 247 std::cout << "Done." << endl;
235 } else { 248 } else {
236 HandleError(error); 249 HandleError(error);
237 } 250 }
238 251
239 done_.Signal(); 252 ui_thread_message_loop_.task_runner()->PostTask(
253 FROM_HERE, ui_thread_run_loop_.QuitClosure());
240 } 254 }
241 255
242 void RecordClockSyncMarker(const std::string& marker) { 256 void RecordClockSyncMarker(const std::string& marker) {
243 io_thread_.task_runner()->PostTask( 257 io_thread_.task_runner()->PostTask(
244 FROM_HERE, base::Bind(&BattOrAgent::RecordClockSyncMarker, 258 FROM_HERE, base::Bind(&BattOrAgent::RecordClockSyncMarker,
245 base::Unretained(agent_.get()), marker)); 259 base::Unretained(agent_.get()), marker));
246 done_.Wait();
247 } 260 }
248 261
249 void OnRecordClockSyncMarkerComplete(BattOrError error) override { 262 void OnRecordClockSyncMarkerComplete(BattOrError error) override {
250 if (error == BATTOR_ERROR_NONE) 263 if (error == BATTOR_ERROR_NONE)
251 std::cout << "Done." << endl; 264 std::cout << "Done." << endl;
252 else 265 else
253 HandleError(error); 266 HandleError(error);
254 267
255 done_.Signal(); 268 PostRunNextCommand();
256 } 269 }
257 270
258 // Postable task for creating the BattOrAgent. Because the BattOrAgent has 271 // Postable task for creating the BattOrAgent. Because the BattOrAgent has
259 // uber thread safe dependencies, all interactions with it, including creating 272 // uber thread safe dependencies, all interactions with it, including creating
260 // and deleting it, MUST happen on the IO thread. 273 // and deleting it, MUST happen on the IO thread.
261 void CreateAgent(const std::string& path) { 274 void CreateAgent(
262 // In Chrome, we already have file and UI threads running. Because the 275 const std::string& path,
263 // Chrome serial libraries rely on having those threads available, we have 276 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner,
264 // to spin up our own because we're in a separate binary. 277 base::WaitableEvent* done) {
278 // In Chrome, we already have a file thread running. Because the Chrome
279 // serial library relies on having it available, we have to spin up our own.
265 if (!file_thread_.Start()) 280 if (!file_thread_.Start())
266 ExitFromThreadStartFailure(kFileThreadName); 281 ExitFromThreadStartFailure(kFileThreadName);
267 282
268 base::Thread::Options ui_thread_options;
269 ui_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_UI;
270 if (!ui_thread_.StartWithOptions(ui_thread_options)) {
271 ExitFromThreadStartFailure(kUiThreadName);
272 }
273
274 agent_.reset(new BattOrAgent(path, this, file_thread_.task_runner(), 283 agent_.reset(new BattOrAgent(path, this, file_thread_.task_runner(),
275 ui_thread_.task_runner())); 284 ui_thread_task_runner));
276 done_.Signal(); 285 done->Signal();
277 } 286 }
278 287
279 // Postable task for deleting the BattOrAgent. See the comment for 288 // Postable task for deleting the BattOrAgent. See the comment for
280 // CreateAgent() above regarding why this is necessary. 289 // CreateAgent() above regarding why this is necessary.
281 void DeleteAgent() { 290 void DeleteAgent(base::WaitableEvent* done) {
282 agent_.reset(nullptr); 291 agent_.reset();
283 done_.Signal(); 292 done->Signal();
284 } 293 }
285 294
286 private: 295 private:
287 // Event signaled when an async task has finished executing. 296 // NOTE: ui_thread_message_loop_ must appear before ui_thread_run_loop_ here
288 base::WaitableEvent done_; 297 // because ui_thread_run_loop_ checks for the current MessageLoop during
298 // initialization.
299 base::MessageLoopForUI ui_thread_message_loop_;
300 base::RunLoop ui_thread_run_loop_;
289 301
290 // Threads needed for serial communication. 302 // Threads needed for serial communication.
291 base::Thread io_thread_; 303 base::Thread io_thread_;
292 base::Thread file_thread_; 304 base::Thread file_thread_;
293 base::Thread ui_thread_;
294 305
295 // The agent capable of asynchronously communicating with the BattOr. 306 // The agent capable of asynchronously communicating with the BattOr.
296 std::unique_ptr<BattOrAgent> agent_; 307 std::unique_ptr<BattOrAgent> agent_;
297 308
298 std::string trace_output_file_; 309 std::string trace_output_file_;
299 }; 310 };
300 311
301 } // namespace battor 312 } // namespace battor
302 313
303 int main(int argc, char* argv[]) { 314 int main(int argc, char* argv[]) {
304 base::AtExitManager exit_manager; 315 base::AtExitManager exit_manager;
305 base::CommandLine::Init(argc, argv); 316 base::CommandLine::Init(argc, argv);
306 battor::BattOrAgentBin bin; 317 battor::BattOrAgentBin bin;
307 return bin.Run(argc, argv); 318 return bin.Run(argc, argv);
308 } 319 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698