blob: 56e78fe7e1dac09eb20d998417c98d4a94ebd543 [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 <lib/async-loop/cpp/loop.h>
#include <lib/fdio/io.h>
#include <lib/zx/handle.h>
#include <lib/zx/log.h>
#include <lib/zx/time.h>
#include <stdio.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/log.h>
#include <utility>
#include <fuchsia/crash/cpp/fidl.h>
#include "lib/app/cpp/startup_context.h"
#include "third_party/crashpad/client/settings.h"
#include "third_party/crashpad/handler/fuchsia/crash_report_exception_handler.h"
#include "third_party/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.h"
namespace {
class ScopedStoppable {
public:
ScopedStoppable() = default;
~ScopedStoppable() {
if (stoppable_) {
stoppable_->Stop();
}
}
void Reset(crashpad::Stoppable* stoppable) { stoppable_.reset(stoppable); }
crashpad::Stoppable* Get() { return stoppable_.get(); }
private:
std::unique_ptr<crashpad::Stoppable> stoppable_;
DISALLOW_COPY_AND_ASSIGN(ScopedStoppable);
};
class ScopedUnlink {
public:
ScopedUnlink(const std::string& filename) : filename_(filename) {}
~ScopedUnlink() { unlink(filename_.c_str()); }
bool is_valid() const { return !filename_.empty(); }
const std::string& get() const { return filename_; }
private:
std::string filename_;
DISALLOW_COPY_AND_ASSIGN(ScopedUnlink);
};
std::string GetSystemLogToFile() {
char filename[] = "/data/crashes/log.XXXXXX";
base::ScopedFD fd(mkstemp(filename));
if (fd.get() < 0) {
printf("crashpad_analyzer: could not create temp file\n");
return std::string();
}
zx::log log;
zx_status_t status = zx::log::create(ZX_LOG_FLAG_READABLE, &log);
if (status != ZX_OK) {
printf("zx::log::create failed %d\n", status);
return std::string();
}
std::vector<std::string> result;
char buf[ZX_LOG_RECORD_MAX + 1];
zx_log_record_t* rec = (zx_log_record_t*)buf;
for (;;) {
if (log.read(ZX_LOG_RECORD_MAX, rec, 0) > 0) {
if (rec->datalen && (rec->data[rec->datalen - 1] == '\n')) {
rec->datalen--;
}
rec->data[rec->datalen] = 0;
dprintf(fd.get(), "[%05d.%03d] %05" PRIu64 ".%05" PRIu64 "> %s\n",
(int)(rec->timestamp / 1000000000ULL),
(int)((rec->timestamp / 1000000ULL) % 1000ULL), rec->pid,
rec->tid, rec->data);
} else {
return std::string(filename);
}
}
}
} // namespace
int HandleException(zx::process process, zx::thread thread) {
// On Fuchsia, the crash reporter does not stay resident, so we don't run
// crashpad_handler here. Instead, directly use CrashReportExceptionHandler
// and terminate when it has completed.
std::unique_ptr<crashpad::CrashReportDatabase> database(
crashpad::CrashReportDatabase::Initialize(
base::FilePath("/data/crashes")));
if (!database) {
return EXIT_FAILURE;
}
printf("WARNING: In test configuration, opting in to crash report upload.\n");
database->GetSettings()->SetUploadsEnabled(true);
ScopedStoppable upload_thread;
crashpad::CrashReportUploadThread::Options upload_thread_options;
upload_thread_options.identify_client_via_url = true;
upload_thread_options.rate_limit = false;
upload_thread_options.upload_gzip = true;
upload_thread_options.watch_pending_reports = true;
upload_thread.Reset(new crashpad::CrashReportUploadThread(
database.get(), "https://clients2.google.com/cr/report",
upload_thread_options));
upload_thread.Get()->Start();
std::map<std::string, std::string> annotations;
annotations["product"] = "Fuchsia";
annotations["version"] = "unknown";
char version[64] = {};
if (zx_system_get_version(version, sizeof(version)) == ZX_OK) {
annotations["version"] = version;
}
std::map<std::string, base::FilePath> attachments;
ScopedUnlink temp_log_file(GetSystemLogToFile());
if (temp_log_file.is_valid()) {
attachments["log"] = base::FilePath(temp_log_file.get());
}
crashpad::CrashReportExceptionHandler exception_handler(
database.get(),
static_cast<crashpad::CrashReportUploadThread*>(upload_thread.Get()),
&annotations, &attachments, nullptr);
return exception_handler.HandleExceptionHandles(process.get(), thread.get())
? EXIT_SUCCESS
: EXIT_FAILURE;
}
class AnalyzerImpl : public fuchsia::crash::Analyzer {
public:
// fuchsia::crash::Analyzer:
void Analyze(::zx::process process, ::zx::thread thread,
AnalyzeCallback callback) override {
callback();
HandleException(std::move(process), std::move(thread));
}
};
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigMakeDefault);
std::unique_ptr<fuchsia::sys::StartupContext> app_context(
fuchsia::sys::StartupContext::CreateFromStartupInfo());
AnalyzerImpl analyzer;
fidl::BindingSet<fuchsia::crash::Analyzer> bindings;
app_context->outgoing().AddPublicService<fuchsia::crash::Analyzer>(
[&analyzer,
&bindings](fidl::InterfaceRequest<fuchsia::crash::Analyzer> request) {
bindings.AddBinding(&analyzer, std::move(request));
});
loop.Run();
return 0;
}