示例#1
0
    def MaybeEvict(self, freeEntriesNeeded):
        maxCacheSize = self.sOptions["maxCacheEntries"]
        LogTrace("Cache occupancy before MaybeEvict: " +
                 str(self.sCacheCount) + "/" + str(maxCacheSize))

        if self.sCacheCount == 0 or \
                self.sCacheCount + freeEntriesNeeded <= maxCacheSize:
            # No need to lock mutex here, this doesn't need to be 100%
            return

        # If adding the new entries would exceed the max cache size,
        # evict so that cache is at 70% capacity after new entries added
        numOldEntriesAfterEvict = max(0, (0.70 * maxCacheSize) -
                                      freeEntriesNeeded)
        numToEvict = self.sCacheCount - numOldEntriesAfterEvict

        # Evict symbols until evict quota is met, starting with least recently
        # used
        for (pdbName, pdbId) in reversed(self.sMruSymbols):
            if numToEvict <= 0:
                break

            evicteeCount = self.sCache[pdbName][pdbId].GetEntryCount()

            del self.sCache[pdbName][pdbId]
            self.sCacheCount -= evicteeCount
            self.sMruSymbols.pop()

            numToEvict -= evicteeCount

        LogTrace("Cache occupancy after MaybeEvict: " + str(self.sCacheCount) +
                 "/" + str(maxCacheSize))
def getModuleV2(libName, pdbAge, pdbSig, pdbName):
    if isinstance(pdbSig, basestring):
        matches = gPdbSigRE.match(pdbSig)
        if matches:
            pdbSig = "".join(matches.groups()).upper()
        elif gPdbSigRE2.match(pdbSig):
            pdbSig = pdbSig.upper()
        else:
            LogTrace("Bad PDB signature: " + pdbSig)
            return None
    else:
        LogTrace("Bad PDB signature: " + str(pdbSig))
        return None

    if isinstance(pdbAge, basestring):
        pdbAge = int(pdbAge)
    if not isinstance(pdbAge, (int, long)) or int(pdbAge) < 0:
        LogTrace("Bad PDB age: " + str(pdbAge))
        return None
    pdbAge = (hex(pdbAge)[2:]).lower()

    if not isinstance(pdbName, basestring) or not gLibNameRE.match(pdbName):
        LogTrace("Bad PDB name: " + str(pdbName))
        return None
    return ModuleV3(pdbName, pdbSig + pdbAge)
示例#3
0
    def GetLibSymbolMap(self, libName, breakpadId, symbolSources):
        # Empty lib name means client couldn't associate frame with any lib
        if libName == "":
            return None

        # Check cache first
        libSymbolMap = None
        self.sCacheLock.acquire()
        try:
            if libName in self.sCache and breakpadId in self.sCache[libName]:
                libSymbolMap = self.sCache[libName][breakpadId]
                self.UpdateMruList(libName, breakpadId)
        finally:
            self.sCacheLock.release()

        if libSymbolMap is None:
            LogTrace("Need to fetch PDB file for " + libName + " " +
                     breakpadId)

            # Guess the name of the .sym or .nmsym file on disk
            if libName[-4:] == ".pdb":
                symFileNameWithoutExtension = re.sub(r"\.[^\.]+$", "", libName)
            else:
                symFileNameWithoutExtension = libName

            # Look in the symbol dirs for this .sym or .nmsym file
            for extension, source in itertools.product([".sym", ".nmsym"],
                                                       symbolSources):
                symFileName = symFileNameWithoutExtension + extension
                pathSuffix = os.sep + libName + os.sep + \
                    breakpadId + os.sep + symFileName
                path = self.sOptions["symbolPaths"][source] + pathSuffix
                libSymbolMap = self.FetchSymbolsFromFile(path)
                if libSymbolMap:
                    break

            if not libSymbolMap:
                LogTrace("No matching sym files, tried " + str(symbolSources))
                return None

            LogTrace("Storing libSymbolMap under [" + libName + "][" +
                     breakpadId + "]")
            self.sCacheLock.acquire()
            try:
                self.MaybeEvict(libSymbolMap.GetEntryCount())
                if libName not in self.sCache:
                    self.sCache[libName] = {}
                self.sCache[libName][breakpadId] = libSymbolMap
                self.sCacheCount += libSymbolMap.GetEntryCount()
                self.UpdateMruList(libName, breakpadId)
                LogTrace(
                    str(self.sCacheCount) +
                    " symbols in cache after fetching symbol file")
            finally:
                self.sCacheLock.release()

        return libSymbolMap
