blob: 78d0bf9f1ecee56f942172c0743a09c2a23109c8 [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 "test_server.h"
#include <array>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <string>
#include <vector>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include "garnet/lib/debugger_utils/jobs.h"
#include "garnet/lib/debugger_utils/sysinfo.h"
#include "garnet/lib/debugger_utils/util.h"
#include "gtest/gtest.h"
#include "lib/component/cpp/environment_services_helper.h"
#include "lib/fxl/arraysize.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_printf.h"
namespace inferior_control {
TestServer::TestServer()
: Server(debugger_utils::GetRootJob(), debugger_utils::GetDefaultJob()),
services_(component::GetEnvironmentServices()) {}
void TestServer::SetUp() {
ASSERT_TRUE(exception_port_.Run());
exception_port_started_ = true;
}
void TestServer::TearDown() {
if (exception_port_started_) {
// Tell the exception port to quit and wait for it to finish.
exception_port_.Quit();
exception_port_started_ = false;
}
EXPECT_TRUE(run_status_);
}
bool TestServer::Run() {
// Start the main loop.
message_loop_.Run();
FXL_LOG(INFO) << "Main loop exited";
// |run_status_| is checked by TearDown().
return true;
}
bool TestServer::SetupInferior(const std::vector<std::string>& argv) {
auto inferior = new Process(this, this, services_);
inferior->set_argv(argv);
// We take over ownership of |inferior| here.
set_current_process(inferior);
return true;
}
bool TestServer::RunHelperProgram(zx::channel channel) {
Process* process = current_process();
const debugger_utils::Argv& argv = process->argv();
FXL_LOG(INFO) << "Starting program: " << argv[0];
if (channel.is_valid()) {
process->AddStartupHandle({
.id = PA_HND(PA_USER0, 0),
.handle = std::move(channel),
});
}
if (!process->Initialize()) {
FXL_LOG(ERROR) << "failed to set up inferior";
return false;
}
FXL_DCHECK(!process->IsLive());
if (!process->Start()) {
FXL_LOG(ERROR) << "failed to start process";
return false;
}
FXL_DCHECK(process->IsLive());
return true;
}
// This method is intended to be called at the end of tests.
// There are several things we check for successful exit, and it's easier
// to have them all in one place. Note that we use gtest macros instead of
// FXL_DCHECK because these all verify test conditions.
bool TestServer::TestSuccessfulExit() {
auto inferior = current_process();
if (inferior == nullptr) {
FXL_LOG(ERROR) << "inferior == nullptr";
return false;
}
if (inferior->IsAttached()) {
FXL_LOG(ERROR) << "inferior still attached";
return false;
}
if (inferior->IsLive()) {
FXL_LOG(ERROR) << "inferior still live";
return false;
}
// We can't get the exit code from |inferior| as we've detached. Instead
// we save it on process exit.
if (!exit_code_set_ || exit_code_ != 0) {
FXL_LOG(ERROR) << "inferior didn't cleanly exit";
return false;
}
return true;
}
void TestServer::OnThreadStarting(Process* process, Thread* thread,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
PrintException(stdout, thread, ZX_EXCP_THREAD_STARTING, context);
switch (process->state()) {
case Process::State::kStarting:
case Process::State::kRunning:
break;
default:
FXL_DCHECK(false);
}
thread->Resume();
}
void TestServer::OnThreadExiting(Process* process, Thread* thread,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
PrintException(stdout, thread, ZX_EXCP_THREAD_EXITING, context);
// We still have to "resume" the thread so that the o/s will complete the
// termination of the thread.
thread->ResumeForExit();
}
void TestServer::OnProcessExit(Process* process) {
FXL_DCHECK(process);
// Save the exit code for later testing.
exit_code_ = process->ExitCode();
exit_code_set_ = true;
printf("Process %s is gone, rc %d\n", process->GetName().c_str(), exit_code_);
// If the process is gone exit main loop.
QuitMessageLoop(true);
}
void TestServer::OnArchitecturalException(
Process* process, Thread* thread, const zx_excp_type_t type,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
PrintException(stdout, thread, type, context);
QuitMessageLoop(true);
}
void TestServer::OnSyntheticException(Process* process, Thread* thread,
zx_excp_type_t type,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
PrintException(stdout, thread, type, context);
QuitMessageLoop(true);
}
} // namespace inferior_control