blob: 26c855c6e870107755917d4f2e70524799f53ce0 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file provides a thin binary wrapper around the BattOr Agent
// library. This binary wrapper provides a means for non-C++ tracing
// controllers, such as Telemetry and Android Systrace, to issue high-level
// tracing commands to the BattOr..
#include <iostream>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/thread.h"
#include "tools/battor_agent/battor_agent.h"
#include "tools/battor_agent/battor_error.h"
using std::endl;
namespace {
const char kIoThreadName[] = "BattOr IO Thread";
const char kFileThreadName[] = "BattOr File Thread";
const char kUiThreadName[] = "BattOr UI Thread";
const int32 kBattOrCommandTimeoutSeconds = 10;
void PrintUsage() {
std::cout << "Usage: battor_agent <command> <arguments>" << endl
<< endl
<< "Commands:" << endl
<< endl
<< " StartTracing <path>" << endl
<< " StopTracing <path>" << endl
<< " SupportsExplicitClockSync" << endl
<< " RecordClockSyncMarker <path> <marker>" << endl
<< " IssueClockSyncMarker <path>" << endl
<< " Help" << endl;
}
void PrintSupportsExplicitClockSync() {
std::cout << battor::BattOrAgent::SupportsExplicitClockSync() << endl;
}
// Retrieves argument argnum from the argument list, printing the usage
// guidelines and exiting with an error code if the argument doesn't exist.
std::string GetArg(int argnum, int argc, char* argv[]) {
if (argnum >= argc) {
PrintUsage();
PLOG(FATAL);
}
return argv[argnum];
}
// Checks if an error occurred and, if it did, prints the error and exits
// with an error code.
void CheckError(battor::BattOrError error) {
if (error != battor::BATTOR_ERROR_NONE) {
LOG(ERROR) << "Fatal error when communicating with the BattOr: " << error;
PLOG(FATAL);
}
}
// Prints an error message and exits due to a required thread failing to start.
void ExitFromThreadStartFailure(const std::string& thread_name) {
LOG(ERROR) << "Failed to start " << thread_name;
PLOG(FATAL);
}
} // namespace
namespace battor {
// Wrapper class containing all state necessary for an independent binary to
// use a BattOrAgent to communicate with a BattOr.
class BattOrAgentBin : public BattOrAgent::Listener {
public:
BattOrAgentBin()
: done_(false, false),
io_thread_(kIoThreadName),
file_thread_(kFileThreadName),
ui_thread_(kUiThreadName) {}
~BattOrAgentBin() {
DCHECK(!agent_);
}
// Runs the BattOr binary and returns the exit code.
int Run(int argc, char* argv[]) {
std::string cmd = GetArg(1, argc, argv);
// SupportsExplicitClockSync doesn't need to use the serial connection, so
// handle it separately.
if (cmd == "SupportsExplicitClockSync") {
PrintSupportsExplicitClockSync();
return 0;
}
std::string path = GetArg(2, argc, argv);
SetUp(path);
if (cmd == "StartTracing") {
StartTracing();
} else if (cmd == "StopTracing") {
// TODO(charliea): Write StopTracing.
} else if (cmd == "RecordClockSyncMarker") {
// TODO(charliea): Write RecordClockSyncMarker.
} else if (cmd == "IssueClockSyncMarker") {
// TODO(charliea): Write IssueClockSyncMarker.
} else {
TearDown();
PrintUsage();
return 1;
}
TearDown();
return 0;
}
// Performs any setup necessary for the BattOr binary to run.
void SetUp(const std::string& path) {
// TODO(charliea): Investigate whether it's possible to replace this
// separate thread with a combination of MessageLoopForIO and RunLoop.
base::Thread::Options io_thread_options;
io_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_IO;
if (!io_thread_.StartWithOptions(io_thread_options)) {
ExitFromThreadStartFailure(kIoThreadName);
}
io_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&BattOrAgentBin::CreateAgent, base::Unretained(this), path));
CheckError(AwaitResult());
}
// Performs any cleanup necessary after the BattOr binary is done running.
void TearDown() {
io_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&BattOrAgentBin::DeleteAgent, base::Unretained(this)));
CheckError(AwaitResult());
}
void StartTracing() {
io_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&BattOrAgent::StartTracing, base::Unretained(agent_.get())));
CheckError(AwaitResult());
}
void OnStartTracingComplete(BattOrError error) override {
error_ = error;
done_.Signal();
}
// Postable task for creating the BattOrAgent. Because the BattOrAgent has
// uber thread safe dependencies, all interactions with it, including creating
// and deleting it, MUST happen on the IO thread.
void CreateAgent(const std::string& path) {
// In Chrome, we already have file and UI threads running. Because the
// Chrome serial libraries rely on having those threads available, we have
// to spin up our own because we're in a separate binary.
if (!file_thread_.Start())
ExitFromThreadStartFailure(kFileThreadName);
base::Thread::Options ui_thread_options;
ui_thread_options.message_loop_type = base::MessageLoopForIO::TYPE_UI;
if (!ui_thread_.StartWithOptions(ui_thread_options)) {
ExitFromThreadStartFailure(kUiThreadName);
}
agent_.reset(new BattOrAgent(file_thread_.task_runner(),
ui_thread_.task_runner(), path, this));
error_ = BATTOR_ERROR_NONE;
done_.Signal();
}
// Postable task for deleting the BattOrAgent. See the comment for
// CreateAgent() above regarding why this is necessary.
void DeleteAgent() {
agent_.reset(nullptr);
error_ = BATTOR_ERROR_NONE;
done_.Signal();
}
// Waits until the previously executed command has finished executing.
BattOrError AwaitResult() {
if (!done_.TimedWait(
base::TimeDelta::FromSeconds(kBattOrCommandTimeoutSeconds)))
return BATTOR_ERROR_TIMEOUT;
return error_;
}
private:
// Event signaled when an async task has finished executing.
base::WaitableEvent done_;
// The error from the last async command that finished.
BattOrError error_;
// Threads needed for serial communication.
base::Thread io_thread_;
base::Thread file_thread_;
base::Thread ui_thread_;
// The agent capable of asynchronously communicating with the BattOr.
scoped_ptr<BattOrAgent> agent_;
};
} // namespace battor
int main(int argc, char* argv[]) {
base::AtExitManager exit_manager;
battor::BattOrAgentBin bin;
return bin.Run(argc, argv);
}