OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Inspection of the prefetch predictor database. | 6 """Inspection of the prefetch predictor database. |
7 | 7 |
8 On Android, the database can be extracted using: | 8 On Android, the database can be extracted using: |
9 adb pull \ | 9 adb pull \ |
10 '/data/user/0/$package_name/app_chrome/Default/Network Action Predictor' | 10 '/data/user/0/$package_name/app_chrome/Default/Network Action Predictor' |
11 predictor_db | 11 predictor_db |
12 """ | 12 """ |
13 | 13 |
14 import argparse | 14 import argparse |
| 15 import datetime |
15 import sqlite3 | 16 import sqlite3 |
16 import os | 17 import os |
17 | 18 import sys |
18 from resource_prefetch_predictor_pb2 import (PrefetchData, ResourceData) | |
19 | 19 |
20 | 20 |
21 class Entry(object): | 21 class Entry(object): |
22 """Represents an entry in the predictor database.""" | 22 """Represents an entry in the predictor database.""" |
23 def __init__( | 23 def __init__( |
24 self, primary_key, proto_buffer): | 24 self, primary_key, proto_buffer): |
| 25 from resource_prefetch_predictor_pb2 import (PrefetchData, ResourceData) |
25 self.primary_key = primary_key | 26 self.primary_key = primary_key |
26 self.prefetch_data = PrefetchData() | 27 self.prefetch_data = PrefetchData() |
27 self.prefetch_data.ParseFromString(proto_buffer) | 28 self.prefetch_data.ParseFromString(proto_buffer) |
28 | 29 |
29 @classmethod | 30 @classmethod |
30 def _ComputeResourceScore(cls, resource): | 31 def _ComputeResourceScore(cls, resource): |
31 """Mirrors ResourcePrefetchPredictorTables::ComputeResourceScore. | 32 """Mirrors ResourcePrefetchPredictorTables::ComputeResourceScore. |
32 | 33 |
33 Args: | 34 Args: |
34 resource: ResourceData. | 35 resource: ResourceData. |
35 | 36 |
36 Return: | 37 Return: |
37 The resource score (int). | 38 The resource score (int). |
38 """ | 39 """ |
| 40 from resource_prefetch_predictor_pb2 import (PrefetchData, ResourceData) |
39 priority_multiplier = 1 | 41 priority_multiplier = 1 |
40 type_multiplier = 1 | 42 type_multiplier = 1 |
41 | 43 |
42 if resource.priority == ResourceData.REQUEST_PRIORITY_HIGHEST: | 44 if resource.priority == ResourceData.REQUEST_PRIORITY_HIGHEST: |
43 priority_multiplier = 3 | 45 priority_multiplier = 3 |
44 elif resource.priority == ResourceData.REQUEST_PRIORITY_MEDIUM: | 46 elif resource.priority == ResourceData.REQUEST_PRIORITY_MEDIUM: |
45 priority_multiplier = 2 | 47 priority_multiplier = 2 |
46 | 48 |
47 if resource.resource_type in (ResourceData.RESOURCE_TYPE_STYLESHEET, | 49 if resource.resource_type in (ResourceData.RESOURCE_TYPE_STYLESHEET, |
48 ResourceData.RESOURCE_TYPE_SCRIPT): | 50 ResourceData.RESOURCE_TYPE_SCRIPT): |
(...skipping 22 matching lines...) Expand all Loading... |
71 def PrettyPrintCandidates(self): | 73 def PrettyPrintCandidates(self): |
72 """Prints the candidates for prefetch.""" | 74 """Prints the candidates for prefetch.""" |
73 print 'primary_key: %s' % self.prefetch_data.primary_key | 75 print 'primary_key: %s' % self.prefetch_data.primary_key |
74 for resource in self.prefetch_data.resources: | 76 for resource in self.prefetch_data.resources: |
75 confidence = float(resource.number_of_hits) / ( | 77 confidence = float(resource.number_of_hits) / ( |
76 resource.number_of_hits + resource.number_of_misses) | 78 resource.number_of_hits + resource.number_of_misses) |
77 if resource.number_of_hits < 2 or confidence < .7: | 79 if resource.number_of_hits < 2 or confidence < .7: |
78 continue | 80 continue |
79 self._PrettyPrintResource(resource) | 81 self._PrettyPrintResource(resource) |
80 | 82 |
| 83 def DumpOriginDatabaseRow(domain, primary_key, proto): |
| 84 from resource_prefetch_predictor_pb2 import OriginData |
| 85 entry = OriginData() |
| 86 entry.ParseFromString(proto) |
| 87 # For the offset, see kWindowsEpochDeltaMicroseconds in |
| 88 # base/time/time_posix.cc. |
| 89 last_visit_timestamp = int(entry.last_visit_time / 1e6 - 11644473600) |
| 90 formatted_last_visit_time = datetime.datetime.utcfromtimestamp( |
| 91 last_visit_timestamp).strftime('%Y-%m-%d %H:%M:%S') |
| 92 print '''host: %s |
| 93 last_visit_time: %s |
| 94 origins:''' % (entry.host, formatted_last_visit_time) |
| 95 for origin_stat in entry.origins: |
| 96 print ''' origin: %s |
| 97 number_of_hits: %d |
| 98 number_of_misses: %d |
| 99 consecutive_misses: %d |
| 100 average_position: %f |
| 101 always_access_network: %s |
| 102 accessed_network: %s |
| 103 ''' % (origin_stat.origin, origin_stat.number_of_hits, |
| 104 origin_stat.number_of_misses, origin_stat.consecutive_misses, |
| 105 origin_stat.average_position, origin_stat.always_access_network, |
| 106 origin_stat.accessed_network) |
| 107 |
| 108 |
81 # The version of python sqlite3 library we have in Ubuntu 14.04 LTS doesn't | 109 # The version of python sqlite3 library we have in Ubuntu 14.04 LTS doesn't |
82 # support views but command line util does. | 110 # support views but command line util does. |
83 # TODO(alexilin): get rid of this when python sqlite3 adds view support. | 111 # TODO(alexilin): get rid of this when python sqlite3 adds view support. |
84 def CreateCompatibleDatabaseCopy(filename): | 112 def CreateCompatibleDatabaseCopy(filename): |
85 import tempfile, shutil, subprocess | 113 import tempfile, shutil, subprocess |
86 _, tmpfile = tempfile.mkstemp() | 114 _, tmpfile = tempfile.mkstemp() |
87 shutil.copy2(filename, tmpfile) | 115 shutil.copy2(filename, tmpfile) |
88 subprocess.call(['sqlite3', tmpfile, 'DROP VIEW MmapStatus']) | 116 subprocess.call(['sqlite3', tmpfile, 'DROP VIEW MmapStatus']) |
89 return tmpfile | 117 return tmpfile |
90 | 118 |
91 def DatabaseStats(filename, domain): | 119 def DatabaseStats(filename, host): |
| 120 query_template = 'SELECT key, proto from %s' |
92 connection = sqlite3.connect(filename) | 121 connection = sqlite3.connect(filename) |
93 c = connection.cursor() | 122 c = connection.cursor() |
94 query = ('SELECT key, proto FROM resource_prefetch_predictor_host') | 123 print 'HOST DATABASE' |
| 124 query = query_template % 'resource_prefetch_predictor_host' |
95 entries = [Entry.FromRow(row) for row in c.execute(query)] | 125 entries = [Entry.FromRow(row) for row in c.execute(query)] |
96 for x in entries: | 126 for x in entries: |
97 if domain is None or x.primary_key == domain: | 127 if host is None or x.primary_key == host: |
98 x.PrettyPrintCandidates() | 128 x.PrettyPrintCandidates() |
99 | 129 |
| 130 print '\n\nORIGIN DATABASE' |
| 131 query = query_template % 'resource_prefetch_predictor_origin' |
| 132 rows = [row for row in c.execute(query) |
| 133 if host is None or row.primary_key == host] |
| 134 for row in rows: |
| 135 DumpOriginDatabaseRow(host, *row) |
| 136 |
| 137 |
| 138 def _AddProtocolBuffersPath(build_dir): |
| 139 assert os.path.isdir(build_dir) |
| 140 proto_dir = os.path.join( |
| 141 build_dir, os.path.join('pyproto', 'chrome', 'browser', 'predictors')) |
| 142 sys.path.append(proto_dir) |
| 143 |
100 | 144 |
101 def main(): | 145 def main(): |
102 parser = argparse.ArgumentParser() | 146 parser = argparse.ArgumentParser() |
103 parser.add_argument('-f', dest='database_filename', required=True, | 147 parser.add_argument('-f', dest='database_filename', required=True, |
104 help='Path to the database') | 148 help='Path to the database') |
105 parser.add_argument('-d', dest='domain', default=None, help='Domain') | 149 parser.add_argument('-d', dest='domain', default=None, help='Domain') |
| 150 parser.add_argument('--build-dir', dest='build_dir', required=True, |
| 151 help='Path to the build directory.') |
106 args = parser.parse_args() | 152 args = parser.parse_args() |
| 153 _AddProtocolBuffersPath(args.build_dir) |
| 154 |
107 try: | 155 try: |
108 database_copy = CreateCompatibleDatabaseCopy(args.database_filename) | 156 database_copy = CreateCompatibleDatabaseCopy(args.database_filename) |
109 DatabaseStats(database_copy, args.domain) | 157 DatabaseStats(database_copy, args.domain) |
110 finally: | 158 finally: |
111 if os.path.exists(database_copy): | 159 if os.path.exists(database_copy): |
112 os.remove(database_copy) | 160 os.remove(database_copy) |
113 | 161 |
114 | 162 |
115 if __name__ == '__main__': | 163 if __name__ == '__main__': |
116 main() | 164 main() |
OLD | NEW |