blob: 5fbf44fb63c325aca23fa94061d750f7c397ec70 [file] [log] [blame]
// 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 <utility>
#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using brillo::ClearLog;
using brillo::FindLog;
using brillo::GetLog;
namespace {
const char k32BitAuxv[] = R"(
20 00 00 00 20 ba 7a ef 21 00 00 00 00 b0 7a ef
10 00 00 00 ff fb eb bf 06 00 00 00 00 10 00 00
11 00 00 00 64 00 00 00 03 00 00 00 34 d0 bb 5e
04 00 00 00 20 00 00 00 05 00 00 00 09 00 00 00
07 00 00 00 00 d0 7a ef 08 00 00 00 00 00 00 00
09 00 00 00 4d e6 bb 5e 0b 00 00 00 00 00 00 00
0c 00 00 00 00 00 00 00 0d 00 00 00 00 00 00 00
0e 00 00 00 00 00 00 00 17 00 00 00 01 00 00 00
19 00 00 00 3b 52 c6 ff 1f 00 00 00 de 6f c6 ff
0f 00 00 00 4b 52 c6 ff 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
)";
const char k64BitAuxv[] = R"(
21 00 00 00 00 00 00 00 00 30 db e6 fe 7f 00 00
10 00 00 00 00 00 00 00 ff fb eb bf 00 00 00 00
06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
11 00 00 00 00 00 00 00 64 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00 40 c0 a6 54 a5 5d 00 00
04 00 00 00 00 00 00 00 38 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00
07 00 00 00 00 00 00 00 00 10 3c 97 9c 7a 00 00
08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
09 00 00 00 00 00 00 00 c8 de a6 54 a5 5d 00 00
0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
17 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
19 00 00 00 00 00 00 00 39 bc da e6 fe 7f 00 00
1f 00 00 00 00 00 00 00 de cf da e6 fe 7f 00 00
0f 00 00 00 00 00 00 00 49 bc da e6 fe 7f 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
)";
} // namespace
class MockArcCollector : public ArcCollector {
public:
using ArcCollector::ArcCollector;
MOCK_METHOD(void, SetUpDBus, (), (override));
};
class Test : public ::testing::Test {
protected:
void Initialize() {
EXPECT_CALL(*collector_, SetUpDBus()).WillRepeatedly(testing::Return());
collector_->Initialize(IsFeedbackAllowed, false, false);
ClearLog();
}
std::unique_ptr<MockArcCollector> collector_;
private:
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,
const char* auxv) {
DCHECK_EQ(processes_.count(pid), 0u);
DCHECK(ns);
DCHECK(exe);
auto& process = processes_[pid];
process.ns = ns;
process.exe = exe;
process.cmd = cmd;
process.auxv = auxv;
}
private:
struct Process {
const char* ns;
const char* exe;
const char* cmd;
const char* auxv;
};
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;
}
bool ReadAuxvForProcess(pid_t pid, std::string* contents) const override {
const auto it = processes_.find(pid);
if (it == processes_.end())
return false;
const auto* auxv = it->second.auxv;
if (!auxv)
return false;
std::istringstream ss(auxv);
contents->clear();
uint32_t byte;
ss >> std::hex;
while (ss >> byte) {
contents->push_back(byte);
}
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", k32BitAuxv);
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",
k32BitAuxv);
context_->AddProcess(123, "arc", "arc_service", "/sbin/arc_service",
k32BitAuxv);
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", k32BitAuxv);
context_->AddProcess(50, "cros", "chrome", "/opt/google/chrome/chrome",
k32BitAuxv);
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", k32BitAuxv);
context_->AddProcess(123, "arc", "arc_service", "/sbin/arc_service",
k32BitAuxv);
context_->AddProcess(456, "arc", "app_process32", nullptr, k32BitAuxv);
context_->AddProcess(789, "arc", "app_process32", "com.arc.app", k32BitAuxv);
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",
k32BitAuxv);
context_->AddProcess(100, "arc", "init", "/sbin/init", k32BitAuxv);
context_->AddProcess(123, "arc", "arc_service", "/sbin/arc_service",
k32BitAuxv);
context_->AddProcess(789, "arc", "app_process32", "com.arc.app", k32BitAuxv);
std::string reason;
EXPECT_FALSE(collector_->ShouldDump(50, 1234, "chrome", &reason));
EXPECT_EQ("ignoring - crash origin is not ARC", reason);
EXPECT_TRUE(collector_->ShouldDump(123, 0, "arc_service", &reason));
EXPECT_EQ("handling", reason);
EXPECT_FALSE(collector_->ShouldDump(123, ArcCollector::kSystemUserEnd,
"com.arc.app", &reason));
EXPECT_EQ("ignoring - not a system process", reason);
}
TEST_F(ArcCollectorTest, CorrectlyDetectBitness) {
bool is_64_bit;
context_->AddProcess(100, "arc", "app_process64", "zygote64", k64BitAuxv);
EXPECT_EQ(ArcCollector::kErrorNone,
collector_->Is64BitProcess(100, &is_64_bit));
EXPECT_TRUE(is_64_bit);
context_->AddProcess(101, "arc", "app_process32", "zygote32", k32BitAuxv);
EXPECT_EQ(ArcCollector::kErrorNone,
collector_->Is64BitProcess(101, &is_64_bit));
EXPECT_FALSE(is_64_bit);
}
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);
}