Get debug filename using Build-ID or .gnu_debuglink section
authorDmitry Kovalenko <d.kovalenko@samsung.com>
Mon, 10 Jul 2017 05:02:44 +0000 (08:02 +0300)
committerMaria Guseva <m.guseva@samsung.com>
Fri, 14 Jul 2017 03:28:38 +0000 (12:28 +0900)
Use readelf output to obtain debug filename

scripts/sancov_symbolize.py

index a080fe9c2a02557ff42893d76b86302592ae693f..d8e3dd2050c8714981779823b61e8fe74c0e977a 100755 (executable)
@@ -12,8 +12,10 @@ import collections
 import hashlib
 import json
 import re
+import binascii
+import struct
 import os
-from os.path import realpath, isfile, basename
+from os.path import realpath, isfile
 
 SANCOV_EXEC = 'sancov.py'
 SYMBOLIZER_EXEC = 'addr2line'
@@ -234,6 +236,80 @@ def serialize_all_points(coverage_points):
     return serialized
 
 
+def calc_file_crc32(path):
+    """
+    Calculate file's crc32
+
+    Keyword arguments:
+    path -- path to elf binary to parse
+    """
+
+    crc = None
+    with open(path, 'rb') as f:
+        crc = binascii.crc32(f.read())
+    return crc
+
+
+def get_debugfile_name(path):
+    """
+    Get debug filename from .gnu_debuglink section
+
+    Keyword arguments:
+    path -- path to elf binary to parse
+
+    Return:
+    Tuple (filename, crc32)
+    """
+
+    cmd = "objcopy " + path +\
+          " /dev/null --dump-section .gnu_debuglink=/dev/stdout"
+    try:
+        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE, shell=True)
+    except subprocess.CalledProcessError as err:
+        sys.stderr.write(err.output.decode())
+        exit(err.returncode)
+
+    out = proc.communicate()[0]
+    if not out:
+        return (None, None)
+
+    # debug filename is zero-ended
+    name = out.split('\x00')[0]
+    # crc32 is last 4 bytes
+    crc = struct.unpack('i', out[-4:])[0]
+    return (name, crc)
+
+
+def get_debugfile_by_id(path):
+    """
+    Get debug filename according Build-ID
+
+    Keyword arguments:
+    path -- path to elf binary to parse
+    """
+
+    cmd = "readelf -n " + path
+
+    try:
+        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE, shell=True)
+    except subprocess.CalledProcessError as err:
+        sys.stderr.write(err.output.decode())
+        exit(err.returncode)
+
+    out = proc.communicate()[0]
+    # Search for such string:
+    # Build ID: b2d87caa09c74d1717fd5b217d85f737fd60c7b8
+    name = re.findall(r"\S*Build ID: ([a-z0-9]*)", out)
+    path = "/usr/lib/debug/.build-id/{}/{}.debug".format(name[0][:2],
+                                                         name[0][2:])
+    if len(name) and os.path.exists(path):
+        return path
+    else:
+        return ""
+
+
 def parse_args(args):
     """
     Parses command line arguments.
@@ -255,11 +331,11 @@ def parse_args(args):
         else:
             debug_dirs = args[1].split(":")
             path_index = 2
-        debug_info = [
-            debug_dir + "/" + f
+        debug_info = dict(
+            (f, debug_dir + "/" + f)
             for debug_dir in debug_dirs for f in os.listdir(debug_dir)
             if isfile(debug_dir + "/" + f)
-        ]
+        )
 
     if (argc - path_index) % 2 != 0:
         usage()
@@ -271,13 +347,16 @@ def parse_args(args):
     for i in range(path_index, argc, 2):
         sancov_dumps.append(realpath(args[i]))
         binaries.append(realpath(args[i + 1]))
-        # TODO: replace basic filenames comparison check with
-        # binary debug-link check
-        for debug_file in debug_info:
-            if basename(binaries[-1]) + ".debug" == basename(debug_file):
-                filtered_debug_info.append(debug_file)
-                break
-        if len(filtered_debug_info) < len(binaries):
+        debug_file = get_debugfile_by_id(binaries[-1])
+        if debug_file:
+            filtered_debug_info.append(debug_file)
+            continue
+
+        debug_file = get_debugfile_name(binaries[-1])
+        if debug_file[0] in debug_info and\
+           calc_file_crc32(debug_info[debug_file[0]]) == debug_file[1]:
+                filtered_debug_info.append(debug_info[debug_file[0]])
+        else:
             filtered_debug_info.append(binaries[-1])
 
     print_symcov(zip(sancov_dumps, binaries, filtered_debug_info))