| 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 // tokens[1] contains the optional output file argument, which allows | |
| 145 // users to dump the trace to a file instead instead of to STDOUT. | |
| 146 std::string trace_output_file = | |
| 147 tokens.size() == 2 ? tokens[1] : std::string(); | |
| 148 | |
| 149 StopTracing(trace_output_file); | |
| 150 break; | |
| 151 } else if (cmd == "SupportsExplicitClockSync") { | |
| 152 PrintSupportsExplicitClockSync(); | |
| 153 } else if (cmd.find("RecordClockSyncMarker") != std::string::npos) { | |
| 154 std::vector<std::string> tokens = TokenizeString(cmd); | |
| 155 if (tokens.size() != 2 || tokens[0] != "RecordClockSyncMarker") { | |
| 156 std::cout << "Invalid RecordClockSyncMarker command." << endl; | |
| 157 std::cout << kUsage << endl; | |
| 158 continue; | |
| 159 } | |
| 160 | |
| 161 RecordClockSyncMarker(tokens[1]); | |
| 162 } else if (cmd == "Exit") { | |
| 163 break; | |
| 164 } else { | |
| 165 std::cout << kUsage << endl; | |
| 166 } | |
| 167 } | |
| 168 | 131 |
| 169 TearDown(); | 132 TearDown(); |
| 170 return 0; | 133 return 0; |
| 171 } | 134 } |
| 172 | 135 |
| 173 // Performs any setup necessary for the BattOr binary to run. | 136 // Performs any setup necessary for the BattOr binary to run. |
| 174 void SetUp(const std::string& path) { | 137 void SetUp(const std::string& path) { |
| 175 // TODO(charliea): Investigate whether it's possible to replace this | |
| 176 // separate thread with a combination of MessageLoopForIO and RunLoop. | |
| 177 base::Thread::Options io_thread_options; | 138 base::Thread::Options io_thread_options; |
| 178 io_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_IO; | 139 io_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_IO; |
| 179 if (!io_thread_.StartWithOptions(io_thread_options)) { | 140 if (!io_thread_.StartWithOptions(io_thread_options)) { |
| 180 ExitFromThreadStartFailure(kIoThreadName); | 141 ExitFromThreadStartFailure(kIoThreadName); |
| 181 } | 142 } |
| 182 | 143 |
| 183 io_thread_.task_runner()->PostTask( | 144 io_thread_.task_runner()->PostTask( |
| 184 FROM_HERE, | 145 FROM_HERE, |
| 185 base::Bind(&BattOrAgentBin::CreateAgent, base::Unretained(this), path)); | 146 base::Bind(&BattOrAgentBin::CreateAgent, base::Unretained(this), path, |
| 186 done_.Wait(); | 147 base::ThreadTaskRunnerHandle::Get())); |
| 187 } | 148 } |
| 188 | 149 |
| 189 // Performs any cleanup necessary after the BattOr binary is done running. | 150 // Performs any cleanup necessary after the BattOr binary is done running. |
| 190 void TearDown() { | 151 void TearDown() { |
| 152 base::WaitableEvent done(false, false); |
| 191 io_thread_.task_runner()->PostTask( | 153 io_thread_.task_runner()->PostTask( |
| 154 FROM_HERE, base::Bind(&BattOrAgentBin::DeleteAgent, |
| 155 base::Unretained(this), &done)); |
| 156 done.Wait(); |
| 157 } |
| 158 |
| 159 void RunNextCommand() { |
| 160 std::string cmd; |
| 161 std::getline(std::cin, cmd); |
| 162 |
| 163 if (cmd == "StartTracing") { |
| 164 StartTracing(); |
| 165 } else if (cmd.find("StopTracing") != std::string::npos) { |
| 166 std::vector<std::string> tokens = TokenizeString(cmd); |
| 167 |
| 168 if (tokens[0] != "StopTracing" || tokens.size() > 2) { |
| 169 std::cout << "Invalid StopTracing command." << endl; |
| 170 std::cout << kUsage << endl; |
| 171 PostRunNextCommand(); |
| 172 return; |
| 173 } |
| 174 |
| 175 // tokens[1] contains the optional output file argument, which allows |
| 176 // users to dump the trace to a file instead instead of to STDOUT. |
| 177 std::string trace_output_file = |
| 178 tokens.size() == 2 ? tokens[1] : std::string(); |
| 179 |
| 180 StopTracing(trace_output_file); |
| 181 } else if (cmd == "SupportsExplicitClockSync") { |
| 182 PrintSupportsExplicitClockSync(); |
| 183 PostRunNextCommand(); |
| 184 } else if (cmd.find("RecordClockSyncMarker") != std::string::npos) { |
| 185 std::vector<std::string> tokens = TokenizeString(cmd); |
| 186 if (tokens.size() != 2 || tokens[0] != "RecordClockSyncMarker") { |
| 187 std::cout << "Invalid RecordClockSyncMarker command." << endl; |
| 188 std::cout << kUsage << endl; |
| 189 PostRunNextCommand(); |
| 190 return; |
| 191 } |
| 192 |
| 193 RecordClockSyncMarker(tokens[1]); |
| 194 } else if (cmd == "Exit") { |
| 195 ui_thread_message_loop_.task_runner()->PostTask( |
| 196 FROM_HERE, ui_thread_run_loop_.QuitClosure()); |
| 197 } else { |
| 198 std::cout << kUsage << endl; |
| 199 PostRunNextCommand(); |
| 200 } |
| 201 } |
| 202 |
| 203 void PostRunNextCommand() { |
| 204 ui_thread_message_loop_.task_runner()->PostTask( |
| 192 FROM_HERE, | 205 FROM_HERE, |
| 193 base::Bind(&BattOrAgentBin::DeleteAgent, base::Unretained(this))); | 206 base::Bind(&BattOrAgentBin::RunNextCommand, base::Unretained(this))); |
| 194 done_.Wait(); | |
| 195 } | 207 } |
| 196 | 208 |
| 197 void StartTracing() { | 209 void StartTracing() { |
| 198 io_thread_.task_runner()->PostTask( | 210 io_thread_.task_runner()->PostTask( |
| 199 FROM_HERE, | 211 FROM_HERE, |
| 200 base::Bind(&BattOrAgent::StartTracing, base::Unretained(agent_.get()))); | 212 base::Bind(&BattOrAgent::StartTracing, base::Unretained(agent_.get()))); |
| 201 done_.Wait(); | |
| 202 } | 213 } |
| 203 | 214 |
| 204 void OnStartTracingComplete(BattOrError error) override { | 215 void OnStartTracingComplete(BattOrError error) override { |
| 205 if (error == BATTOR_ERROR_NONE) | 216 if (error == BATTOR_ERROR_NONE) |
| 206 std::cout << "Done." << endl; | 217 std::cout << "Done." << endl; |
| 207 else | 218 else |
| 208 HandleError(error); | 219 HandleError(error); |
| 209 | 220 |
| 210 done_.Signal(); | 221 PostRunNextCommand(); |
| 211 } | 222 } |
| 212 | 223 |
| 213 void StopTracing(const std::string& trace_output_file) { | 224 void StopTracing(const std::string& trace_output_file) { |
| 214 trace_output_file_ = trace_output_file; | 225 trace_output_file_ = trace_output_file; |
| 215 io_thread_.task_runner()->PostTask( | 226 io_thread_.task_runner()->PostTask( |
| 216 FROM_HERE, | 227 FROM_HERE, |
| 217 base::Bind(&BattOrAgent::StopTracing, base::Unretained(agent_.get()))); | 228 base::Bind(&BattOrAgent::StopTracing, base::Unretained(agent_.get()))); |
| 218 done_.Wait(); | |
| 219 trace_output_file_ = std::string(); | |
| 220 } | 229 } |
| 221 | 230 |
| 222 void OnStopTracingComplete(const std::string& trace, | 231 void OnStopTracingComplete(const std::string& trace, |
| 223 BattOrError error) override { | 232 BattOrError error) override { |
| 224 if (error == BATTOR_ERROR_NONE) { | 233 if (error == BATTOR_ERROR_NONE) { |
| 225 if (trace_output_file_.empty()) { | 234 if (trace_output_file_.empty()) { |
| 226 std::cout << trace; | 235 std::cout << trace; |
| 227 } else { | 236 } else { |
| 228 std::ofstream trace_stream(trace_output_file_); | 237 std::ofstream trace_stream(trace_output_file_); |
| 229 if (!trace_stream.is_open()) { | 238 if (!trace_stream.is_open()) { |
| 230 std::cout << "Tracing output file could not be opened." << endl; | 239 std::cout << "Tracing output file could not be opened." << endl; |
| 231 exit(1); | 240 exit(1); |
| 232 } | 241 } |
| 233 trace_stream << trace; | 242 trace_stream << trace; |
| 234 trace_stream.close(); | 243 trace_stream.close(); |
| 235 } | 244 } |
| 236 std::cout << "Done." << endl; | 245 std::cout << "Done." << endl; |
| 237 } else { | 246 } else { |
| 238 HandleError(error); | 247 HandleError(error); |
| 239 } | 248 } |
| 240 | 249 |
| 241 done_.Signal(); | 250 ui_thread_message_loop_.task_runner()->PostTask( |
| 251 FROM_HERE, ui_thread_run_loop_.QuitClosure()); |
| 242 } | 252 } |
| 243 | 253 |
| 244 void RecordClockSyncMarker(const std::string& marker) { | 254 void RecordClockSyncMarker(const std::string& marker) { |
| 245 io_thread_.task_runner()->PostTask( | 255 io_thread_.task_runner()->PostTask( |
| 246 FROM_HERE, base::Bind(&BattOrAgent::RecordClockSyncMarker, | 256 FROM_HERE, base::Bind(&BattOrAgent::RecordClockSyncMarker, |
| 247 base::Unretained(agent_.get()), marker)); | 257 base::Unretained(agent_.get()), marker)); |
| 248 done_.Wait(); | |
| 249 } | 258 } |
| 250 | 259 |
| 251 void OnRecordClockSyncMarkerComplete(BattOrError error) override { | 260 void OnRecordClockSyncMarkerComplete(BattOrError error) override { |
| 252 if (error == BATTOR_ERROR_NONE) | 261 if (error == BATTOR_ERROR_NONE) |
| 253 std::cout << "Done." << endl; | 262 std::cout << "Done." << endl; |
| 254 else | 263 else |
| 255 HandleError(error); | 264 HandleError(error); |
| 256 | 265 |
| 257 done_.Signal(); | 266 PostRunNextCommand(); |
| 258 } | 267 } |
| 259 | 268 |
| 260 // Postable task for creating the BattOrAgent. Because the BattOrAgent has | 269 // Postable task for creating the BattOrAgent. Because the BattOrAgent has |
| 261 // uber thread safe dependencies, all interactions with it, including creating | 270 // uber thread safe dependencies, all interactions with it, including creating |
| 262 // and deleting it, MUST happen on the IO thread. | 271 // and deleting it, MUST happen on the IO thread. |
| 263 void CreateAgent(const std::string& path) { | 272 void CreateAgent( |
| 264 // In Chrome, we already have file and UI threads running. Because the | 273 const std::string& path, |
| 265 // Chrome serial libraries rely on having those threads available, we have | 274 scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner) { |
| 266 // to spin up our own because we're in a separate binary. | 275 // In Chrome, we already have a file thread running. Because the Chrome |
| 276 // serial library relies on having it available, we have to spin up our own. |
| 267 if (!file_thread_.Start()) | 277 if (!file_thread_.Start()) |
| 268 ExitFromThreadStartFailure(kFileThreadName); | 278 ExitFromThreadStartFailure(kFileThreadName); |
| 269 | 279 |
| 270 base::Thread::Options ui_thread_options; | |
| 271 ui_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_UI; | |
| 272 if (!ui_thread_.StartWithOptions(ui_thread_options)) { | |
| 273 ExitFromThreadStartFailure(kUiThreadName); | |
| 274 } | |
| 275 | |
| 276 agent_.reset(new BattOrAgent(path, this, file_thread_.task_runner(), | 280 agent_.reset(new BattOrAgent(path, this, file_thread_.task_runner(), |
| 277 ui_thread_.task_runner())); | 281 ui_thread_task_runner)); |
| 278 done_.Signal(); | |
| 279 } | 282 } |
| 280 | 283 |
| 281 // Postable task for deleting the BattOrAgent. See the comment for | 284 // Postable task for deleting the BattOrAgent. See the comment for |
| 282 // CreateAgent() above regarding why this is necessary. | 285 // CreateAgent() above regarding why this is necessary. |
| 283 void DeleteAgent() { | 286 void DeleteAgent(base::WaitableEvent* done) { |
| 284 agent_.reset(nullptr); | 287 agent_.reset(); |
| 285 done_.Signal(); | 288 done->Signal(); |
| 286 } | 289 } |
| 287 | 290 |
| 288 private: | 291 private: |
| 289 // Event signaled when an async task has finished executing. | 292 // NOTE: ui_thread_message_loop_ must appear before ui_thread_run_loop_ here |
| 290 base::WaitableEvent done_; | 293 // because ui_thread_run_loop_ checks for the current MessageLoop during |
| 294 // initialization. |
| 295 base::MessageLoopForUI ui_thread_message_loop_; |
| 296 base::RunLoop ui_thread_run_loop_; |
| 291 | 297 |
| 292 // Threads needed for serial communication. | 298 // Threads needed for serial communication. |
| 293 base::Thread io_thread_; | 299 base::Thread io_thread_; |
| 294 base::Thread file_thread_; | 300 base::Thread file_thread_; |
| 295 base::Thread ui_thread_; | |
| 296 | 301 |
| 297 // The agent capable of asynchronously communicating with the BattOr. | 302 // The agent capable of asynchronously communicating with the BattOr. |
| 298 std::unique_ptr<BattOrAgent> agent_; | 303 std::unique_ptr<BattOrAgent> agent_; |
| 299 | 304 |
| 300 std::string trace_output_file_; | 305 std::string trace_output_file_; |
| 301 }; | 306 }; |
| 302 | 307 |
| 303 } // namespace battor | 308 } // namespace battor |
| 304 | 309 |
| 305 int main(int argc, char* argv[]) { | 310 int main(int argc, char* argv[]) { |
| 306 base::AtExitManager exit_manager; | 311 base::AtExitManager exit_manager; |
| 307 base::CommandLine::Init(argc, argv); | 312 base::CommandLine::Init(argc, argv); |
| 308 battor::BattOrAgentBin bin; | 313 battor::BattOrAgentBin bin; |
| 309 return bin.Run(argc, argv); | 314 return bin.Run(argc, argv); |
| 310 } | 315 } |
| OLD | NEW |