Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b889d7e4 authored by Gaurav Shah's avatar Gaurav Shah Committed by Gerrit Code Review
Browse files

Merge "crash_reporter: Remove Chrome collector, CrOS build files, DBus"

parents f06041d7 c8b74142
Loading
Loading
Loading
Loading
+0 −335
Original line number Original line Diff line number Diff line
// Copyright (c) 2013 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 "chrome_collector.h"

#include <pcrecpp.h>
#include <stdint.h>

#include <map>
#include <string>
#include <vector>

#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <chromeos/data_encoding.h>
#include <chromeos/dbus/service_constants.h>
#include <chromeos/process.h>
#include <chromeos/syslog_logging.h>

using base::FilePath;
using base::StringPrintf;

namespace {

const char kDefaultMinidumpName[] = "upload_file_minidump";

// Path to the gzip binary.
const char kGzipPath[] = "/bin/gzip";

// Filenames for logs attached to crash reports. Also used as metadata keys.
const char kChromeLogFilename[] = "chrome.txt";
const char kGpuStateFilename[] = "i915_error_state.log.xz";

// From //net/crash/collector/collector.h
const int kDefaultMaxUploadBytes = 1024 * 1024;

// Extract a string delimited by the given character, from the given offset
// into a source string. Returns false if the string is zero-sized or no
// delimiter was found.
bool GetDelimitedString(const std::string &str, char ch, size_t offset,
                        std::string *substr) {
  size_t at = str.find_first_of(ch, offset);
  if (at == std::string::npos || at == offset)
    return false;
  *substr = str.substr(offset, at - offset);
  return true;
}

// Gets the GPU's error state from debugd and writes it to |error_state_path|.
// Returns true on success.
bool GetDriErrorState(const FilePath &error_state_path,
                      org::chromium::debugdProxy *proxy) {
  chromeos::ErrorPtr error;
  std::string error_state_str;

  proxy->GetLog("i915_error_state", &error_state_str, &error);

  if (error) {
    LOG(ERROR) << "Error calling D-Bus proxy call to interface "
               << "'" << proxy->GetObjectPath().value() << "':"
               << error->GetMessage();
    return false;
  }

  if (error_state_str == "<empty>")
    return false;

  const char kBase64Header[] = "<base64>: ";
  const size_t kBase64HeaderLength = sizeof(kBase64Header) - 1;
  if (error_state_str.compare(0, kBase64HeaderLength, kBase64Header)) {
    LOG(ERROR) << "i915_error_state is missing base64 header";
    return false;
  }

  std::string decoded_error_state;

  if (!chromeos::data_encoding::Base64Decode(
      error_state_str.c_str() + kBase64HeaderLength,
      &decoded_error_state)) {
    LOG(ERROR) << "Could not decode i915_error_state";
    return false;
  }

  int written = base::WriteFile(error_state_path,
                                decoded_error_state.c_str(),
                                decoded_error_state.length());
  if (written < 0 ||
      static_cast<size_t>(written) != decoded_error_state.length()) {
    LOG(ERROR) << "Could not write file " << error_state_path.value()
               << " Written: " << written << " Len: "
               << decoded_error_state.length();
    base::DeleteFile(error_state_path, false);
    return false;
  }

  return true;
}

// Gzip-compresses |path|, removes the original file, and returns the path of
// the new file. On failure, the original file is left alone and an empty path
// is returned.
FilePath GzipFile(const FilePath& path) {
  chromeos::ProcessImpl proc;
  proc.AddArg(kGzipPath);
  proc.AddArg(path.value());
  const int res = proc.Run();
  if (res != 0) {
    LOG(ERROR) << "Failed to gzip " << path.value();
    return FilePath();
  }
  return path.AddExtension(".gz");
}

}  // namespace


ChromeCollector::ChromeCollector() : output_file_ptr_(stdout) {}

ChromeCollector::~ChromeCollector() {}

