[oe] [meta-oe][PATCH] socorro-syms: Add Breakpad symbol generation adapted for Socorro

Daniel Markus daniel.markus at leica-geosystems.com
Tue Feb 24 13:13:26 UTC 2015


https://github.com/mozilla/socorro

Mozilla Socorro is a downloadable crash dump server that handles
Breakpad crash dumps coming from own applications. In order for
Socorro to create clickable links into your source code repository,
Breakpad's symbol files must be copied and modified to contain VCS
information for each file reference.

This class reads the symbol file generated by Breakpad and creates
a new symbol file with that VCS information contained.

Signed-off-by: Daniel Markus <daniel.markus at leica-geosystems.com>
---
 meta-oe/classes/socorro-syms.bbclass | 165 +++++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)
 create mode 100644 meta-oe/classes/socorro-syms.bbclass

diff --git a/meta-oe/classes/socorro-syms.bbclass b/meta-oe/classes/socorro-syms.bbclass
new file mode 100644
index 0000000..a4ac079
--- /dev/null
+++ b/meta-oe/classes/socorro-syms.bbclass
@@ -0,0 +1,165 @@
+# Inherit this class when you want to allow Mozilla Socorro to link Breakpad's
+# stack trace information to the correct source code revision.
+# This class creates a new version of the symbol file (.sym) created by
+# Breakpad. The absolute file paths in the symbol file will be replaced by VCS,
+# branch, file and revision of the source file. That information facilitates the
+# lookup of a particular source code line in the stack trace.
+#
+# Use example:
+#
+# inherit breakpad
+# inherit socorro-syms
+# BREAKPAD_BIN = "YourBinary"
+#
+
+# We depend on Breakpad creating the original symbol file.
+DEPENDS += "breakpad"
+
+PACKAGE_PREPROCESS_FUNCS += "symbol_file_preprocess"
+PACKAGES =+ "${PN}-socorro-syms"
+FILES_${PN}-socorro-syms = "/usr/share/socorro-syms"
+
+
+python symbol_file_preprocess() {
+
+    package_dir = d.getVar("PKGD", True)
+    breakpad_bin = d.getVar("BREAKPAD_BIN", True)
+    if not breakpad_bin:
+        package_name = d.getVar("PN", True)
+        bb.error("Package %s depends on Breakpad via socorro-syms. See "
+            "breakpad.bbclass for instructions on setting up the Breakpad "
+            "configuration." % package_name)
+        raise ValueError("BREAKPAD_BIN not defined in %s." % package_name)
+
+    sym_file_name = breakpad_bin + ".sym"
+
+    breakpad_syms_dir = os.path.join(
+        package_dir, "usr", "share", "breakpad-syms")
+    socorro_syms_dir = os.path.join(
+        package_dir, "usr", "share", "socorro-syms")
+    if not os.path.exists(socorro_syms_dir):
+        os.makedirs(socorro_syms_dir)
+
+    breakpad_sym_file_path = os.path.join(breakpad_syms_dir, sym_file_name)
+    socorro_sym_file_path = os.path.join(socorro_syms_dir, sym_file_name)
+
+    # In the symbol file, all source files are referenced like the following.
+    # FILE 123 /path/to/some/File.cpp
+    # Go through all references and replace the file paths with repository
+    # paths.
+    with open(breakpad_sym_file_path, 'r') as breakpad_sym_file, \
+            open(socorro_sym_file_path, 'w') as socorro_sym_file:
+
+        for line in breakpad_sym_file:
+            if line.startswith("FILE "):
+                socorro_sym_file.write(socorro_file_reference(line))
+            else:
+                socorro_sym_file.write(line)
+
+    return
+}
+
+
+def socorro_file_reference(line):
+
+    # The 3rd position is the file path. See example above.
+    source_file_path = line.split()[2]
+    source_file_repo_path = repository_path(os.path.normpath(source_file_path))
+
+    # If the file could be found in any repository then replace it with the
+    # repository's path.
+    if source_file_repo_path:
+        return line.replace(source_file_path, source_file_repo_path)
+
+    return line
+
+
+def repository_path(source_file_path):
+
+    if not os.path.isfile(source_file_path):
+        return None
+
+    # Check which VCS is used and use that to extract repository information.
+    (output, error) = bb.process.run("git status",
+        cwd=os.path.dirname(source_file_path))
+    if not error:
+        return git_repository_path(source_file_path)
+
+    # Here we can add support for other VCSs like hg, svn, cvs, etc.
+
+    # The source file isn't under any VCS so we leave it be.
+    return None
+
+
+def run_command(command, directory):
+
+    (output, error) = bb.process.run(command, cwd=directory)
+    if error:
+        raise bb.process.ExecutionError(command, error)
+
+    return output.rstrip()
+
+
+def git_repository_path(source_file_path):
+
+    import re
+
+    # We need to extract the following.
+    # (1): VCS URL, (2): branch, (3): repo root directory name, (4): repo file,
+    # (5): revision.
+
+    source_file_dir = os.path.dirname(source_file_path)
+
+    # (1) Get the VCS URL and extract the server part, i.e. change the URL from
+    # gitolite at git.someserver.com:SomeRepo.git to just git.someserver.com.
+    source_long_url = run_command(
+        "git config --get remote.origin.url", source_file_dir)
+
+    # The URL could be a local download directory. If so, get the URL again
+    # using the local directory's config file.
+    if os.path.isdir(source_long_url):
+        git_config_file = os.path.join(source_long_url, "config")
+        source_long_url = run_command(
+            "git config --file %s --get remote.origin.url" % git_config_file,
+            source_file_dir)
+
+    # The URL can have several formats. A full list can be found using
+    # git help clone. Extract the server part with a regex.
+    url_match = re.search(".*(://|@)([^:/]*).*", source_long_url)
+    source_server = url_match.group(2)
+
+    # (2) Get the branch for this file. If it's detached and just shows HEAD
+    # then set it to master and hope we can get the correct revision from there.
+    source_branch = run_command(
+        "git rev-parse --abbrev-ref HEAD", source_file_dir)
+    if source_branch == "HEAD":
+        source_branch = "master"
+
+    # (3) Since the repo root directory name can be changed without affecting
+    # git, we need to extract the name from something more reliable.
+    # The git URL has a repo name that we could use. We just need to strip off
+    # everything around it - from gitolite at git.someserver.com:SomeRepo.git/ to
+    # SomeRepo.
+    source_repo_dir = re.sub("/$", "", source_long_url)
+    source_repo_dir = re.sub("\.git$", "", source_repo_dir)
+    source_repo_dir = re.sub(".*[:/]", "", source_repo_dir)
+
+    # (4) We know the file but want to remove all of the build system dependent
+    # path up to and including the repository's root directory, e.g. remove
+    # /home/someuser/dev/repo/projectx/
+    source_toplevel = run_command(
+        "git rev-parse --show-toplevel", source_file_dir)
+    source_toplevel = source_toplevel + os.path.sep
+    source_file = source_file_path.replace(source_toplevel, "")
+
+    # (5) Get the source revision this file is part of.
+    source_revision = run_command("git rev-parse HEAD", source_file_dir)
+
+    # Assemble the repository path according to the Socorro format.
+    socorro_reference = "git:%s/%s:%s/%s:%s" % \
+        (source_server, source_branch,
+        source_repo_dir, source_file,
+        source_revision)
+
+    return socorro_reference
+
-- 
1.9.1




More information about the Openembedded-devel mailing list