forked from mirrors/gecko-dev
Bug 1847098 - Report library version from filename at crash r=gsvelto
Differential Revision: https://phabricator.services.mozilla.com/D202249
This commit is contained in:
parent
9d1edf90f0
commit
636e377cbf
13 changed files with 325 additions and 8 deletions
|
|
@ -466,8 +466,8 @@ class MicrodumpWriter {
|
|||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
dumper_->GetMappingEffectiveNameAndPath(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||
dumper_->GetMappingEffectiveNamePathAndVersion(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name), nullptr);
|
||||
|
||||
LogAppend("M ");
|
||||
LogAppend(static_cast<uintptr_t>(mapping.start_addr));
|
||||
|
|
|
|||
|
|
@ -468,11 +468,12 @@ bool ElfFileSoName(const LinuxDumper& dumper,
|
|||
} // namespace
|
||||
|
||||
|
||||
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
void LinuxDumper::GetMappingEffectiveNamePathAndVersion(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size) {
|
||||
size_t file_name_size,
|
||||
VersionComponents* version) {
|
||||
my_strlcpy(file_path, mapping.name, file_path_size);
|
||||
|
||||
// Tools such as minidump_stackwalk use the name of the module to look up
|
||||
|
|
@ -487,6 +488,9 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
|||
const char* basename = my_strrchr(file_path, '/');
|
||||
basename = basename == NULL ? file_path : (basename + 1);
|
||||
my_strlcpy(file_name, basename, file_name_size);
|
||||
if (version) {
|
||||
ElfFileSoVersion(mapping.name, version);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -512,6 +516,10 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
|||
my_strlcpy(file_path, file_name, file_path_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (version) {
|
||||
ElfFileSoVersion(mapping.name, version);
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxDumper::ReadAuxv() {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@
|
|||
#include "common/memory_allocator.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
# include "linux_utils.h"
|
||||
#endif // defined(XP_LINUX)
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
|
|
@ -213,11 +217,12 @@ class LinuxDumper {
|
|||
// other cases, however, a library can be mapped from an archive (e.g., when
|
||||
// loading .so libs from an apk on Android) and this method is able to
|
||||
// reconstruct the original file name.
|
||||
void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
void GetMappingEffectiveNamePathAndVersion(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size);
|
||||
size_t file_name_size,
|
||||
VersionComponents* version);
|
||||
|
||||
protected:
|
||||
bool ReadAuxv();
|
||||
|
|
|
|||
|
|
@ -717,8 +717,16 @@ class MinidumpWriter {
|
|||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
dumper_->GetMappingEffectiveNameAndPath(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||
VersionComponents version;
|
||||
dumper_->GetMappingEffectiveNamePathAndVersion(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name), &version);
|
||||
|
||||
mod->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
|
||||
mod->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
|
||||
mod->version_info.file_version_hi = version.size() >= 1 ? version[0] : 0;
|
||||
mod->version_info.file_version_lo = version.size() >= 2 ? version[1] : 0;
|
||||
mod->version_info.product_version_hi = version.size() >= 3 ? version[2] : 0;
|
||||
mod->version_info.product_version_lo = version.size() == 4 ? version[3] : 0;
|
||||
|
||||
MDLocationDescriptor ld;
|
||||
if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld))
|
||||
|
|
|
|||
62
toolkit/crashreporter/linux_utils.cc
Normal file
62
toolkit/crashreporter/linux_utils.cc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linux_utils.h"
|
||||
|
||||
bool ElfFileSoVersion(const char* mapping_name,
|
||||
VersionComponents* version_components) {
|
||||
if (!version_components) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = std::string(mapping_name);
|
||||
std::string filename = path.substr(path.find_last_of('/') + 1);
|
||||
|
||||
std::string dot_so_dot(".so.");
|
||||
// We found no version so just report 0
|
||||
size_t has_dot_so_dot = filename.find(dot_so_dot);
|
||||
if (has_dot_so_dot == std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string so_version =
|
||||
filename.substr(has_dot_so_dot + dot_so_dot.length());
|
||||
std::string tmp;
|
||||
for (std::string::iterator it = so_version.begin(); it != so_version.end();
|
||||
++it) {
|
||||
// We can't have more than four: MAJOR.minor.release.patch
|
||||
if (version_components->size() == 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
char c = *it;
|
||||
if (c != '.') {
|
||||
if (isdigit(c)) {
|
||||
tmp += c;
|
||||
}
|
||||
|
||||
if (std::next(it) != so_version.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp.length() > 0) {
|
||||
int t = std::stoi(tmp); // assume tmp is < UINT32T_MAX
|
||||
if (t < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t casted_tmp = static_cast<uint32_t>(t);
|
||||
// We have lost some information we should warn.
|
||||
if ((unsigned int)t != casted_tmp) {
|
||||
return false;
|
||||
}
|
||||
version_components->push_back(casted_tmp);
|
||||
}
|
||||
tmp = "";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
18
toolkit/crashreporter/linux_utils.h
Normal file
18
toolkit/crashreporter/linux_utils.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef toolkit_breakpad_linux_utils_h__
|
||||
#define toolkit_breakpad_linux_utils_h__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using VersionComponents = std::vector<uint32_t>;
|
||||
|
||||
bool ElfFileSoVersion(const char* mapping_name, VersionComponents* version);
|
||||
|
||||
#endif /* toolkit_breakpad_linux_utils_h__ */
|
||||
|
|
@ -55,6 +55,14 @@ if CONFIG["MOZ_CRASHREPORTER"]:
|
|||
"google-breakpad/src/processor",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"linux_utils.cc",
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
"linux_utils.h",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_OXIDIZED_BREAKPAD"]:
|
||||
DIRS += ["rust_minidump_writer_linux"]
|
||||
|
||||
|
|
|
|||
146
toolkit/crashreporter/test/gtest/TestElfSoVersion.cpp
Normal file
146
toolkit/crashreporter/test/gtest/TestElfSoVersion.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mozilla/SpinEventLoopUntil.h"
|
||||
|
||||
#include "linux_utils.h"
|
||||
|
||||
#define ASSERT_EQ_UNSIGNED(v, e) ASSERT_EQ((v), (uint32_t)(e))
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class CrashReporter : public ::testing::Test {};
|
||||
|
||||
TEST_F(CrashReporter, ElfSoNoVersion) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libdbus1.so", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSo6) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libm.so.6", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 1);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 6);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoNormalShort) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libdbus1.so.1.2", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 2);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 1);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 2);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoNormalComplete) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libdbus1.so.1.2.3", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 3);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 1);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 2);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 3);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoNormalPrerelease) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libdbus1.so.1.2.3.98", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 4);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 1);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 2);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 3);
|
||||
ASSERT_EQ_UNSIGNED(version[3], 98);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoNormalPrereleaseToomuch) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libdbus1.so.1.2.3.98.9.2.3", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 4);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 1);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 2);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 3);
|
||||
ASSERT_EQ_UNSIGNED(version[3], 98);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoBig) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libatk-1.0.so.0.25009.1", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 3);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 0);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 25009);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 1);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoCairo) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libcairo.so.2.11800.3", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 3);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 2);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 11800);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 3);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoMax) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion(
|
||||
"libcairo.so.2147483647.2147483647.2147483647.2147483647", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 4);
|
||||
ASSERT_EQ_UNSIGNED(version[0], INT32_MAX);
|
||||
ASSERT_EQ_UNSIGNED(version[1], INT32_MAX);
|
||||
ASSERT_EQ_UNSIGNED(version[2], INT32_MAX);
|
||||
ASSERT_EQ_UNSIGNED(version[3], INT32_MAX);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoTimestamp) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libabsl_time_zone.so.20220623.0.0", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 3);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 20220623);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 0);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 0);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoChars) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libabsl_time_zone.so.1.2.3rc4", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 3);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 1);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 2);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 34);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoCharsMore) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libabsl_time_zone.so.1.2.3rc4.9", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 4);
|
||||
ASSERT_EQ_UNSIGNED(version[0], 1);
|
||||
ASSERT_EQ_UNSIGNED(version[1], 2);
|
||||
ASSERT_EQ_UNSIGNED(version[2], 34);
|
||||
ASSERT_EQ_UNSIGNED(version[3], 9);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoCharsOnly) {
|
||||
VersionComponents version;
|
||||
bool rv = ElfFileSoVersion("libabsl_time_zone.so.final", &version);
|
||||
ASSERT_TRUE(rv);
|
||||
ASSERT_EQ_UNSIGNED(version.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(CrashReporter, ElfSoNullVersion) {
|
||||
bool rv = ElfFileSoVersion("libabsl_time_zone.so.1", nullptr);
|
||||
ASSERT_FALSE(rv);
|
||||
}
|
||||
16
toolkit/crashreporter/test/gtest/moz.build
Normal file
16
toolkit/crashreporter/test/gtest/moz.build
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
Library("crashreportertest")
|
||||
|
||||
if CONFIG["OS_ARCH"] == "Linux":
|
||||
UNIFIED_SOURCES = [
|
||||
"TestElfSoVersion.cpp",
|
||||
]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
||||
|
|
@ -9,6 +9,10 @@ XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.toml", "unit_ipc/xpcshell.toml"]
|
|||
if CONFIG["MOZ_PHC"]:
|
||||
XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell-phc.toml", "unit_ipc/xpcshell-phc.toml"]
|
||||
|
||||
TEST_DIRS += [
|
||||
"gtest",
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ["browser/browser.toml"]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
|
|||
33
toolkit/crashreporter/test/unit/test_crash_modules_linux.js
Normal file
33
toolkit/crashreporter/test/unit/test_crash_modules_linux.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
add_task(async function run_test() {
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
|
||||
dump(
|
||||
"INFO | test_crash_modules.js | Can't test crashreporter in a non-libxul build.\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await do_crash(
|
||||
function () {
|
||||
crashType = CrashTestUtils.CRASH_ABORT;
|
||||
},
|
||||
async function (mdump, extra, extraFile) {
|
||||
runMinidumpAnalyzer(mdump);
|
||||
|
||||
// Refresh updated extra data
|
||||
extra = await IOUtils.readJSON(extraFile.path);
|
||||
|
||||
// Check modules' versions
|
||||
const modules = extra.StackTraces.modules;
|
||||
const version_regexp = /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/;
|
||||
|
||||
for (let module of modules) {
|
||||
console.debug("module", module);
|
||||
console.debug("version => ", module.version);
|
||||
console.debug("version regex => ", version_regexp.exec(module.version));
|
||||
Assert.notEqual(version_regexp.exec(module.version), null);
|
||||
}
|
||||
},
|
||||
// process will exit with a zero exit status
|
||||
true
|
||||
);
|
||||
});
|
||||
|
|
@ -40,6 +40,10 @@ run-if = ["os == 'win'"]
|
|||
reason = "Test covering Windows-specific module handling"
|
||||
run-sequentially = "very high failure rate in parallel"
|
||||
|
||||
["test_crash_modules_linux.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
reason = "Test covering Linux-specific module handling"
|
||||
|
||||
["test_crash_moz_crash.js"]
|
||||
|
||||
["test_crash_oom.js"]
|
||||
|
|
|
|||
|
|
@ -19,9 +19,14 @@ UNIFIED_SOURCES += [
|
|||
"/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.cc",
|
||||
"/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc",
|
||||
"/toolkit/crashreporter/breakpad-client/minidump_file_writer.cc",
|
||||
"/toolkit/crashreporter/linux_utils.cc",
|
||||
"injector.cc",
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/toolkit/crashreporter/",
|
||||
]
|
||||
|
||||
USE_LIBS += [
|
||||
"breakpad_common_s",
|
||||
"breakpad_linux_common_s",
|
||||
|
|
|
|||
Loading…
Reference in a new issue