示例#4
0
def getModuleV3(libName, breakpadId):
    if not isinstance(libName, basestring) or not gLibNameRE.match(libName):
        LogTrace("Bad library name: " + str(libName))
        return None

    if not isinstance(breakpadId, basestring):
        LogTrace("Bad breakpad id: " + str(breakpadId))
        return None

    return ModuleV3(libName, breakpadId)
 def FetchSymbolsFromFile(self, path):
     try:
         with open(path, "r") as symFile:
             LogMessage("Parsing SYM file at " + path)
             return self.FetchSymbolsFromFileObj(symFile)
     except Exception as e:
         LogTrace("Error opening file " + path + ": " + str(e))
         return None
示例#6
0
    def FetchSymbolsFromFile(self, path):
        try:
            symFile = open(path, "r")
        except Exception as e:
            LogTrace("Error opening file " + path + ": " + str(e))
            return None

        LogMessage("Parsing SYM file at " + path)

        try:
            symbolMap = {}
            lineNum = 0
            publicCount = 0
            funcCount = 0
            for line in symFile:
                lineNum += 1
                if line[0:7] == "PUBLIC ":
                    line = line.rstrip()
                    fields = line.split(" ")
                    if len(fields) < 4:
                        LogTrace("Line " + str(lineNum) + " is messed")
                        continue
                    address = int(fields[1], 16)
                    symbolMap[address] = " ".join(fields[3:])
                    publicCount += 1
                elif line[0:5] == "FUNC ":
                    line = line.rstrip()
                    fields = line.split(" ")
                    if len(fields) < 5:
                        LogTrace("Line " + str(lineNum) + " is messed")
                        continue
                    address = int(fields[1], 16)
                    symbolMap[address] = " ".join(fields[4:])
                    funcCount += 1
        except Exception as e:
            LogError("Error parsing SYM file " + path)
            return None

        logString = "Found " + str(len(
            symbolMap.keys())) + " unique entries from "
        logString += str(publicCount) + " PUBLIC lines, " + str(
            funcCount) + " FUNC lines"
        LogTrace(logString)

        return SymbolInfo(symbolMap)
 def FetchSymbolsFromURL(self, url):
     try:
         with contextlib.closing(urllib2.urlopen(url)) as request:
             if request.getcode() != 200:
                 return None
             LogMessage("Parsing SYM file at " + url)
             return self.FetchSymbolsFromFileObj(request)
     except Exception as e:
         LogTrace("Error opening URL " + url + ": " + str(e))
         return None
    def do_POST(self):
        LogTrace("Received request: " + self.path + " on thread " +
                 threading.currentThread().getName())

        try:
            length = int(self.headers["Content-Length"])
            # Read in the request body without blocking
            self.connection.settimeout(SOCKET_READ_TIMEOUT)
            requestBody = self.rfile.read(length)
            # Put the connection back into blocking mode so rfile/wfile can be used safely
            self.connection.settimeout(None)

            if len(requestBody) < length:
                # This could be a broken connection, writing an error message into it could be a bad idea
                # See http://bugs.python.org/issue14574
                LogTrace("Read " + str(len(requestBody)) +
                         " bytes but Content-Length is " + str(length))
                return

            LogTrace("Request body: " + requestBody)
            rawRequest = json.loads(requestBody)

            request = SymbolicationRequest(gSymFileManager, rawRequest)
            if not request.isValidRequest:
                LogTrace("Unable to parse request")
                self.sendHeaders(400)
                return
        except Exception as e:
            LogTrace("Unable to parse request body: " + str(e))
            # Ensure connection is back in blocking mode so rfile/wfile can be used safely
            self.connection.settimeout(None)
            self.sendHeaders(400)
            return

        try:
            self.sendHeaders(200)

            response = {'symbolicatedStacks': []}
            for stackIndex in range(len(request.stacks)):
                symbolicatedStack = request.Symbolicate(stackIndex)

                # Free up memory ASAP
                request.stacks[stackIndex] = []

                response['symbolicatedStacks'].append(symbolicatedStack)

            response['knownModules'] = request.knownModules[:]
            if not request.includeKnownModulesInResponse:
                response = response['symbolicatedStacks']

            request.Reset()

            LogTrace("Response: " + json.dumps(response))
            self.wfile.write(json.dumps(response))
        except Exception as e:
            LogTrace("Exception in do_POST: " + str(e))
    def MaybeEvict(self, freeEntriesNeeded):
        maxCacheSize = self.sOptions["maxCacheEntries"]
        LogTrace("Cache occupancy before MaybeEvict: " +
                 str(self.sCacheCount) + "/" + str(maxCacheSize))
        #print "Current MRU: " + str(self.sMruSymbols)
        #print "Maybe evicting to make room for ", freeEntriesNeeded, " new entries"

        if self.sCacheCount == 0 or self.sCacheCount + freeEntriesNeeded <= maxCacheSize:
            # No need to lock mutex here, this doesn't need to be 100%
            #print "Sufficient room for new entries, no need to evict"
            return

        # If adding the new entries would exceed the max cache size,
        # evict so that cache is at 70% capacity after new entries added
        numOldEntriesAfterEvict = max(0, (0.70 * maxCacheSize) -
                                      freeEntriesNeeded)
        numToEvict = self.sCacheCount - numOldEntriesAfterEvict

        #print "Evicting: " + str(numToEvict)

        # Evict symbols until evict quota is met, starting with least recently used
        for (pdbName, pdbId) in reversed(self.sMruSymbols):
            if numToEvict <= 0:
                break

            evicteeCount = self.sCache[pdbName][pdbId].GetEntryCount()
            #print "Evicting symbols at " + pdbName + "/" + pdbId + ": " + str(evicteeCount) + " entries"

            del self.sCache[pdbName][pdbId]
            self.sCacheCount -= evicteeCount
            self.sMruSymbols.pop()

            numToEvict -= evicteeCount

        #print "MRU after: " + str(self.sMruSymbols)
        LogTrace("Cache occupancy after MaybeEvict: " + str(self.sCacheCount) +
                 "/" + str(maxCacheSize))
 def PrefetchRecentSymbolFiles(self):
     try:
         mruSymbols = []
         with open(self.sOptions["mruSymbolStateFile"], "rb") as f:
             mruSymbols = json.load(
                 f)["symbols"][:self.sOptions["maxMRUSymbolsPersist"]]
         LogMessage("Going to prefetch %d recent symbol files" %
                    len(mruSymbols))
         self.sUpdateMRUFile = False
         for libName, breakpadId in mruSymbols:
             sym = self.GetLibSymbolMap(libName, breakpadId)
             if sym is None:
                 LogTrace("Failed to prefetch symbols for (%s,%s)" %
                          (libName, breakpadId))
         LogMessage("Finished prefetching recent symbol files")
     except IOError:
         LogError("Error reading MRU symbols state file")
     finally:
         self.sUpdateMRUFile = True
    def GetLibSymbolMap(self, libName, breakpadId):
        # Empty lib name means client couldn't associate frame with any lib
        if libName == "":
            return None

        # Check cache first
        libSymbolMap = None
        self.sCacheLock.acquire()
        try:
            if libName in self.sCache and breakpadId in self.sCache[libName]:
                libSymbolMap = self.sCache[libName][breakpadId]
                self.UpdateMruList(libName, breakpadId)
        finally:
            self.sCacheLock.release()

        if libSymbolMap is None:
            LogTrace("Need to fetch PDB file for " + libName + " " +
                     breakpadId)

            # Guess the name of the .sym file on disk
            if libName[-4:] == ".pdb":
                symFileName = re.sub(r"\.[^\.]+$", ".sym", libName)
            else:
                symFileName = libName + ".sym"

            pathSuffix = os.path.join(libName, breakpadId, symFileName)

            # Look in the symbol dirs for this .sym file
            for symbolPath in self.sOptions["symbolPaths"]:
                path = os.path.join(symbolPath, pathSuffix)
                libSymbolMap = self.FetchSymbolsFromFile(path)
                if libSymbolMap:
                    break

            # If not in symbolPaths try URLs
            if not libSymbolMap:
                for symbolURL in self.sOptions["symbolURLs"]:
                    url = urlparse.urljoin(symbolURL, pathSuffix)
                    libSymbolMap = self.FetchSymbolsFromURL(url)
                    if libSymbolMap:
                        break

            if not libSymbolMap:
                LogTrace(
                    "No matching sym files, tried paths: %s and URLs: %s" %
                    (", ".join(self.sOptions["symbolPaths"]), ", ".join(
                        self.sOptions["symbolURLs"])))
                return None

            LogTrace("Storing libSymbolMap under [" + libName + "][" +
                     breakpadId + "]")
            self.sCacheLock.acquire()
            try:
                self.MaybeEvict(libSymbolMap.GetEntryCount())
                if libName not in self.sCache:
                    self.sCache[libName] = {}
                self.sCache[libName][breakpadId] = libSymbolMap
                self.sCacheCount += libSymbolMap.GetEntryCount()
                self.UpdateMruList(libName, breakpadId)
                LogTrace(
                    str(self.sCacheCount) +
                    " symbols in cache after fetching symbol file")
            finally:
                self.sCacheLock.release()

        return libSymbolMap