bool ChromeCollector::HandleCrash(const FilePath &file_path,
                                  const std::string &pid_string,
                                  const std::string &uid_string,
                                  const std::string &exe_name) {
  if (!is_feedback_allowed_function_())
    return true;

  LOG(WARNING) << "Received crash notification for " << exe_name << "["
               << pid_string << "] user " << uid_string << " (called directly)";

  if (exe_name.find('/') != std::string::npos) {
    LOG(ERROR) << "exe_name contains illegal characters: " << exe_name;
    return false;
  }

  FilePath dir;
  uid_t uid = atoi(uid_string.c_str());
  pid_t pid = atoi(pid_string.c_str());
  if (!GetCreatedCrashDirectoryByEuid(uid, &dir, nullptr)) {
    LOG(ERROR) << "Can't create crash directory for uid " << uid;
    return false;
  }

  std::string dump_basename = FormatDumpBasename(exe_name, time(nullptr), pid);
  FilePath meta_path = GetCrashPath(dir, dump_basename, "meta");
  FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp");

  std::string data;
  if (!base::ReadFileToString(file_path, &data)) {
    LOG(ERROR) << "Can't read crash log: " << file_path.value();
    return false;
  }

  if (!ParseCrashLog(data, dir, minidump_path, dump_basename)) {
    LOG(ERROR) << "Failed to parse Chrome's crash log";
    return false;
  }


  int64_t report_size = 0;
  base::GetFileSize(minidump_path, &report_size);

  // Keyed by crash metadata key name.
  const std::map<std::string, base::FilePath> additional_logs =
      GetAdditionalLogs(dir, dump_basename, exe_name);
  for (auto it : additional_logs) {
    int64_t file_size = 0;
    if (!base::GetFileSize(it.second, &file_size)) {
      PLOG(WARNING) << "Unable to get size of " << it.second.value();
      continue;
    }
    if (report_size + file_size > kDefaultMaxUploadBytes) {
      LOG(INFO) << "Skipping upload of " << it.second.value() << "("
                << file_size << "B) because report size would exceed limit ("
                << kDefaultMaxUploadBytes << "B)";
      continue;
    }
    VLOG(1) << "Adding metadata: " << it.first << " -> " << it.second.value();
    // Call AddCrashMetaUploadFile() rather than AddCrashMetaData() here. The
    // former adds a prefix to the key name; without the prefix, only the key
    // "logs" appears to be displayed on the crash server.
    AddCrashMetaUploadFile(it.first, it.second.value());
    report_size += file_size;
  }

  // We're done.
  WriteCrashMetaData(meta_path, exe_name, minidump_path.value());

  fprintf(output_file_ptr_, "%s", kSuccessMagic);
  fflush(output_file_ptr_);

  return true;
}

void ChromeCollector::SetUpDBus() {
  CrashCollector::SetUpDBus();

  debugd_proxy_.reset(
      new org::chromium::debugdProxy(bus_, debugd::kDebugdServiceName));
}

bool ChromeCollector::ParseCrashLog(const std::string &data,
                                    const FilePath &dir,
                                    const FilePath &minidump,
                                    const std::string &basename) {
  size_t at = 0;
  while (at < data.size()) {
    // Look for a : followed by a decimal number, followed by another :
    // followed by N bytes of data.
    std::string name, size_string;
    if (!GetDelimitedString(data, ':', at, &name)) {
      LOG(ERROR) << "Can't find : after name @ offset " << at;
      break;
    }
    at += name.size() + 1;  // Skip the name & : delimiter.

    if (!GetDelimitedString(data, ':', at, &size_string)) {
      LOG(ERROR) << "Can't find : after size @ offset " << at;
      break;
    }
    at += size_string.size() + 1;  // Skip the size & : delimiter.

    size_t size;
    if (!base::StringToSizeT(size_string, &size)) {
      LOG(ERROR) << "String not convertible to integer: " << size_string;
      break;
    }

    // Data would run past the end, did we get a truncated file?
    if (at + size > data.size()) {
      LOG(ERROR) << "Overrun, expected " << size << " bytes of data, got "
        << (data.size() - at);
      break;
    }

    if (name.find("filename") != std::string::npos) {
      // File.
      // Name will be in a semi-MIME format of
      // <descriptive name>"; filename="<name>"
      // Descriptive name will be upload_file_minidump for the dump.
      std::string desc, filename;
      pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\"");
      if (!re.FullMatch(name.c_str(), &desc, &filename)) {
        LOG(ERROR) << "Filename was not in expected format: " << name;
        break;
      }

      if (desc.compare(kDefaultMinidumpName) == 0) {
        // The minidump.
        WriteNewFile(minidump, data.c_str() + at, size);
      } else {
        // Some other file.
        FilePath path = GetCrashPath(dir, basename + "-" + filename, "other");
        if (WriteNewFile(path, data.c_str() + at, size) >= 0) {
          AddCrashMetaUploadFile(desc, path.value());
        }
      }
    } else {
      // Other attribute.
      std::string value_str;
      value_str.reserve(size);

      // Since metadata is one line/value the values must be escaped properly.
      for (size_t i = at; i < at + size; i++) {
        switch (data[i]) {
          case '"':
          case '\\':
            value_str.push_back('\\');
            value_str.push_back(data[i]);
            break;

          case '\r':
            value_str += "\\r";
            break;

          case '\n':
            value_str += "\\n";
           break;

          case '\t':
            value_str += "\\t";
           break;

          case '\0':
            value_str += "\\0";
           break;

          default:
           value_str.push_back(data[i]);
           break;
        }
      }
      AddCrashMetaUploadData(name, value_str);
    }

    at += size;
  }

  return at == data.size();
}

