OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import posixpath | 5 import posixpath |
6 import re | 6 import re |
7 | 7 |
8 from telemetry.timeline import event as timeline_event | 8 from telemetry.timeline import event as timeline_event |
9 | 9 |
10 | 10 |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 return self._bucket[name] | 133 return self._bucket[name] |
134 | 134 |
135 | 135 |
136 class ProcessMemoryDumpEvent(timeline_event.TimelineEvent): | 136 class ProcessMemoryDumpEvent(timeline_event.TimelineEvent): |
137 """A memory dump event belonging to a single timeline.Process object. | 137 """A memory dump event belonging to a single timeline.Process object. |
138 | 138 |
139 It's a subclass of telemetry's TimelineEvent so it can be included in | 139 It's a subclass of telemetry's TimelineEvent so it can be included in |
140 the stream of events contained in timeline.model objects, and have its | 140 the stream of events contained in timeline.model objects, and have its |
141 timing correlated with that of other events in the model. | 141 timing correlated with that of other events in the model. |
142 | 142 |
| 143 Args: |
| 144 process: The Process object associated with the memory dump. |
| 145 dump_events: A list of dump events of the process with the same dump id. |
| 146 |
143 Properties: | 147 Properties: |
144 dump_id: A string to identify events belonging to the same global dump. | 148 dump_id: A string to identify events belonging to the same global dump. |
145 process: The timeline.Process object that owns this memory dump event. | 149 process: The timeline.Process object that owns this memory dump event. |
146 has_mmaps: True if the memory dump has mmaps information. If False then | 150 has_mmaps: True if the memory dump has mmaps information. If False then |
147 GetMemoryUsage will report all zeros. | 151 GetMemoryUsage will report all zeros. |
148 """ | 152 """ |
149 def __init__(self, process, event): | 153 def __init__(self, process, dump_events): |
150 assert event['ph'] == 'v' and process.pid == event['pid'] | 154 assert dump_events |
151 | 155 |
152 super(ProcessMemoryDumpEvent, self).__init__( | 156 start_time = min(event['ts'] for event in dump_events) / 1000.0 |
153 'memory', 'memory_dump', event['ts'] / 1000.0, 0.0) | 157 duration = max(event['ts'] for event in dump_events) / 1000.0 - start_time |
| 158 super(ProcessMemoryDumpEvent, self).__init__('memory', 'memory_dump', |
| 159 start_time, duration) |
154 | 160 |
155 self.process = process | 161 self.process = process |
156 self.dump_id = event['id'] | 162 self.dump_id = dump_events[0]['id'] |
157 | 163 |
158 try: | 164 allocator_dumps = {} |
159 allocators_dict = event['args']['dumps']['allocators'] | 165 vm_regions = [] |
160 except KeyError: | 166 for event in dump_events: |
161 allocators_dict = {} | 167 assert (event['ph'] == 'v' and self.process.pid == event['pid'] and |
| 168 self.dump_id == event['id']) |
| 169 try: |
| 170 allocator_dumps.update(event['args']['dumps']['allocators']) |
| 171 except KeyError: |
| 172 pass # It's ok if any of those keys are not present. |
| 173 try: |
| 174 value = event['args']['dumps']['process_mmaps']['vm_regions'] |
| 175 assert not vm_regions |
| 176 vm_regions = value |
| 177 except KeyError: |
| 178 pass # It's ok if any of those keys are not present. |
| 179 |
162 self._allocators = {} | 180 self._allocators = {} |
163 parent_path = '' | 181 parent_path = '' |
164 parent_has_size = False | 182 parent_has_size = False |
165 for allocator_name, size_values in sorted(allocators_dict.iteritems()): | 183 for allocator_name, size_values in sorted(allocator_dumps.iteritems()): |
166 if ((allocator_name.startswith(parent_path) and parent_has_size) | 184 if ((allocator_name.startswith(parent_path) and parent_has_size) or |
167 or allocator_name.startswith('global/')): | 185 allocator_name.startswith('global/')): |
168 continue | 186 continue |
169 parent_path = allocator_name + '/' | 187 parent_path = allocator_name + '/' |
170 parent_has_size = 'size' in size_values['attrs'] | 188 parent_has_size = 'size' in size_values['attrs'] |
171 name_parts = allocator_name.split('/') | 189 name_parts = allocator_name.split('/') |
172 allocator_name = name_parts[0] | 190 allocator_name = name_parts[0] |
173 # For 'gpu/android_memtrack/*' we want to keep track of individual | 191 # For 'gpu/android_memtrack/*' we want to keep track of individual |
174 # components. E.g. 'gpu/android_memtrack/gl' will be stored as | 192 # components. E.g. 'gpu/android_memtrack/gl' will be stored as |
175 # 'android_memtrack_gl' in the allocators dict. | 193 # 'android_memtrack_gl' in the allocators dict. |
176 if (len(name_parts) == 3 and allocator_name == 'gpu' | 194 if (len(name_parts) == 3 and allocator_name == 'gpu' and |
177 and name_parts[1] == 'android_memtrack'): | 195 name_parts[1] == 'android_memtrack'): |
178 allocator_name = '_'.join(name_parts[1:3]) | 196 allocator_name = '_'.join(name_parts[1:3]) |
179 allocator = self._allocators.setdefault(allocator_name, {}) | 197 allocator = self._allocators.setdefault(allocator_name, {}) |
180 for size_key, size_value in size_values['attrs'].iteritems(): | 198 for size_key, size_value in size_values['attrs'].iteritems(): |
181 if size_value['units'] == 'bytes': | 199 if size_value['units'] == 'bytes': |
182 allocator[size_key] = (allocator.get(size_key, 0) | 200 allocator[size_key] = (allocator.get(size_key, 0) |
183 + int(size_value['value'], 16)) | 201 + int(size_value['value'], 16)) |
184 # we need to discount tracing from malloc size. | 202 # we need to discount tracing from malloc size. |
185 try: | 203 try: |
186 self._allocators['malloc']['size'] -= self._allocators['tracing']['size'] | 204 self._allocators['malloc']['size'] -= self._allocators['tracing']['size'] |
187 except KeyError: | 205 except KeyError: |
188 pass # it's ok if any of those keys are not present | 206 pass # It's ok if any of those keys are not present. |
189 | 207 |
| 208 self.has_mmaps = bool(vm_regions) |
190 self._buckets = {} | 209 self._buckets = {} |
191 try: | |
192 vm_regions = event['args']['dumps']['process_mmaps']['vm_regions'] | |
193 except KeyError: | |
194 vm_regions = [] | |
195 self.has_mmaps = bool(vm_regions) | |
196 for vm_region in vm_regions: | 210 for vm_region in vm_regions: |
197 self._AddRegion(vm_region) | 211 self._AddRegion(vm_region) |
198 | 212 |
199 @property | 213 @property |
200 def process_name(self): | 214 def process_name(self): |
201 return self.process.name | 215 return self.process.name |
202 | 216 |
203 def _AddRegion(self, vm_region): | 217 def _AddRegion(self, vm_region): |
204 path = '' | 218 path = '' |
205 category = ROOT_CATEGORY | 219 category = ROOT_CATEGORY |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 have_mmaps = set(dump.has_mmaps for dump in self._process_dumps) | 304 have_mmaps = set(dump.has_mmaps for dump in self._process_dumps) |
291 assert len(have_mmaps) == 1 | 305 assert len(have_mmaps) == 1 |
292 self.has_mmaps = have_mmaps.pop() | 306 self.has_mmaps = have_mmaps.pop() |
293 | 307 |
294 @property | 308 @property |
295 def start(self): | 309 def start(self): |
296 return self._process_dumps[0].start | 310 return self._process_dumps[0].start |
297 | 311 |
298 @property | 312 @property |
299 def end(self): | 313 def end(self): |
300 return self._process_dumps[-1].start | 314 return max(dump.end for dump in self._process_dumps) |
301 | 315 |
302 @property | 316 @property |
303 def duration(self): | 317 def duration(self): |
304 return self.end - self.start | 318 return self.end - self.start |
305 | 319 |
306 def IterProcessMemoryDumps(self): | 320 def IterProcessMemoryDumps(self): |
307 return iter(self._process_dumps) | 321 return iter(self._process_dumps) |
308 | 322 |
309 def __repr__(self): | 323 def __repr__(self): |
310 values = ['id=%s' % self.dump_id] | 324 values = ['id=%s' % self.dump_id] |
311 for key, value in sorted(self.GetMemoryUsage().iteritems()): | 325 for key, value in sorted(self.GetMemoryUsage().iteritems()): |
312 values.append('%s=%d' % (key, value)) | 326 values.append('%s=%d' % (key, value)) |
313 values = ', '.join(values) | 327 values = ', '.join(values) |
314 return '%s[%s]' % (type(self).__name__, values) | 328 return '%s[%s]' % (type(self).__name__, values) |
315 | 329 |
316 def GetMemoryUsage(self): | 330 def GetMemoryUsage(self): |
317 """Get the aggregated memory usage over all processes in this dump.""" | 331 """Get the aggregated memory usage over all processes in this dump.""" |
318 result = {} | 332 result = {} |
319 for dump in self._process_dumps: | 333 for dump in self._process_dumps: |
320 for key, value in dump.GetMemoryUsage().iteritems(): | 334 for key, value in dump.GetMemoryUsage().iteritems(): |
321 result[key] = result.get(key, 0) + value | 335 result[key] = result.get(key, 0) + value |
322 return result | 336 return result |
OLD | NEW |