示例#12
0
    def FetchSymbolsFromFile(self, path):
        try:
            symFile = open(path, "r")
        except Exception as e:
            LogTrace("Error opening file " + path + ": " + str(e))
            return None

        LogMessage("Parsing SYM file at " + path)

        try:
            symbolMap = {}
            lineNum = 0
            publicCount = 0
            funcCount = 0
            if path.endswith(".sym"):
                for line in symFile:
                    lineNum += 1
                    if line[0:7] == "PUBLIC ":
                        line = line.rstrip()
                        fields = line.split(" ")
                        if len(fields) < 4:
                            LogTrace("Line " + str(lineNum) + " is messed")
                            continue
                        address = int(fields[1], 16)
                        symbolMap[address] = " ".join(fields[3:])
                        publicCount += 1
                    elif line[0:5] == "FUNC ":
                        line = line.rstrip()
                        fields = line.split(" ")
                        if len(fields) < 5:
                            LogTrace("Line " + str(lineNum) + " is messed")
                            continue
                        address = int(fields[1], 16)
                        symbolMap[address] = " ".join(fields[4:])
                        funcCount += 1
            elif path.endswith(".nmsym"):
                addressLength = 0
                for line in symFile:
                    lineNum += 1
                    if line.startswith(" "):
                        continue
                    if addressLength == 0:
                        addressLength = line.find(" ")
                    address = int(line[0:addressLength], 16)
                    # Some lines have the form
                    # "address space letter space symbol",
                    # some have the form "address space symbol".
                    # The letter has a meaning, but we ignore it.
                    if line[addressLength + 2] == " ":
                        symbol = line[addressLength + 3:].rstrip()
                    else:
                        symbol = line[addressLength + 1:].rstrip()
                    symbolMap[address] = symbol
                    publicCount += 1
        except Exception as e:
            LogError("Error parsing SYM file " + path)
            return None

        logString = "Found " + \
            str(len(symbolMap.keys())) + " unique entries from "
        logString += str(publicCount) + " PUBLIC lines, " + \
            str(funcCount) + " FUNC lines"
        LogTrace(logString)

        return SymbolInfo(symbolMap)
