Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |