Bug 1216681 - Add a fileid utility to extract the breakpad GUID from object files for identification in fix_stack_using_bpsyms. r=ted

fix_stack_using_bpsyms.py locates a .sym file based on file name only, not uuid or full path,
which causes a failure if a duplicate leaf file name is introduced. This patch introduces a
small utility program on mac and linux to extract a breakpad guid from a shared library or
executable to identify the correct symbol file when this ambiguity occurs. A subsequent commit
implements this for windows.

--HG--
extra : commitid : 2O7REfHuDus
This commit is contained in:
Chris Manchester 2015-10-21 16:37:42 -07:00
parent 570a28bb10
commit 58d5a05e65
6 changed files with 155 additions and 4 deletions

View File

@ -20,6 +20,7 @@ libs::
TEST_HARNESS_BINS := \
xpcshell$(BIN_SUFFIX) \
ssltunnel$(BIN_SUFFIX) \
fileid$(BIN_SUFFIX) \
certutil$(BIN_SUFFIX) \
pk12util$(BIN_SUFFIX) \
BadCertServer$(BIN_SUFFIX) \

View File

@ -0,0 +1,51 @@
/* 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 <stdio.h>
#include "common/linux/file_id.h"
//TODO: move this somewhere common, this is copied from dump_symbols.cc
// Format the Elf file identifier in IDENTIFIER as a UUID with the
// dashes removed.
void FormatIdentifier(unsigned char identifier[google_breakpad::kMDGUIDSize],
char result_guid[40]) {
char identifier_str[40];
google_breakpad::FileID::ConvertIdentifierToString(
identifier,
identifier_str,
sizeof(identifier_str));
int bufpos = 0;
for (int i = 0; identifier_str[i] != '\0'; ++i)
if (identifier_str[i] != '-')
result_guid[bufpos++] = identifier_str[i];
// Add an extra "0" by the end. PDB files on Windows have an 'age'
// number appended to the end of the file identifier; this isn't
// really used or necessary on other platforms, but let's preserve
// the pattern.
result_guid[bufpos++] = '0';
// And null terminate.
result_guid[bufpos] = '\0';
}
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "usage: fileid <elf file>\n");
return 1;
}
unsigned char identifier[google_breakpad::kMDGUIDSize];
google_breakpad::FileID file_id(argv[1]);
if (!file_id.ElfFileIdentifier(identifier)) {
fprintf(stderr, "%s: unable to generate file identifier\n",
argv[1]);
return 1;
}
char result_guid[40];
FormatIdentifier(identifier, result_guid);
printf("%s\n", result_guid);
return 0;
}

View File

@ -0,0 +1,52 @@
/* 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 <stdio.h>
#include <string>
#include "common/mac/arch_utilities.h"
#include "common/mac/file_id.h"
//TODO: move this somewhere common, this is copied from dump_symbols.cc
// Format the Mach-O identifier in IDENTIFIER as a UUID with the
// dashes removed.
std::string FormatIdentifier(unsigned char identifier[16])
{
char identifier_string[40];
google_breakpad::FileID::ConvertIdentifierToString(identifier, identifier_string,
sizeof(identifier_string));
std::string compacted(identifier_string);
for(size_t i = compacted.find('-'); i != std::string::npos;
i = compacted.find('-', i))
compacted.erase(i, 1);
compacted += '0';
return compacted;
}
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "usage: fileid <object file>\n");
return 1;
}
unsigned char identifier[16];
google_breakpad::FileID file_id(argv[1]);
// We should be able to use NXGetLocalArchInfo for this, but it returns
// CPU_TYPE_X86 (which is the same as CPU_TYPE_I386) on x86_64 machines,
// when our binary will typically have CPU_TYPE_X86_64 to match against.
// So we hard code x86_64. In practice that's where we're running tests,
// and that's what our debug binaries will contain.
if (!file_id.MachoIdentifier(CPU_TYPE_X86_64, CPU_SUBTYPE_MULTIPLE,
identifier)) {
fprintf(stderr, "%s: unable to generate file identifier\n",
argv[1]);
return 1;
}
printf("%s\n", FormatIdentifier(identifier).c_str());
return 0;
}

View File

@ -0,0 +1,29 @@
# -*- 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/.
if CONFIG['OS_ARCH'] == 'Linux':
USE_LIBS += [
'breakpad_linux_common_s',
]
SOURCES += ['linux_fileid.cpp']
if CONFIG['OS_ARCH'] == 'Darwin':
USE_LIBS += [
'breakpad_mac_common_s',
]
SOURCES += ['mac_fileid.cpp']
if CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'Darwin':
USE_LIBS += [
'breakpad_common_s',
'breakpad_logging',
]
LOCAL_INCLUDES += [
'/toolkit/crashreporter/google-breakpad/src',
]
Program('fileid')

View File

@ -173,6 +173,7 @@ if CONFIG['ENABLE_TESTS']:
'/testing/mochitest',
'/testing/xpcshell',
'/testing/tools/screenshot',
'/testing/tools/fileid',
'/testing/profiles',
'/testing/mozbase',
'/testing/modules',

View File

@ -13,8 +13,11 @@ from __future__ import with_statement
import sys
import os
import re
import subprocess
import bisect
here = os.path.dirname(__file__)
def prettyFileName(name):
if name.startswith("../") or name.startswith("..\\"):
# dom_quickstubs.cpp and many .h files show up with relative paths that are useless
@ -77,9 +80,21 @@ class SymbolFile:
else:
return ""
def guessSymbolFile(fn, symbolsDir):
def findIdForPath(path):
"""Finds the breakpad id for the object file at the given path."""
# We should always be packaged with a "fileid" executable.
fileid_exe = os.path.join(here, 'fileid')
if not os.path.isfile(fileid_exe):
raise Exception("Could not find fileid executable in %s" % here)
try:
return subprocess.check_output([fileid_exe, path]).rstrip()
except subprocess.CalledProcessError as e:
raise Exception("Error getting fileid for %s: %s" %
(path, e.output))
def guessSymbolFile(full_path, symbolsDir):
"""Guess a symbol file based on an object file's basename, ignoring the path and UUID."""
fn = os.path.basename(fn)
fn = os.path.basename(full_path)
d1 = os.path.join(symbolsDir, fn)
if not os.path.exists(d1):
fn = fn + ".pdb"
@ -90,10 +105,12 @@ def guessSymbolFile(fn, symbolsDir):
if len(uuids) == 0:
raise Exception("Missing symbol file for " + fn)
if len(uuids) > 1:
raise Exception("Ambiguous symbol file for " + fn)
uuid = findIdForPath(full_path)
else:
uuid = uuids[0]
if fn.endswith(".pdb"):
fn = fn[:-4]
return os.path.join(d1, uuids[0], fn + ".sym")
return os.path.join(d1, uuid, fn + ".sym")
parsedSymbolFiles = {}
def getSymbolFile(file, symbolsDir):