示例#13
0
    def ParseRequests(self, rawRequests):
        self.isValidRequest = False

        try:
            if not isinstance(rawRequests, dict):
                LogTrace("Request is not a dictionary")
                return

            if "version" not in rawRequests:
                LogTrace("Request is missing 'version' field")
                return
            version = rawRequests["version"]
            if version != 4:
                LogTrace("Invalid version: %s" % version)
                return

            if "forwarded" in rawRequests:
                if not isinstance(rawRequests["forwarded"], (int, long)):
                    LogTrace("Invalid 'forwards' field: " +
                             str(rawRequests["forwarded"]))
                    return
                self.forwardCount = rawRequests["forwarded"]

            # Client specifies which sets of symbols should be used
            if "symbolSources" in rawRequests:
                try:
                    sourceList = [
                        x.upper() for x in rawRequests["symbolSources"]
                    ]
                    for source in sourceList:
                        if source in self.symFileManager.sOptions[
                                "symbolPaths"]:
                            self.symbolSources.append(source)
                        else:
                            LogTrace("Unrecognized symbol source: " + source)
                            continue
                except:
                    self.symbolSources = []
                    pass

            if not self.symbolSources:
                self.symbolSources.append(
                    self.symFileManager.sOptions["defaultApp"])
                self.symbolSources.append(
                    self.symFileManager.sOptions["defaultOs"])

            if "memoryMap" not in rawRequests:
                LogTrace("Request is missing 'memoryMap' field")
                return
            memoryMap = rawRequests["memoryMap"]
            if not isinstance(memoryMap, list):
                LogTrace("'memoryMap' field in request is not a list")

            if "stacks" not in rawRequests:
                LogTrace("Request is missing 'stacks' field")
                return
            stacks = rawRequests["stacks"]
            if not isinstance(stacks, list):
                LogTrace("'stacks' field in request is not a list")
                return

            # Check memory map is well-formatted
            cleanMemoryMap = []
            for module in memoryMap:
                if not isinstance(module, list):
                    LogTrace("Entry in memory map is not a list: " +
                             str(module))
                    return

                if len(module) != 2:
                    LogTrace("Entry in memory map is not a 2 item list: " +
                             str(module))
                    return
                module = getModuleV3(*module)

                if module is None:
                    return

                cleanMemoryMap.append(module)

            self.combinedMemoryMap = cleanMemoryMap
            self.knownModules = [False] * len(self.combinedMemoryMap)

            if version < 4:
                self.includeKnownModulesInResponse = False

            # Check stack is well-formatted
            for stack in stacks:
                if not isinstance(stack, list):
                    LogTrace("stack is not a list")
                    return
                for entry in stack:
                    if not isinstance(entry, list):
                        LogTrace("stack entry is not a list")
                        return
                    if len(entry) != 2:
                        LogTrace("stack entry doesn't have exactly 2 elements")
                        return

                self.stacks.append(stack)

        except Exception as e:
            LogTrace("Exception while parsing request: " + str(e))
            return

        self.isValidRequest = True
