| #! /usr/bin/python |
| |
| # Copyright (C) 2019 Apple Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| # THE POSSIBILITY OF SUCH DAMAGE. |
| |
| import re |
| import sys |
| |
| icStatRecord = re.compile(r" +(\w+)\(([^,]+), ([^)]+)\)([^:]*): (\d+)") |
| getByIdPrefix = "OperationGetById" |
| putByIdPrefix = "OperationPutById" |
| |
| |
| class ICStats: |
| def __init__(self): |
| self.combinedRecords = {} |
| self.slowGetById = {} |
| self.slowPutById = {} |
| self.totalSlowGetById = 0 |
| self.totalSlowPutById = 0 |
| |
| def parse(self, file): |
| for line in file: |
| match = re.match(icStatRecord, line) |
| if match: |
| operation = match.group(1) |
| base = match.group(2) |
| property = match.group(3) |
| location = match.group(4).strip() |
| count = int(match.group(5)) |
| recordKey = (operation, base, property, location) |
| |
| if recordKey not in self.combinedRecords: |
| self.combinedRecords[recordKey] = count |
| else: |
| self.combinedRecords[recordKey] += count |
| |
| if operation.startswith(getByIdPrefix): |
| self.totalSlowGetById += count |
| |
| slowGetByIdKey = (base, property) |
| if slowGetByIdKey not in self.slowGetById: |
| self.slowGetById[slowGetByIdKey] = count |
| else: |
| self.slowGetById[slowGetByIdKey] += count |
| |
| elif operation.startswith(putByIdPrefix): |
| self.totalSlowPutById += count |
| |
| slowPutByIdKey = (base, property) |
| if slowPutByIdKey not in self.slowPutById: |
| self.slowPutById[slowPutByIdKey] = count |
| else: |
| self.slowPutById[slowPutByIdKey] += count |
| |
| def dumpStats(self): |
| print("Total Slow getById = {0:>13,d}".format(self.totalSlowGetById)) |
| print("Total Slow putById = {0:>13,d}".format(self.totalSlowPutById)) |
| |
| print("Operation Base Property Location Count % tot") |
| print("----------------------------------- -------------------- ------------------------------------ ------------ --------- slow") |
| |
| keys = sorted(self.combinedRecords.keys(), key=lambda t: self.combinedRecords[t], reverse=True) |
| for key in keys: |
| count = self.combinedRecords[key] |
| operation = key[0] |
| base = key[1] |
| property = key[2] |
| |
| if operation.startswith(getByIdPrefix): |
| slowPercent = " {0:>4.1f}%".format(float(self.slowGetById[(base, property)] * 100) / float(self.totalSlowGetById)) |
| elif operation.startswith(putByIdPrefix): |
| slowPercent = " {0:>4.1f}%".format(float(self.slowPutById[(base, property)] * 100) / float(self.totalSlowPutById)) |
| else: |
| slowPercent = "" |
| |
| if len(property) > 36: |
| property = property[0:32] + "..." |
| |
| print("{0:35} {1:20} {2:36} {3:12} {4:>9d}{5}".format(operation[0:34], base, property, key[3], count, slowPercent)) |
| |
| |
| def usage(): |
| print("Usage: {0} [ic-stats-file]".format(sys.argv[0])) |
| print(" Where <ic-stats-file> is the results of using the useICStats option.") |
| exit(1) |
| |
| if __name__ == "__main__": |
| if len(sys.argv) > 1: |
| if sys.argv[1] == "-h" or sys.argv[1].lower() == "--help" or sys.argv[1] == "-?": |
| usage() |
| try: |
| file = open(sys.argv[1], "r") |
| except IOError as e: |
| print("Couldn't open {0}, {1}".format(sys.argv[1], e.strerror)) |
| usage() |
| except: |
| print("Unexpected error:", sys.exc_info()[0]) |
| usage() |
| else: |
| file = sys.stdin |
| |
| icStats = ICStats() |
| icStats.parse(file) |
| icStats.dumpStats() |