Index: tools/stats-viewer.py |
diff --git a/tools/stats-viewer.py b/tools/stats-viewer.py |
index bd6a8fb913e24e8983ad85664799e722a53bc39f..220124fd8560c545699a58725b3c2ecc46f58b11 100755 |
--- a/tools/stats-viewer.py |
+++ b/tools/stats-viewer.py |
@@ -1,3 +1,5 @@ |
+#!/usr/bin/env python |
+# |
# Copyright 2008 the V8 project authors. All rights reserved. |
# Redistribution and use in source and binary forms, with or without |
# modification, are permitted provided that the following conditions are |
@@ -49,8 +51,9 @@ UPDATE_INTERVAL_MS = 100 |
COUNTER_LABELS = {"t": "%i ms.", "c": "%i"} |
-# The magic number used to check if a file is not a counters file |
+# The magic numbers used to check if a file is not a counters file |
COUNTERS_FILE_MAGIC_NUMBER = 0xDEADFACE |
+CHROME_COUNTERS_FILE_MAGIC_NUMBER = 0x13131313 |
class StatsViewer(object): |
@@ -99,10 +102,12 @@ class StatsViewer(object): |
fileno = data_file.fileno() |
self.shared_mmap = mmap.mmap(fileno, size, access=mmap.ACCESS_READ) |
data_access = SharedDataAccess(self.shared_mmap) |
- if data_access.IntAt(0) != COUNTERS_FILE_MAGIC_NUMBER: |
- print "File %s is not stats data." % self.data_name |
- sys.exit(1) |
- return CounterCollection(data_access) |
+ if data_access.IntAt(0) == COUNTERS_FILE_MAGIC_NUMBER: |
+ return CounterCollection(data_access) |
+ elif data_access.IntAt(0) == CHROME_COUNTERS_FILE_MAGIC_NUMBER: |
+ return ChromeCounterCollection(data_access) |
+ print "File %s is not stats data." % self.data_name |
+ sys.exit(1) |
def CleanUp(self): |
"""Cleans up the memory mapped file if necessary.""" |
@@ -356,6 +361,72 @@ class CounterCollection(object): |
return 4 + self.max_name_size |
+class ChromeCounter(object): |
+ """A pointer to a single counter withing a binary counters file.""" |
+ |
+ def __init__(self, data, name_offset, value_offset): |
+ """Create a new instance. |
+ |
+ Args: |
+ data: the shared data access object containing the counter |
+ name_offset: the byte offset of the start of this counter's name |
+ value_offset: the byte offset of the start of this counter's value |
+ """ |
+ self.data = data |
+ self.name_offset = name_offset |
+ self.value_offset = value_offset |
+ |
+ def Value(self): |
+ """Return the integer value of this counter.""" |
+ return self.data.IntAt(self.value_offset) |
+ |
+ def Name(self): |
+ """Return the ascii name of this counter.""" |
+ result = "" |
+ index = self.name_offset |
+ current = self.data.ByteAt(index) |
+ while current: |
+ result += chr(current) |
+ index += 1 |
+ current = self.data.ByteAt(index) |
+ return result |
+ |
+ |
+class ChromeCounterCollection(object): |
+ """An overlay over a counters file that provides access to the |
+ individual counters contained in the file.""" |
+ |
+ _HEADER_SIZE = 4 * 4 |
+ _NAME_SIZE = 32 |
+ |
+ def __init__(self, data): |
+ """Create a new instance. |
+ |
+ Args: |
+ data: the shared data access object |
+ """ |
+ self.data = data |
+ self.max_counters = data.IntAt(8) |
+ self.max_threads = data.IntAt(12) |
+ self.counter_names_offset = \ |
+ self._HEADER_SIZE + self.max_threads * (self._NAME_SIZE + 2 * 4) |
+ self.counter_values_offset = \ |
+ self.counter_names_offset + self.max_counters * self._NAME_SIZE |
+ |
+ def CountersInUse(self): |
+ """Return the number of counters in active use.""" |
+ for i in xrange(self.max_counters): |
+ if self.data.ByteAt(self.counter_names_offset + i * self._NAME_SIZE) == 0: |
+ return i |
+ return self.max_counters |
+ |
+ def Counter(self, i): |
+ """Return the i'th counter.""" |
+ return ChromeCounter(self.data, |
+ self.counter_names_offset + i * self._NAME_SIZE, |
+ self.counter_values_offset + i * self.max_threads * 4) |
+ |
+ |
def Main(data_file): |
"""Run the stats counter. |