std::map<std::string, base::FilePath> ChromeCollector::GetAdditionalLogs(
    const FilePath &dir,
    const std::string &basename,
    const std::string &exe_name) {
  std::map<std::string, base::FilePath> logs;

  // Run the command specified by the config file to gather logs.
  const FilePath chrome_log_path =
      GetCrashPath(dir, basename, kChromeLogFilename);
  if (GetLogContents(log_config_path_, exe_name, chrome_log_path)) {
    const FilePath compressed_path = GzipFile(chrome_log_path);
    if (!compressed_path.empty())
      logs[kChromeLogFilename] = compressed_path;
    else
      base::DeleteFile(chrome_log_path, false /* recursive */);
  }

  // For unit testing, debugd_proxy_ isn't initialized, so skip attempting to
  // get the GPU error state from debugd.
  if (debugd_proxy_) {
    const FilePath dri_error_state_path =
        GetCrashPath(dir, basename, kGpuStateFilename);
    if (GetDriErrorState(dri_error_state_path, debugd_proxy_.get()))
      logs[kGpuStateFilename] = dri_error_state_path;
  }

  return logs;
}

// static
const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished";

crash_reporter/chrome_collector.h

deleted100644 → 0
+0 −72
Original line number Original line Diff line number Diff line
// Copyright (c) 2013 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.

#ifndef CRASH_REPORTER_CHROME_COLLECTOR_H_
#define CRASH_REPORTER_CHROME_COLLECTOR_H_

#include <map>
#include <string>

#include <base/files/file_path.h>
#include <base/macros.h>
#include <gtest/gtest_prod.h>  // for FRIEND_TEST

#include "crash_collector.h"
#include "debugd/dbus-proxies.h"

class SystemLogging;

// Chrome crash collector.
class ChromeCollector : public CrashCollector {
 public:
  ChromeCollector();
  ~ChromeCollector() override;

  // Magic string to let Chrome know the crash report succeeded.
  static const char kSuccessMagic[];

  // Handle a specific chrome crash.  Returns true on success.
  bool HandleCrash(const base::FilePath &file_path,
                   const std::string &pid_string,
                   const std::string &uid_string,
                   const std::string &exe_name);

 protected:
  void SetUpDBus() override;

 private:
  friend class ChromeCollectorTest;
  FRIEND_TEST(ChromeCollectorTest, GoodValues);
  FRIEND_TEST(ChromeCollectorTest, BadValues);
  FRIEND_TEST(ChromeCollectorTest, Newlines);
  FRIEND_TEST(ChromeCollectorTest, File);
  FRIEND_TEST(ChromeCollectorTest, HandleCrash);

  // Crashes are expected to be in a TLV-style format of:
  // <name>:<length>:<value>
  // Length is encoded as a decimal number. It can be zero, but must consist of
  // at least one character
  // For file values, name actually contains both a description and a filename,
  // in a fixed format of: <description>"; filename="<filename>"
  bool ParseCrashLog(const std::string &data, const base::FilePath &dir,
                     const base::FilePath &minidump,
                     const std::string &basename);

  // Writes additional logs for |exe_name| to files based on |basename| within
  // |dir|. Crash report metadata key names and the corresponding file paths are
  // returned.
  std::map<std::string, base::FilePath> GetAdditionalLogs(
      const base::FilePath &dir,
      const std::string &basename,
      const std::string &exe_name);

