blob: f3dfcf1eca0c0fb7fecee2f3f597a4483c4f9296 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/trace/tests/run_test.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/zx/channel.h>
#include <lib/zx/process.h>
#include <lib/zx/time.h>
#include <zircon/device/vfs.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <string>
#include <vector>
#include "garnet/bin/trace/spec.h"
#include "garnet/bin/trace/tests/component_context.h"
#include "garnet/bin/trace/tests/integration_test_utils.h"
#include "src/developer/tracing/lib/test_utils/run_program.h"
#include "src/lib/files/file.h"
#include "src/lib/fxl/arraysize.h"
#include "src/lib/fxl/log_settings.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/strings/join_strings.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace tracing {
namespace test {
// The "path" of the trace program from outside the trace package.
const char kTraceProgramUrl[] = "fuchsia-pkg://fuchsia.com/trace#meta/trace.cmx";
// The path of the trace program as a shell command.
const char kTraceProgramPath[] = "/bin/trace";
static bool ReadTspec(const std::string& tspec_path, tracing::Spec* spec) {
std::string tspec_contents;
if (!files::ReadFileToString(tspec_path, &tspec_contents)) {
FXL_LOG(ERROR) << "Can't read test spec: " << tspec_path;
return false;
}
if (!tracing::DecodeSpec(tspec_contents, spec)) {
FXL_LOG(ERROR) << "Error decoding test spec: " << tspec_path;
return false;
}
return true;
}
static bool BuildTraceProgramArgs(const std::string& relative_tspec_path,
const std::string& relative_output_file_path,
std::vector<std::string>* args) {
tracing::Spec spec;
if (!ReadTspec(std::string(kTestPackagePath) + "/" + relative_tspec_path, &spec)) {
return false;
}
AppendLoggingArgs(args, "");
args->push_back("record");
args->push_back(fxl::StringPrintf(
"--spec-file=%s",
(std::string(kSpawnedTestPackagePath) + "/" + relative_tspec_path).c_str()));
args->push_back(fxl::StringPrintf(
"--output-file=%s",
(std::string(kSpawnedTestTmpPath) + "/" + relative_output_file_path).c_str()));
AppendLoggingArgs(args, "--append-args=");
// Note that |relative_tspec_path| cannot have a comma.
args->push_back(
fxl::StringPrintf("--append-args=run,%s",
(std::string(spec.spawn ? kSpawnedTestPackagePath : kTestPackagePath) +
"/" + relative_tspec_path)
.c_str()));
return true;
}
static void BuildVerificationProgramArgs(const std::string& tspec_path,
const std::string& output_file_path,
std::vector<std::string>* args) {
AppendLoggingArgs(args, "");
args->push_back("verify");
args->push_back(tspec_path);
args->push_back(output_file_path);
}
static zx_status_t AddAuxDirToSpawnAction(const char* local_path, const char* remote_path,
fdio_spawn_action_t* actions) {
zx::channel dir, server;
zx_status_t status = zx::channel::create(0, &dir, &server);
if (status != ZX_OK) {
FXL_PLOG(ERROR, status) << "Could not create channel aux directory";
return false;
}
status = fdio_open(local_path, ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE, server.release());
if (status != ZX_OK) {
FXL_PLOG(ERROR, status) << "Could not open " << local_path;
return false;
}
actions->action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY;
actions->ns.prefix = remote_path;
actions->ns.handle = dir.release();
return ZX_OK;
}
bool RunTrace(const zx::job& job, const std::vector<std::string>& args, zx::process* out_child) {
std::vector<std::string> argv{kTraceProgramPath};
for (const auto& arg : args) {
argv.push_back(arg);
}
size_t num_actions = 0;
fdio_spawn_action_t spawn_actions[2];
// Add a path to our /pkg so trace can read, e.g., tspec files.
zx_status_t status = AddAuxDirToSpawnAction(kTestPackagePath, kSpawnedTestPackagePath,
&spawn_actions[num_actions++]);
if (status != ZX_OK) {
return false;
}
// Add a path to our /tmp so trace can write, e.g., trace files there.
status = AddAuxDirToSpawnAction(kTestTmpPath, kSpawnedTestTmpPath, &spawn_actions[num_actions++]);
if (status != ZX_OK) {
return false;
}
FXL_CHECK(num_actions <= arraysize(spawn_actions));
return RunProgram(job, argv, num_actions, spawn_actions, out_child) == ZX_OK;
}
bool RunTraceAndWait(const zx::job& job, const std::vector<std::string>& args) {
zx::process subprocess;
if (!RunTrace(job, args, &subprocess)) {
return false;
}
int64_t return_code;
if (!WaitAndGetReturnCode("trace", subprocess, &return_code)) {
return false;
}
if (return_code != 0) {
FXL_LOG(ERROR) << "trace exited with return code " << return_code;
return false;
}
return true;
}
static bool AddAuxDirToLaunchInfo(const char* local_path, const char* remote_path,
fuchsia::sys::FlatNamespace* flat_namespace) {
zx::channel dir, server;
zx_status_t status = zx::channel::create(0, &dir, &server);
if (status != ZX_OK) {
FXL_PLOG(ERROR, status) << "Could not create channel aux directory";
return false;
}
status = fdio_open(local_path, ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE, server.release());
if (status != ZX_OK) {
FXL_PLOG(ERROR, status) << "Could not open " << local_path;
return false;
}
flat_namespace->paths.push_back(remote_path);
flat_namespace->directories.push_back(std::move(dir));
return true;
}
static bool RunTraceComponentAndWait(const std::string& app, const std::vector<std::string>& args) {
auto flat_namespace = fuchsia::sys::FlatNamespace::New();
// Add a path to our /pkg so trace can read tspec files.
if (!AddAuxDirToLaunchInfo(kTestPackagePath, kSpawnedTestPackagePath, flat_namespace.get())) {
return false;
}
// Add a path to our /tmp so trace can write trace files there.
if (!AddAuxDirToLaunchInfo(kTestTmpPath, kSpawnedTestTmpPath, flat_namespace.get())) {
return false;
}
async::Loop loop{&kAsyncLoopConfigAttachToCurrentThread};
sys::ComponentContext* context = tracing::test::GetComponentContext();
return RunComponentAndWait(&loop, context, app, args, std::move(flat_namespace));
}
bool RunTspec(const std::string& relative_tspec_path,
const std::string& relative_output_file_path) {
std::vector<std::string> args;
if (!BuildTraceProgramArgs(relative_tspec_path, relative_output_file_path, &args)) {
return false;
}
FXL_LOG(INFO) << "Running tspec " << relative_tspec_path << ", output file "
<< relative_output_file_path;
return RunTraceComponentAndWait(kTraceProgramUrl, args);
}
bool VerifyTspec(const std::string& relative_tspec_path,
const std::string& relative_output_file_path) {
tracing::Spec spec;
if (!ReadTspec(std::string(kTestPackagePath) + "/" + relative_tspec_path, &spec)) {
return false;
}
FXL_DCHECK(spec.app);
const std::string& program_path = *spec.app;
std::vector<std::string> args;
BuildVerificationProgramArgs(
(std::string(spec.spawn ? kSpawnedTestPackagePath : kTestPackagePath) + "/" +
relative_tspec_path),
std::string(kSpawnedTestTmpPath) + "/" + relative_output_file_path, &args);
FXL_LOG(INFO) << "Verifying tspec " << relative_tspec_path << ", output file "
<< relative_output_file_path;
// For consistency we do the exact same thing that the trace program does.
if (spec.spawn) {
zx::job job{}; // -> default job
std::vector<std::string> argv{program_path};
for (const auto& arg : args) {
argv.push_back(arg);
}
return RunProgramAndWait(job, argv, 0, nullptr);
} else {
return RunTraceComponentAndWait(program_path, args);
}
}
} // namespace test
} // namespace tracing