blob: c9370412a114a50202cf2e01f6b164afa4631d91 [file] [log] [blame] [edit]
// Copyright 2015 The Chromium OS 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 "crash-reporter/arc_collector.h"
#include <memory>
#include <unordered_map>
#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using brillo::ClearLog;
using brillo::FindLog;
using brillo::GetLog;
class MockArcCollector : public ArcCollector {
public:
using ArcCollector::ArcCollector;
MOCK_METHOD0(SetUpDBus, void());
};
class Test : public ::testing::Test {
protected:
void Initialize() {
EXPECT_CALL(*collector_, SetUpDBus()).WillRepeatedly(testing::Return());
collector_->Initialize(CountCrash, IsFeedbackAllowed, false, false, "");
ClearLog();
}
std::unique_ptr<MockArcCollector> collector_;
private:
static void CountCrash() {}
static bool IsFeedbackAllowed() { return true; }
};
class ArcCollectorTest : public Test {
protected:
class MockContext : public ArcCollector::Context {
public:
void SetArcPid(pid_t pid) { arc_pid_ = pid; }
void AddProcess(pid_t pid,
const char *ns,
const char *exe,
const char *cmd) {
DCHECK_EQ(processes_.count(pid), 0u);
DCHECK(ns);
DCHECK(exe);
auto& process = processes_[pid];
process.ns = ns;
process.exe = exe;
process.cmd = cmd;
}
private:
struct Process {
const char *ns;
const char *exe;
const char *cmd;
};
bool GetArcPid(pid_t *pid) const override {
if (arc_pid_ == 0)
return false;
*pid = arc_pid_;
return true;
}
bool GetPidNamespace(pid_t pid, std::string *ns) const override {
const auto it = processes_.find(pid);
if (it == processes_.end())
return false;
ns->assign(it->second.ns);
return true;
}
bool GetExeBaseName(pid_t pid, std::string *exe) const override {
const auto it = processes_.find(pid);
if (it == processes_.end())
return false;
exe->assign(it->second.exe);
return true;
}
bool GetCommand(pid_t pid, std::string *command) const override {
const auto it = processes_.find(pid);
if (it == processes_.end())
return false;
const auto cmd = it->second.cmd;
if (!cmd)
return false;
command->assign(cmd);
return true;
}
pid_t arc_pid_ = 0;
std::unordered_map<pid_t, Process> processes_;
};
MockContext *context_; // Owned by collector.
private:
void SetUp() override {
context_ = new MockContext;
collector_.reset(new MockArcCollector(ArcCollector::ContextPtr(context_)));
Initialize();
}
};
class ArcContextTest : public Test {
protected:
pid_t pid_;
private:
void SetUp() override {
collector_.reset(new MockArcCollector);
Initialize();
pid_ = getpid();
}
};
TEST_F(ArcCollectorTest, IsArcProcess) {
EXPECT_FALSE(collector_->IsArcProcess(123));
EXPECT_TRUE(FindLog("Failed to get PID of ARC container"));
ClearLog();
context_->SetArcPid(100);
EXPECT_FALSE(collector_->IsArcProcess(123));
EXPECT_TRUE(FindLog("Failed to get PID namespace of ARC container"));
ClearLog();
context_->AddProcess(100, "arc", "init", "/sbin/init");
EXPECT_FALSE(collector_->IsArcProcess(123));
EXPECT_TRUE(FindLog("Failed to get PID namespace of process"));
ClearLog();
context_->AddProcess(50, "cros", "chrome", "/opt/google/chrome/chrome");
context_->AddProcess(123, "arc", "arc_service", "/sbin/arc_service");
EXPECT_TRUE(collector_->IsArcProcess(123));
EXPECT_TRUE(GetLog().empty());
EXPECT_FALSE(collector_->IsArcProcess(50));
EXPECT_TRUE(GetLog().empty());
}
TEST_F(ArcCollectorTest, GetExeBaseNameForUserCrash) {
context_->SetArcPid(100);
context_->AddProcess(100, "arc", "init", "/sbin/init");
context_->AddProcess(50, "cros", "chrome", "/opt/google/chrome/chrome");
std::string exe;
EXPECT_TRUE(collector_->GetExecutableBaseNameFromPid(50, &exe));
EXPECT_EQ("chrome", exe);
}
TEST_F(ArcCollectorTest, GetExeBaseNameForArcCrash) {
context_->SetArcPid(100);
context_->AddProcess(100, "arc", "init", "/sbin/init");
context_->AddProcess(123, "arc", "arc_service", "/sbin/arc_service");
context_->AddProcess(456, "arc", "app_process32", nullptr);
context_->AddProcess(789, "arc", "app_process32", "com.arc.app");
std::string exe;
EXPECT_TRUE(collector_->GetExecutableBaseNameFromPid(123, &exe));
EXPECT_EQ("arc_service", exe);
EXPECT_TRUE(collector_->GetExecutableBaseNameFromPid(456, &exe));
EXPECT_TRUE(FindLog("Failed to get package name"));
EXPECT_EQ("app_process32", exe);
EXPECT_TRUE(collector_->GetExecutableBaseNameFromPid(789, &exe));
EXPECT_EQ("com.arc.app", exe);
}
TEST_F(ArcCollectorTest, ShouldDump) {
context_->SetArcPid(100);
context_->AddProcess(50, "cros", "chrome", "/opt/google/chrome/chrome");
context_->AddProcess(100, "arc", "init", "/sbin/init");
context_->AddProcess(123, "arc", "arc_service", "/sbin/arc_service");
context_->AddProcess(789, "arc", "app_process32", "com.arc.app");
std::string reason;
EXPECT_FALSE(collector_->ShouldDump(50, ArcCollector::kSystemUser,
"chrome", &reason));
EXPECT_EQ("ARC: ignoring - crash origin is not ARC", reason);
EXPECT_TRUE(collector_->ShouldDump(123, ArcCollector::kSystemUser,
"arc_service", &reason));
EXPECT_EQ("ARC: handling", reason);
EXPECT_FALSE(collector_->ShouldDump(123, ArcCollector::kSystemUser + 1,
"com.arc.app", &reason));
EXPECT_EQ("ARC: ignoring - not a system process", reason);
}
TEST_F(ArcContextTest, GetArcPid) {
EXPECT_FALSE(ArcCollector::IsArcRunning());
pid_t pid;
EXPECT_FALSE(collector_->context().GetArcPid(&pid));
}
TEST_F(ArcContextTest, GetPidNamespace) {
std::string ns;
EXPECT_TRUE(collector_->context().GetPidNamespace(pid_, &ns));
EXPECT_THAT(ns, testing::MatchesRegex("^pid:\\[[0-9]+\\]$"));
}
TEST_F(ArcContextTest, GetExeBaseName) {
std::string exe;
EXPECT_TRUE(collector_->context().GetExeBaseName(pid_, &exe));
EXPECT_EQ("crash_reporter_test", exe);
}
// TODO(crbug.com/590044)
TEST_F(ArcContextTest, DISABLED_GetCommand) {
std::string command;
EXPECT_TRUE(collector_->context().GetCommand(pid_, &command));
// TODO(domlaskowski): QEMU mishandles emulation of /proc/self/cmdline,
// prepending QEMU flags to the command line of the emulated program.
// Keep in sync with qargv[1] in qemu-binfmt-wrapper.c for now.
EXPECT_EQ("-0", command);
}