示例#14
0
    def ForwardRequest(self, indexes, stack, modules, symbolicatedStack):
        LogTrace("Forwarding " + str(len(stack)) + " PCs for symbolication")

        try:
            url = self.symFileManager.sOptions["remoteSymbolServer"]
            rawModules = []
            moduleToIndex = {}
            newIndexToOldIndex = {}
            for moduleIndex, m in modules:
                l = [m.libName, m.breakpadId]
                newModuleIndex = len(rawModules)
                rawModules.append(l)
                moduleToIndex[m] = newModuleIndex
                newIndexToOldIndex[newModuleIndex] = moduleIndex

            rawStack = []
            for entry in stack:
                moduleIndex = entry[0]
                offset = entry[1]
                module = self.combinedMemoryMap[moduleIndex]
                newIndex = moduleToIndex[module]
                rawStack.append([newIndex, offset])

            requestVersion = 4
            while True:
                requestObj = {
                    "symbolSources": self.symbolSources,
                    "stacks": [rawStack],
                    "memoryMap": rawModules,
                    "forwarded": self.forwardCount + 1,
                    "version": requestVersion
                }
                requestJson = json.dumps(requestObj)
                headers = {"Content-Type": "application/json"}
                requestHandle = urllib2.Request(url, requestJson, headers)
                try:
                    response = urllib2.urlopen(requestHandle)
                except Exception as e:
                    if requestVersion == 4:
                        # Try again with version 3
                        requestVersion = 3
                        continue
                    raise e
                succeededVersion = requestVersion
                break

        except Exception as e:
            LogError("Exception while forwarding request: " + str(e))
            return

        try:
            responseJson = json.loads(response.read())
        except Exception as e:
            LogError(
                "Exception while reading server response to forwarded request: "
                + str(e))
            return

        try:
            if succeededVersion == 4:
                responseKnownModules = responseJson['knownModules']
                for newIndex, known in enumerate(responseKnownModules):
                    if known and newIndex in newIndexToOldIndex:
                        self.knownModules[newIndexToOldIndex[newIndex]] = True

                responseSymbols = responseJson['symbolicatedStacks'][0]
            else:
                responseSymbols = responseJson[0]
            if len(responseSymbols) != len(stack):
                LogError(
                    str(len(responseSymbols)) + " symbols in response, " +
                    str(len(stack)) + " PCs in request!")
                return

            for index in range(0, len(stack)):
                symbol = responseSymbols[index]
                originalIndex = indexes[index]
                symbolicatedStack[originalIndex] = symbol
        except Exception as e:
            LogError(
                "Exception while parsing server response to forwarded request: "
                + str(e))
            return
    def ParseRequests(self, rawRequests):
        self.isValidRequest = False

        try:
            if not isinstance(rawRequests, dict):
                LogTrace("Request is not a dictionary")
                return

            if "version" not in rawRequests:
                LogTrace("Request is missing 'version' field")
                return
            version = rawRequests["version"]
            if version != 2 and version != 3 and version != 4:
                LogTrace("Invalid version: %s" % version)
                return

            if "forwarded" in rawRequests:
                if not isinstance(rawRequests["forwarded"], (int, long)):
                    LogTrace("Invalid 'forwards' field: " +
                             str(rawRequests["forwarded"]))
                    return
                self.forwardCount = rawRequests["forwarded"]

            if "memoryMap" not in rawRequests:
                LogTrace("Request is missing 'memoryMap' field")
                return
            memoryMap = rawRequests["memoryMap"]
            if not isinstance(memoryMap, list):
                LogTrace("'memoryMap' field in request is not a list")

            if "stacks" not in rawRequests:
                LogTrace("Request is missing 'stacks' field")
                return
            stacks = rawRequests["stacks"]
            if not isinstance(stacks, list):
                LogTrace("'stacks' field in request is not a list")
                return

            # Check memory map is well-formatted
            cleanMemoryMap = []
            for module in memoryMap:
                if not isinstance(module, list):
                    LogTrace("Entry in memory map is not a list: " +
                             str(module))
                    return

                if version == 2:
                    if len(module) != 4:
                        LogTrace("Entry in memory map is not a 4 item list: " +
                                 str(module))
                        return
                    module = getModuleV2(*module)
                else:
                    assert version == 3 or version == 4
                    if len(module) != 2:
                        LogTrace("Entry in memory map is not a 2 item list: " +
                                 str(module))
                        return
                    module = getModuleV3(*module)

                if module is None:
                    return

                cleanMemoryMap.append(module)

            self.combinedMemoryMap = cleanMemoryMap
            self.knownModules = [False] * len(self.combinedMemoryMap)

            if version < 4:
                self.includeKnownModulesInResponse = False

            # Check stack is well-formatted
            for stack in stacks:
                if not isinstance(stack, list):
                    LogTrace("stack is not a list")
                    return
                for entry in stack:
                    if not isinstance(entry, list):
                        LogTrace("stack entry is not a list")
                        return
                    if len(entry) != 2:
                        LogTrace("stack entry doesn't have exactly 2 elements")
                        return

                self.stacks.append(stack)

        except Exception as e:
            LogTrace("Exception while parsing request: " + str(e))
            return

        self.isValidRequest = True