  FILE *output_file_ptr_;

  // D-Bus proxy for debugd interface.  Unset in unit tests.
  std::unique_ptr<org::chromium::debugdProxy> debugd_proxy_;

  DISALLOW_COPY_AND_ASSIGN(ChromeCollector);
};

#endif  // CRASH_REPORTER_CHROME_COLLECTOR_H_
+0 −150
Original line number Original line Diff line number Diff line
// Copyright (c) 2013 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 "chrome_collector.h"

#include <stdio.h>

#include <base/auto_reset.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <chromeos/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

using base::FilePath;

namespace {

const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345";
const char kCrashFormatEmbeddedNewline[] =
    "value1:10:abcd\r\nghijvalue2:5:12\n34";
const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345";
const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345";
const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345";
const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345";

const char kCrashFormatWithFile[] =
    "value1:10:abcdefghijvalue2:5:12345"
    "some_file\"; filename=\"foo.txt\":15:12345\n789\n12345"
    "value3:2:ok";

void CountCrash() {
}

bool s_allow_crash = false;

bool IsMetrics() {
  return s_allow_crash;
}

}  // namespace

class ChromeCollectorMock : public ChromeCollector {
 public:
  MOCK_METHOD0(SetUpDBus, void());
};

class ChromeCollectorTest : public ::testing::Test {
 protected:
  void ExpectFileEquals(const char *golden,
                        const FilePath &file_path) {
    std::string contents;
    EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
    EXPECT_EQ(golden, contents);
  }

  ChromeCollectorMock collector_;

 private:
  void SetUp() override {
    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());

    collector_.Initialize(CountCrash, IsMetrics);
    chromeos::ClearLog();
  }
};

TEST_F(ChromeCollectorTest, GoodValues) {
  FilePath dir(".");
  EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatGood,
                                       dir, dir.Append("minidump.dmp"),
                                       "base"));

  // Check to see if the values made it in properly.
  std::string meta = collector_.extra_metadata_;
  EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
  EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
}

TEST_F(ChromeCollectorTest, Newlines) {
  FilePath dir(".");
  EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatEmbeddedNewline,
                                       dir, dir.Append("minidump.dmp"),
                                       "base"));

  // Check to see if the values were escaped.
  std::string meta = collector_.extra_metadata_;
  EXPECT_TRUE(meta.find("value1=abcd\\r\\nghij") != std::string::npos);
  EXPECT_TRUE(meta.find("value2=12\\n34") != std::string::npos);
}

TEST_F(ChromeCollectorTest, BadValues) {
  FilePath dir(".");
  const struct {
    const char *data;
  } list[] = {
    {kCrashFormatBad1, },
    {kCrashFormatBad2, },
    {kCrashFormatBad3, },
    {kCrashFormatBad4, },
  };

  for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++) {
    chromeos::ClearLog();
    EXPECT_FALSE(collector_.ParseCrashLog(list[i].data,
                                          dir, dir.Append("minidump.dmp"),
                                          "base"));
  }
}

TEST_F(ChromeCollectorTest, File) {
  base::ScopedTempDir scoped_temp_dir;
  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
  const FilePath& dir = scoped_temp_dir.path();
  EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile,
                                       dir, dir.Append("minidump.dmp"),
                                       "base"));

  // Check to see if the values are still correct and that the file was
  // written with the right data.
  std::string meta = collector_.extra_metadata_;
  EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos);
  EXPECT_TRUE(meta.find("value2=12345") != std::string::npos);
  EXPECT_TRUE(meta.find("value3=ok") != std::string::npos);
  ExpectFileEquals("12345\n789\n12345", dir.Append("base-foo.txt.other"));
}

TEST_F(ChromeCollectorTest, HandleCrash) {
  base::AutoReset<bool> auto_reset(&s_allow_crash, true);
  base::ScopedTempDir scoped_temp_dir;
  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
  const FilePath& dir = scoped_temp_dir.path();
  FilePath dump_file = dir.Append("test.dmp");
  ASSERT_EQ(strlen(kCrashFormatWithFile),
            base::WriteFile(dump_file, kCrashFormatWithFile,
                            strlen(kCrashFormatWithFile)));
  collector_.ForceCrashDirectory(dir);

  FilePath log_file;
  {
    base::ScopedFILE output(
        base::CreateAndOpenTemporaryFileInDir(dir, &log_file));
    ASSERT_TRUE(output.get());
    base::AutoReset<FILE*> auto_reset_file_ptr(&collector_.output_file_ptr_,
                                               output.get());
    EXPECT_TRUE(collector_.HandleCrash(dump_file, "123", "456", "chrome_test"));
  }
  ExpectFileEquals(ChromeCollector::kSuccessMagic, log_file);
}

crash_reporter/crash-reporter.gyp

deleted100644 → 0
+0 −147
Original line number Original line Diff line number Diff line
{
  # Shouldn't need this, but doesn't work otherwise.
  # http://crbug.com/340086 and http://crbug.com/385186
  # Note: the unused dependencies are optimized out by the compiler.
  'target_defaults': {
    'variables': {
      'deps': [
        'libchromeos-<(libbase_ver)',
      ],
    },
  },
  'targets': [
    {
      'target_name': 'libcrash',
      'type': 'static_library',
      'variables': {
        'exported_deps': [
          'libchrome-<(libbase_ver)',
          'libpcrecpp',
        ],
        'deps': ['<@(exported_deps)'],
      },
      'all_dependent_settings': {
        'variables': {
          'deps': [
            '<@(exported_deps)',
          ],
        },
      },
      'sources': [
        'chrome_collector.cc',
        'crash_collector.cc',
        'kernel_collector.cc',
        'kernel_warning_collector.cc',
        'udev_collector.cc',
        'unclean_shutdown_collector.cc',
        'user_collector.cc',
      ],
      'actions': [
        {
          'action_name': 'generate-session-manager-proxies',
          'variables': {
            'proxy_output_file': 'include/session_manager/dbus-proxies.h'
          },
          'sources': [
            '../login_manager/org.chromium.SessionManagerInterface.xml',
          ],
          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
        },
        {
          'action_name': 'generate-debugd-proxies',
          'variables': {
            'proxy_output_file': 'include/debugd/dbus-proxies.h'
          },
          'sources': [
            '../debugd/share/org.chromium.debugd.xml',
          ],
          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
        },
      ],
    },
    {
      'target_name': 'crash_reporter',
      'type': 'executable',
      'variables': {
        'deps': [
          'dbus-1',
          'libmetrics-<(libbase_ver)',
        ],
      },
      'dependencies': [
        'libcrash',
      ],
      'sources': [
        'crash_reporter.cc',
      ],
    },
    {
      'target_name': 'list_proxies',
      'type': 'executable',
      'variables': {
        'deps': [
          'dbus-1',
          'libchrome-<(libbase_ver)',
        ],
      },
      'sources': [
        'list_proxies.cc',
      ],
      'actions': [
        {
          'action_name': 'generate-lib-cros-service-proxies',
          'variables': {
            'proxy_output_file': 'include/libcrosservice/dbus-proxies.h'
          },
          'sources': [
            './dbus_bindings/org.chromium.LibCrosService.xml',
          ],
          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
        },
      ],
    },
    {
      'target_name': 'warn_collector',
      'type': 'executable',
      'variables': {
        'lexer_out_dir': 'crash-reporter',
        'deps': [
          'libmetrics-<(libbase_ver)',
        ],
      },
      'link_settings': {
        'libraries': [
          '-lfl',
        ],
      },
      'sources': [
        'warn_collector.l',
      ],
      'includes': ['../common-mk/lex.gypi'],
    },
  ],
  'conditions': [
    ['USE_test == 1', {
      'targets': [
        {
          'target_name': 'crash_reporter_test',
          'type': 'executable',
          'includes': ['../common-mk/common_test.gypi'],
          'dependencies': ['libcrash'],
          'sources': [
            'chrome_collector_test.cc',
            'crash_collector_test.cc',
            'crash_collector_test.h',
            'crash_reporter_logs_test.cc',
            'kernel_collector_test.cc',
            'kernel_collector_test.h',
            'testrunner.cc',
            'udev_collector_test.cc',
            'unclean_shutdown_collector_test.cc',
            'user_collector_test.cc',
          ],
        },
      ],
    }],
  ],
}
+2 −78

File changed.

Preview size limit exceeded, changes collapsed.

Loading