1#!/usr/bin/env python 2 3# Copyright (c) 2012 ARM Limited 4# All rights reserved 5# 6# The license below extends only to copyright in the software and shall 7# not be construed as granting a license to any other intellectual 8# property including but not limited to intellectual property relating 9# to a hardware implementation of the functionality of the software 10# licensed hereunder. You may use the software subject to the license 11# terms below provided that you ensure that this notice is replicated 12# unmodified and in its entirety in all distributions of the software, 13# modified or unmodified, in source code or in binary form. 14# 15# Redistribution and use in source and binary forms, with or without 16# modification, are permitted provided that the following conditions are 17# met: redistributions of source code must retain the above copyright 18# notice, this list of conditions and the following disclaimer; 19# redistributions in binary form must reproduce the above copyright 20# notice, this list of conditions and the following disclaimer in the 21# documentation and/or other materials provided with the distribution; 22# neither the name of the copyright holders nor the names of its 23# contributors may be used to endorse or promote products derived from 24# this software without specific prior written permission. 25# 26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37# 38# Author: Dam Sunwoo 39# 40 41# This script converts gem5 output to ARM DS-5 Streamline .apc project file 42# (Requires the gem5 runs to be run with ContextSwitchStatsDump enabled and 43# some patches applied to target Linux kernel.) 44# Visit http://www.gem5.org/Streamline for more details. 45# 46# Usage: 47# m5stats2streamline.py <stat_config.ini> <gem5 run folder> <dest .apc folder> 48# 49# <stat_config.ini>: .ini file that describes which stats to be included 50# in conversion. Sample .ini files can be found in 51# util/streamline. 52# NOTE: this is NOT the gem5 config.ini file. 53# 54# <gem5 run folder>: Path to gem5 run folder (must contain config.ini, 55# stats.txt[.gz], and system.tasks.txt.) 56# 57# <dest .apc folder>: Destination .apc folder path 58#
| 1#!/usr/bin/env python 2 3# Copyright (c) 2012 ARM Limited 4# All rights reserved 5# 6# The license below extends only to copyright in the software and shall 7# not be construed as granting a license to any other intellectual 8# property including but not limited to intellectual property relating 9# to a hardware implementation of the functionality of the software 10# licensed hereunder. You may use the software subject to the license 11# terms below provided that you ensure that this notice is replicated 12# unmodified and in its entirety in all distributions of the software, 13# modified or unmodified, in source code or in binary form. 14# 15# Redistribution and use in source and binary forms, with or without 16# modification, are permitted provided that the following conditions are 17# met: redistributions of source code must retain the above copyright 18# notice, this list of conditions and the following disclaimer; 19# redistributions in binary form must reproduce the above copyright 20# notice, this list of conditions and the following disclaimer in the 21# documentation and/or other materials provided with the distribution; 22# neither the name of the copyright holders nor the names of its 23# contributors may be used to endorse or promote products derived from 24# this software without specific prior written permission. 25# 26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37# 38# Author: Dam Sunwoo 39# 40 41# This script converts gem5 output to ARM DS-5 Streamline .apc project file 42# (Requires the gem5 runs to be run with ContextSwitchStatsDump enabled and 43# some patches applied to target Linux kernel.) 44# Visit http://www.gem5.org/Streamline for more details. 45# 46# Usage: 47# m5stats2streamline.py <stat_config.ini> <gem5 run folder> <dest .apc folder> 48# 49# <stat_config.ini>: .ini file that describes which stats to be included 50# in conversion. Sample .ini files can be found in 51# util/streamline. 52# NOTE: this is NOT the gem5 config.ini file. 53# 54# <gem5 run folder>: Path to gem5 run folder (must contain config.ini, 55# stats.txt[.gz], and system.tasks.txt.) 56# 57# <dest .apc folder>: Destination .apc folder path 58#
|
59# APC project generation based on Gator v12 (DS-5 v5.13)
| 59# APC project generation based on Gator v17 (DS-5 v5.17)
|
60# Subsequent versions should be backward compatible 61 62import re, sys, os 63from ConfigParser import ConfigParser 64import gzip 65import xml.etree.ElementTree as ET 66import xml.dom.minidom as minidom 67import shutil 68import zlib 69 70import argparse 71 72parser = argparse.ArgumentParser( 73 formatter_class=argparse.RawDescriptionHelpFormatter, 74 description=""" 75 Converts gem5 runs to ARM DS-5 Streamline .apc project file. 76 (NOTE: Requires gem5 runs to be run with ContextSwitchStatsDump 77 enabled and some patches applied to the target Linux kernel.) 78 79 Visit http://www.gem5.org/Streamline for more details. 80
| 60# Subsequent versions should be backward compatible 61 62import re, sys, os 63from ConfigParser import ConfigParser 64import gzip 65import xml.etree.ElementTree as ET 66import xml.dom.minidom as minidom 67import shutil 68import zlib 69 70import argparse 71 72parser = argparse.ArgumentParser( 73 formatter_class=argparse.RawDescriptionHelpFormatter, 74 description=""" 75 Converts gem5 runs to ARM DS-5 Streamline .apc project file. 76 (NOTE: Requires gem5 runs to be run with ContextSwitchStatsDump 77 enabled and some patches applied to the target Linux kernel.) 78 79 Visit http://www.gem5.org/Streamline for more details. 80
|
81 APC project generation based on Gator v12 (DS-5 v5.13)
| 81 APC project generation based on Gator v17 (DS-5 v5.17)
|
82 Subsequent versions should be backward compatible 83 """) 84 85parser.add_argument("stat_config_file", metavar="<stat_config.ini>", 86 help=".ini file that describes which stats to be included \ 87 in conversion. Sample .ini files can be found in \ 88 util/streamline. NOTE: this is NOT the gem5 config.ini \ 89 file.") 90 91parser.add_argument("input_path", metavar="<gem5 run folder>", 92 help="Path to gem5 run folder (must contain config.ini, \ 93 stats.txt[.gz], and system.tasks.txt.)") 94 95parser.add_argument("output_path", metavar="<dest .apc folder>", 96 help="Destination .apc folder path") 97 98parser.add_argument("--num-events", action="store", type=int, 99 default=1000000, 100 help="Maximum number of scheduling (context switch) \ 101 events to be processed. Set to truncate early. \ 102 Default=1000000") 103 104parser.add_argument("--gzipped-bmp-not-supported", action="store_true", 105 help="Do not use gzipped .bmp files for visual annotations. \ 106 This option is only required when using Streamline versions \ 107 older than 5.14") 108 109parser.add_argument("--verbose", action="store_true", 110 help="Enable verbose output") 111 112args = parser.parse_args() 113 114if not re.match("(.*)\.apc", args.output_path): 115 print "ERROR: <dest .apc folder> should end with '.apc'!" 116 sys.exit(1) 117 118# gzipped BMP files for visual annotation is supported in Streamline 5.14. 119# Setting this to True will significantly compress the .apc binary file that 120# includes frame buffer snapshots. 121gzipped_bmp_supported = not args.gzipped_bmp_not_supported 122 123ticks_in_ns = -1 124 125# Default max # of events. Increase this for longer runs. 126num_events = args.num_events 127 128start_tick = -1 129end_tick = -1 130 131# Parse gem5 config.ini file to determine some system configurations. 132# Number of CPUs, L2s, etc. 133def parseConfig(config_file): 134 global num_cpus, num_l2 135 136 print "\n===============================" 137 print "Parsing gem5 config.ini file..." 138 print config_file 139 print "===============================\n" 140 config = ConfigParser() 141 if not config.read(config_file): 142 print "ERROR: config file '", config_file, "' not found" 143 sys.exit(1) 144 145 if config.has_section("system.cpu"): 146 num_cpus = 1 147 else: 148 num_cpus = 0 149 while config.has_section("system.cpu" + str(num_cpus)): 150 num_cpus += 1 151 152 if config.has_section("system.l2"): 153 num_l2 = 1 154 else: 155 num_l2 = 0 156 while config.has_section("system.l2" + str(num_l2)): 157 num_l2 += 1 158 159 print "Num CPUs:", num_cpus 160 print "Num L2s:", num_l2 161 print "" 162 163 return (num_cpus, num_l2) 164 165 166process_dict = {} 167thread_dict = {} 168 169process_list = [] 170 171idle_uid = -1 172kernel_uid = -1 173 174class Task(object): 175 def __init__(self, uid, pid, tgid, task_name, is_process, tick): 176 if pid == 0: # Idle 177 self.uid = 0 178 elif pid == -1: # Kernel 179 self.uid = 0 180 else: 181 self.uid = uid 182 self.pid = pid 183 self.tgid = tgid 184 self.is_process = is_process 185 self.task_name = task_name 186 self.children = [] 187 self.tick = tick # time this task first appeared 188 189class Event(object): 190 def __init__(self, tick, task): 191 self.tick = tick 192 self.task = task 193 194############################################################ 195# Types used in APC Protocol 196# - packed32, packed64 197# - int32 198# - string 199############################################################ 200
| 82 Subsequent versions should be backward compatible 83 """) 84 85parser.add_argument("stat_config_file", metavar="<stat_config.ini>", 86 help=".ini file that describes which stats to be included \ 87 in conversion. Sample .ini files can be found in \ 88 util/streamline. NOTE: this is NOT the gem5 config.ini \ 89 file.") 90 91parser.add_argument("input_path", metavar="<gem5 run folder>", 92 help="Path to gem5 run folder (must contain config.ini, \ 93 stats.txt[.gz], and system.tasks.txt.)") 94 95parser.add_argument("output_path", metavar="<dest .apc folder>", 96 help="Destination .apc folder path") 97 98parser.add_argument("--num-events", action="store", type=int, 99 default=1000000, 100 help="Maximum number of scheduling (context switch) \ 101 events to be processed. Set to truncate early. \ 102 Default=1000000") 103 104parser.add_argument("--gzipped-bmp-not-supported", action="store_true", 105 help="Do not use gzipped .bmp files for visual annotations. \ 106 This option is only required when using Streamline versions \ 107 older than 5.14") 108 109parser.add_argument("--verbose", action="store_true", 110 help="Enable verbose output") 111 112args = parser.parse_args() 113 114if not re.match("(.*)\.apc", args.output_path): 115 print "ERROR: <dest .apc folder> should end with '.apc'!" 116 sys.exit(1) 117 118# gzipped BMP files for visual annotation is supported in Streamline 5.14. 119# Setting this to True will significantly compress the .apc binary file that 120# includes frame buffer snapshots. 121gzipped_bmp_supported = not args.gzipped_bmp_not_supported 122 123ticks_in_ns = -1 124 125# Default max # of events. Increase this for longer runs. 126num_events = args.num_events 127 128start_tick = -1 129end_tick = -1 130 131# Parse gem5 config.ini file to determine some system configurations. 132# Number of CPUs, L2s, etc. 133def parseConfig(config_file): 134 global num_cpus, num_l2 135 136 print "\n===============================" 137 print "Parsing gem5 config.ini file..." 138 print config_file 139 print "===============================\n" 140 config = ConfigParser() 141 if not config.read(config_file): 142 print "ERROR: config file '", config_file, "' not found" 143 sys.exit(1) 144 145 if config.has_section("system.cpu"): 146 num_cpus = 1 147 else: 148 num_cpus = 0 149 while config.has_section("system.cpu" + str(num_cpus)): 150 num_cpus += 1 151 152 if config.has_section("system.l2"): 153 num_l2 = 1 154 else: 155 num_l2 = 0 156 while config.has_section("system.l2" + str(num_l2)): 157 num_l2 += 1 158 159 print "Num CPUs:", num_cpus 160 print "Num L2s:", num_l2 161 print "" 162 163 return (num_cpus, num_l2) 164 165 166process_dict = {} 167thread_dict = {} 168 169process_list = [] 170 171idle_uid = -1 172kernel_uid = -1 173 174class Task(object): 175 def __init__(self, uid, pid, tgid, task_name, is_process, tick): 176 if pid == 0: # Idle 177 self.uid = 0 178 elif pid == -1: # Kernel 179 self.uid = 0 180 else: 181 self.uid = uid 182 self.pid = pid 183 self.tgid = tgid 184 self.is_process = is_process 185 self.task_name = task_name 186 self.children = [] 187 self.tick = tick # time this task first appeared 188 189class Event(object): 190 def __init__(self, tick, task): 191 self.tick = tick 192 self.task = task 193 194############################################################ 195# Types used in APC Protocol 196# - packed32, packed64 197# - int32 198# - string 199############################################################ 200
|
201# variable length packed 4-byte signed value
| |
202def packed32(x): 203 ret = []
| 201def packed32(x): 202 ret = []
|
| 203 more = True 204 while more: 205 b = x & 0x7f 206 x = x >> 7 207 if (((x == 0) and ((b & 0x40) == 0)) or \ 208 ((x == -1) and ((b & 0x40) != 0))): 209 more = False 210 else: 211 b = b | 0x80 212 ret.append(b) 213 return ret 214 215# For historical reasons, 32/64-bit versions of functions are presevered 216def packed64(x): 217 return packed32(x) 218 219# variable length packed 4-byte signed value 220def unsigned_packed32(x): 221 ret = []
|
204 if ((x & 0xffffff80) == 0): 205 ret.append(x & 0x7f) 206 elif ((x & 0xffffc000) == 0): 207 ret.append((x | 0x80) & 0xff) 208 ret.append((x >> 7) & 0x7f) 209 elif ((x & 0xffe00000) == 0): 210 ret.append((x | 0x80) & 0xff) 211 ret.append(((x >> 7) | 0x80) & 0xff) 212 ret.append((x >> 14) & 0x7f) 213 elif ((x & 0xf0000000) == 0): 214 ret.append((x | 0x80) & 0xff) 215 ret.append(((x >> 7) | 0x80) & 0xff) 216 ret.append(((x >> 14) | 0x80) & 0xff) 217 ret.append((x >> 21) & 0x7f) 218 else: 219 ret.append((x | 0x80) & 0xff) 220 ret.append(((x >> 7) | 0x80) & 0xff) 221 ret.append(((x >> 14) | 0x80) & 0xff) 222 ret.append(((x >> 21) | 0x80) & 0xff) 223 ret.append((x >> 28) & 0x0f) 224 return ret 225 226# variable length packed 8-byte signed value
| 222 if ((x & 0xffffff80) == 0): 223 ret.append(x & 0x7f) 224 elif ((x & 0xffffc000) == 0): 225 ret.append((x | 0x80) & 0xff) 226 ret.append((x >> 7) & 0x7f) 227 elif ((x & 0xffe00000) == 0): 228 ret.append((x | 0x80) & 0xff) 229 ret.append(((x >> 7) | 0x80) & 0xff) 230 ret.append((x >> 14) & 0x7f) 231 elif ((x & 0xf0000000) == 0): 232 ret.append((x | 0x80) & 0xff) 233 ret.append(((x >> 7) | 0x80) & 0xff) 234 ret.append(((x >> 14) | 0x80) & 0xff) 235 ret.append((x >> 21) & 0x7f) 236 else: 237 ret.append((x | 0x80) & 0xff) 238 ret.append(((x >> 7) | 0x80) & 0xff) 239 ret.append(((x >> 14) | 0x80) & 0xff) 240 ret.append(((x >> 21) | 0x80) & 0xff) 241 ret.append((x >> 28) & 0x0f) 242 return ret 243 244# variable length packed 8-byte signed value
|
227def packed64(x):
| 245def unsigned_packed64(x):
|
228 ret = [] 229 if ((x & 0xffffffffffffff80) == 0): 230 ret.append(x & 0x7f) 231 elif ((x & 0xffffffffffffc000) == 0): 232 ret.append((x | 0x80) & 0xff) 233 ret.append((x >> 7) & 0x7f) 234 elif ((x & 0xffffffffffe00000) == 0): 235 ret.append((x | 0x80) & 0xff) 236 ret.append(((x >> 7) | 0x80) & 0xff) 237 ret.append((x >> 14) & 0x7f) 238 elif ((x & 0xfffffffff0000000) == 0): 239 ret.append((x | 0x80) & 0xff) 240 ret.append(((x >> 7) | 0x80) & 0xff) 241 ret.append(((x >> 14) | 0x80) & 0xff) 242 ret.append((x >> 21) & 0x7f) 243 elif ((x & 0xfffffff800000000) == 0): 244 ret.append((x | 0x80) & 0xff) 245 ret.append(((x >> 7) | 0x80) & 0xff) 246 ret.append(((x >> 14) | 0x80) & 0xff) 247 ret.append(((x >> 21) | 0x80) & 0xff) 248 ret.append((x >> 28) & 0x7f) 249 elif ((x & 0xfffffc0000000000) == 0): 250 ret.append((x | 0x80) & 0xff) 251 ret.append(((x >> 7) | 0x80) & 0xff) 252 ret.append(((x >> 14) | 0x80) & 0xff) 253 ret.append(((x >> 21) | 0x80) & 0xff) 254 ret.append(((x >> 28) | 0x80) & 0xff) 255 ret.append((x >> 35) & 0x7f) 256 elif ((x & 0xfffe000000000000) == 0): 257 ret.append((x | 0x80) & 0xff) 258 ret.append(((x >> 7) | 0x80) & 0xff) 259 ret.append(((x >> 14) | 0x80) & 0xff) 260 ret.append(((x >> 21) | 0x80) & 0xff) 261 ret.append(((x >> 28) | 0x80) & 0xff) 262 ret.append(((x >> 35) | 0x80) & 0xff) 263 ret.append((x >> 42) & 0x7f) 264 elif ((x & 0xff00000000000000) == 0): 265 ret.append((x | 0x80) & 0xff) 266 ret.append(((x >> 7) | 0x80) & 0xff) 267 ret.append(((x >> 14) | 0x80) & 0xff) 268 ret.append(((x >> 21) | 0x80) & 0xff) 269 ret.append(((x >> 28) | 0x80) & 0xff) 270 ret.append(((x >> 35) | 0x80) & 0xff) 271 ret.append(((x >> 42) | 0x80) & 0xff) 272 ret.append((x >> 49) & 0x7f) 273 elif ((x & 0x8000000000000000) == 0): 274 ret.append((x | 0x80) & 0xff) 275 ret.append(((x >> 7) | 0x80) & 0xff) 276 ret.append(((x >> 14) | 0x80) & 0xff) 277 ret.append(((x >> 21) | 0x80) & 0xff) 278 ret.append(((x >> 28) | 0x80) & 0xff) 279 ret.append(((x >> 35) | 0x80) & 0xff) 280 ret.append(((x >> 42) | 0x80) & 0xff) 281 ret.append(((x >> 49) | 0x80) & 0xff) 282 ret.append((x >> 56) & 0x7f) 283 else: 284 ret.append((x | 0x80) & 0xff) 285 ret.append(((x >> 7) | 0x80) & 0xff) 286 ret.append(((x >> 14) | 0x80) & 0xff) 287 ret.append(((x >> 21) | 0x80) & 0xff) 288 ret.append(((x >> 28) | 0x80) & 0xff) 289 ret.append(((x >> 35) | 0x80) & 0xff) 290 ret.append(((x >> 42) | 0x80) & 0xff) 291 ret.append(((x >> 49) | 0x80) & 0xff) 292 ret.append(((x >> 56) | 0x80) & 0xff) 293 ret.append((x >> 63) & 0x7f) 294 return ret 295 296# 4-byte signed little endian 297def int32(x): 298 ret = [] 299 ret.append(x & 0xff) 300 ret.append((x >> 8) & 0xff) 301 ret.append((x >> 16) & 0xff) 302 ret.append((x >> 24) & 0xff) 303 return ret 304 305# 2-byte signed little endian 306def int16(x): 307 ret = [] 308 ret.append(x & 0xff) 309 ret.append((x >> 8) & 0xff) 310 return ret 311 312# a packed32 length followed by the specified number of characters 313def stringList(x): 314 ret = [] 315 ret += packed32(len(x)) 316 for i in x: 317 ret.append(i) 318 return ret 319 320def utf8StringList(x): 321 ret = [] 322 for i in x: 323 ret.append(ord(i)) 324 return ret 325 326# packed64 time value in nanoseconds relative to the uptime from the 327# Summary message. 328def timestampList(x): 329 ret = packed64(x) 330 return ret 331 332 333############################################################ 334# Write binary 335############################################################ 336 337def writeBinary(outfile, binary_list): 338 for i in binary_list: 339 outfile.write("%c" % i) 340 341############################################################ 342# APC Protocol Frame Types 343############################################################ 344 345def addFrameHeader(frame_type, body, core): 346 ret = [] 347 348 if frame_type == "Summary": 349 code = 1 350 elif frame_type == "Backtrace": 351 code = 2 352 elif frame_type == "Name": 353 code = 3 354 elif frame_type == "Counter": 355 code = 4 356 elif frame_type == "Block Counter": 357 code = 5 358 elif frame_type == "Annotate": 359 code = 6 360 elif frame_type == "Sched Trace": 361 code = 7 362 elif frame_type == "GPU Trace": 363 code = 8 364 elif frame_type == "Idle": 365 code = 9 366 else: 367 print "ERROR: Unknown frame type:", frame_type 368 sys.exit(1) 369 370 packed_code = packed32(code) 371 372 packed_core = packed32(core) 373 374 length = int32(len(packed_code) + len(packed_core) + len(body)) 375 376 ret = length + packed_code + packed_core + body 377 return ret 378 379 380# Summary frame 381# - timestamp: packed64 382# - uptime: packed64 383def summaryFrame(timestamp, uptime): 384 frame_type = "Summary"
| 246 ret = [] 247 if ((x & 0xffffffffffffff80) == 0): 248 ret.append(x & 0x7f) 249 elif ((x & 0xffffffffffffc000) == 0): 250 ret.append((x | 0x80) & 0xff) 251 ret.append((x >> 7) & 0x7f) 252 elif ((x & 0xffffffffffe00000) == 0): 253 ret.append((x | 0x80) & 0xff) 254 ret.append(((x >> 7) | 0x80) & 0xff) 255 ret.append((x >> 14) & 0x7f) 256 elif ((x & 0xfffffffff0000000) == 0): 257 ret.append((x | 0x80) & 0xff) 258 ret.append(((x >> 7) | 0x80) & 0xff) 259 ret.append(((x >> 14) | 0x80) & 0xff) 260 ret.append((x >> 21) & 0x7f) 261 elif ((x & 0xfffffff800000000) == 0): 262 ret.append((x | 0x80) & 0xff) 263 ret.append(((x >> 7) | 0x80) & 0xff) 264 ret.append(((x >> 14) | 0x80) & 0xff) 265 ret.append(((x >> 21) | 0x80) & 0xff) 266 ret.append((x >> 28) & 0x7f) 267 elif ((x & 0xfffffc0000000000) == 0): 268 ret.append((x | 0x80) & 0xff) 269 ret.append(((x >> 7) | 0x80) & 0xff) 270 ret.append(((x >> 14) | 0x80) & 0xff) 271 ret.append(((x >> 21) | 0x80) & 0xff) 272 ret.append(((x >> 28) | 0x80) & 0xff) 273 ret.append((x >> 35) & 0x7f) 274 elif ((x & 0xfffe000000000000) == 0): 275 ret.append((x | 0x80) & 0xff) 276 ret.append(((x >> 7) | 0x80) & 0xff) 277 ret.append(((x >> 14) | 0x80) & 0xff) 278 ret.append(((x >> 21) | 0x80) & 0xff) 279 ret.append(((x >> 28) | 0x80) & 0xff) 280 ret.append(((x >> 35) | 0x80) & 0xff) 281 ret.append((x >> 42) & 0x7f) 282 elif ((x & 0xff00000000000000) == 0): 283 ret.append((x | 0x80) & 0xff) 284 ret.append(((x >> 7) | 0x80) & 0xff) 285 ret.append(((x >> 14) | 0x80) & 0xff) 286 ret.append(((x >> 21) | 0x80) & 0xff) 287 ret.append(((x >> 28) | 0x80) & 0xff) 288 ret.append(((x >> 35) | 0x80) & 0xff) 289 ret.append(((x >> 42) | 0x80) & 0xff) 290 ret.append((x >> 49) & 0x7f) 291 elif ((x & 0x8000000000000000) == 0): 292 ret.append((x | 0x80) & 0xff) 293 ret.append(((x >> 7) | 0x80) & 0xff) 294 ret.append(((x >> 14) | 0x80) & 0xff) 295 ret.append(((x >> 21) | 0x80) & 0xff) 296 ret.append(((x >> 28) | 0x80) & 0xff) 297 ret.append(((x >> 35) | 0x80) & 0xff) 298 ret.append(((x >> 42) | 0x80) & 0xff) 299 ret.append(((x >> 49) | 0x80) & 0xff) 300 ret.append((x >> 56) & 0x7f) 301 else: 302 ret.append((x | 0x80) & 0xff) 303 ret.append(((x >> 7) | 0x80) & 0xff) 304 ret.append(((x >> 14) | 0x80) & 0xff) 305 ret.append(((x >> 21) | 0x80) & 0xff) 306 ret.append(((x >> 28) | 0x80) & 0xff) 307 ret.append(((x >> 35) | 0x80) & 0xff) 308 ret.append(((x >> 42) | 0x80) & 0xff) 309 ret.append(((x >> 49) | 0x80) & 0xff) 310 ret.append(((x >> 56) | 0x80) & 0xff) 311 ret.append((x >> 63) & 0x7f) 312 return ret 313 314# 4-byte signed little endian 315def int32(x): 316 ret = [] 317 ret.append(x & 0xff) 318 ret.append((x >> 8) & 0xff) 319 ret.append((x >> 16) & 0xff) 320 ret.append((x >> 24) & 0xff) 321 return ret 322 323# 2-byte signed little endian 324def int16(x): 325 ret = [] 326 ret.append(x & 0xff) 327 ret.append((x >> 8) & 0xff) 328 return ret 329 330# a packed32 length followed by the specified number of characters 331def stringList(x): 332 ret = [] 333 ret += packed32(len(x)) 334 for i in x: 335 ret.append(i) 336 return ret 337 338def utf8StringList(x): 339 ret = [] 340 for i in x: 341 ret.append(ord(i)) 342 return ret 343 344# packed64 time value in nanoseconds relative to the uptime from the 345# Summary message. 346def timestampList(x): 347 ret = packed64(x) 348 return ret 349 350 351############################################################ 352# Write binary 353############################################################ 354 355def writeBinary(outfile, binary_list): 356 for i in binary_list: 357 outfile.write("%c" % i) 358 359############################################################ 360# APC Protocol Frame Types 361############################################################ 362 363def addFrameHeader(frame_type, body, core): 364 ret = [] 365 366 if frame_type == "Summary": 367 code = 1 368 elif frame_type == "Backtrace": 369 code = 2 370 elif frame_type == "Name": 371 code = 3 372 elif frame_type == "Counter": 373 code = 4 374 elif frame_type == "Block Counter": 375 code = 5 376 elif frame_type == "Annotate": 377 code = 6 378 elif frame_type == "Sched Trace": 379 code = 7 380 elif frame_type == "GPU Trace": 381 code = 8 382 elif frame_type == "Idle": 383 code = 9 384 else: 385 print "ERROR: Unknown frame type:", frame_type 386 sys.exit(1) 387 388 packed_code = packed32(code) 389 390 packed_core = packed32(core) 391 392 length = int32(len(packed_code) + len(packed_core) + len(body)) 393 394 ret = length + packed_code + packed_core + body 395 return ret 396 397 398# Summary frame 399# - timestamp: packed64 400# - uptime: packed64 401def summaryFrame(timestamp, uptime): 402 frame_type = "Summary"
|
385 body = packed64(timestamp) + packed64(uptime)
| 403 newline_canary = stringList("1\n2\r\n3\r4\n\r5") 404 monotonic_delta = packed64(0) 405 end_of_attr = stringList("") 406 body = newline_canary + packed64(timestamp) + packed64(uptime) 407 body += monotonic_delta + end_of_attr
|
386 ret = addFrameHeader(frame_type, body, 0) 387 return ret 388 389# Backtrace frame 390# - not implemented yet 391def backtraceFrame(): 392 pass 393 394# Cookie name message 395# - cookie: packed32 396# - name: string 397def cookieNameFrame(cookie, name): 398 frame_type = "Name" 399 packed_code = packed32(1) 400 body = packed_code + packed32(cookie) + stringList(name) 401 ret = addFrameHeader(frame_type, body, 0) 402 return ret 403 404# Thread name message 405# - timestamp: timestamp 406# - thread id: packed32 407# - name: string 408def threadNameFrame(timestamp, thread_id, name): 409 frame_type = "Name" 410 packed_code = packed32(2) 411 body = packed_code + timestampList(timestamp) + \ 412 packed32(thread_id) + stringList(name) 413 ret = addFrameHeader(frame_type, body, 0) 414 return ret 415 416# Core name message 417# - name: string
| 408 ret = addFrameHeader(frame_type, body, 0) 409 return ret 410 411# Backtrace frame 412# - not implemented yet 413def backtraceFrame(): 414 pass 415 416# Cookie name message 417# - cookie: packed32 418# - name: string 419def cookieNameFrame(cookie, name): 420 frame_type = "Name" 421 packed_code = packed32(1) 422 body = packed_code + packed32(cookie) + stringList(name) 423 ret = addFrameHeader(frame_type, body, 0) 424 return ret 425 426# Thread name message 427# - timestamp: timestamp 428# - thread id: packed32 429# - name: string 430def threadNameFrame(timestamp, thread_id, name): 431 frame_type = "Name" 432 packed_code = packed32(2) 433 body = packed_code + timestampList(timestamp) + \ 434 packed32(thread_id) + stringList(name) 435 ret = addFrameHeader(frame_type, body, 0) 436 return ret 437 438# Core name message 439# - name: string
|
418def coreNameFrame(name):
| 440# - core_id: packed32 441# - cpuid: packed32 442def coreNameFrame(name, core_id, cpuid):
|
419 frame_type = "Name" 420 packed_code = packed32(3)
| 443 frame_type = "Name" 444 packed_code = packed32(3)
|
421 body = packed_code + stringList(name)
| 445 body = packed_code + packed32(core_id) + packed32(cpuid) + stringList(name)
|
422 ret = addFrameHeader(frame_type, body, 0) 423 return ret 424
| 446 ret = addFrameHeader(frame_type, body, 0) 447 return ret 448
|
| 449# IRQ Cookie name message 450# - cookie: packed32 451# - name: string 452# - irq: packed32 453def irqCookieNameFrame(cookie, name, irq): 454 frame_type = "Name" 455 packed_code = packed32(5) 456 body = packed_code + packed32(cookie) + stringList(name) + packed32(irq) 457 ret = addFrameHeader(frame_type, body, 0) 458 return ret 459
|
425# Counter frame message 426# - timestamp: timestamp 427# - core: packed32 428# - key: packed32 429# - value: packed64 430def counterFrame(timestamp, core, key, value): 431 frame_type = "Counter" 432 body = timestampList(timestamp) + packed32(core) + packed32(key) + \ 433 packed64(value) 434 ret = addFrameHeader(frame_type, body, core) 435 return ret 436 437# Block Counter frame message 438# - key: packed32 439# - value: packed64 440def blockCounterFrame(core, key, value): 441 frame_type = "Block Counter" 442 body = packed32(key) + packed64(value) 443 ret = addFrameHeader(frame_type, body, core) 444 return ret 445 446# Annotate frame messages 447# - core: packed32 448# - tid: packed32 449# - timestamp: timestamp 450# - size: packed32 451# - body 452def annotateFrame(core, tid, timestamp, size, userspace_body): 453 frame_type = "Annotate" 454 body = packed32(core) + packed32(tid) + timestampList(timestamp) + \ 455 packed32(size) + userspace_body 456 ret = addFrameHeader(frame_type, body, core) 457 return ret 458 459# Scheduler Trace frame messages 460# Sched Switch 461# - Code: 1 462# - timestamp: timestamp 463# - pid: packed32 464# - tid: packed32 465# - cookie: packed32 466# - state: packed32 467def schedSwitchFrame(core, timestamp, pid, tid, cookie, state): 468 frame_type = "Sched Trace" 469 body = packed32(1) + timestampList(timestamp) + packed32(pid) + \ 470 packed32(tid) + packed32(cookie) + packed32(state) 471 ret = addFrameHeader(frame_type, body, core) 472 return ret 473 474# Sched Thread Exit 475# - Code: 2 476# - timestamp: timestamp 477# - tid: packed32 478def schedThreadExitFrame(core, timestamp, pid, tid, cookie, state): 479 frame_type = "Sched Trace" 480 body = packed32(2) + timestampList(timestamp) + packed32(tid) 481 ret = addFrameHeader(frame_type, body, core) 482 return ret 483 484# GPU Trace frame messages 485# - Not implemented yet 486def gpuTraceFrame(): 487 pass 488 489# Idle frame messages 490# Enter Idle 491# - code: 1 492# - timestamp: timestamp 493# - core: packed32 494def enterIdleFrame(timestamp, core): 495 frame_type = "Idle" 496 body = packed32(1) + timestampList(timestamp) + packed32(core) 497 ret = addFrameHeader(frame_type, body, core) 498 return ret 499 500# Exit Idle 501# - code: 2 502# - timestamp: timestamp 503# - core: packed32 504def exitIdleFrame(timestamp, core): 505 frame_type = "Idle" 506 body = packed32(2) + timestampList(timestamp) + packed32(core) 507 ret = addFrameHeader(frame_type, body, core) 508 return ret 509 510 511#################################################################### 512def parseProcessInfo(task_file): 513 print "\n===============================" 514 print "Parsing Task file..." 515 print task_file 516 print "===============================\n" 517 518 global start_tick, end_tick, num_cpus 519 global process_dict, thread_dict, process_list 520 global event_list, unified_event_list 521 global idle_uid, kernel_uid 522 523 event_list = [] 524 unified_event_list = [] 525 for cpu in range(num_cpus): 526 event_list.append([]) 527 528 uid = 1 # uid 0 is reserved for idle 529 530 # Dummy Tasks for frame buffers and system diagrams 531 process = Task(uid, 9999, 9999, "framebuffer", True, 0) 532 process_list.append(process) 533 uid += 1 534 thread = Task(uid, 9999, 9999, "framebuffer", False, 0) 535 process.children.append(thread) 536 uid += 1 537 process = Task(uid, 9998, 9998, "System", True, 0) 538 process_list.append(process) 539 # if we don't find the real kernel, use this to keep things going 540 kernel_uid = uid 541 uid += 1 542 thread = Task(uid, 9998, 9998, "System", False, 0) 543 process.children.append(thread) 544 uid += 1 545 546 ext = os.path.splitext(task_file)[1] 547 548 try: 549 if ext == ".gz": 550 process_file = gzip.open(task_file, 'rb') 551 else: 552 process_file = open(task_file, 'rb') 553 except: 554 print "ERROR opening task file:", task_file 555 print "Make sure context switch task dumping is enabled in gem5." 556 sys.exit(1) 557 558 process_re = re.compile("tick=(\d+)\s+(\d+)\s+cpu_id=(\d+)\s+" + 559 "next_pid=([-\d]+)\s+next_tgid=([-\d]+)\s+next_task=(.*)") 560 561 task_name_failure_warned = False 562 563 for line in process_file: 564 match = re.match(process_re, line) 565 if match: 566 tick = int(match.group(1)) 567 if (start_tick < 0): 568 start_tick = tick 569 cpu_id = int(match.group(3)) 570 pid = int(match.group(4)) 571 tgid = int(match.group(5)) 572 task_name = match.group(6) 573 574 if not task_name_failure_warned: 575 if task_name == "FailureIn_curTaskName": 576 print "-------------------------------------------------" 577 print "WARNING: Task name not set correctly!" 578 print "Process/Thread info will not be displayed correctly" 579 print "Perhaps forgot to apply m5struct.patch to kernel?" 580 print "-------------------------------------------------" 581 task_name_failure_warned = True 582 583 if not tgid in process_dict: 584 if tgid == pid: 585 # new task is parent as well 586 if args.verbose: 587 print "new process", uid, pid, tgid, task_name 588 if tgid == 0: 589 # new process is the "idle" task 590 process = Task(uid, pid, tgid, "idle", True, tick) 591 idle_uid = 0 592 else: 593 process = Task(uid, pid, tgid, task_name, True, tick) 594 else: 595 if tgid == 0: 596 process = Task(uid, tgid, tgid, "idle", True, tick) 597 idle_uid = 0 598 else: 599 # parent process name not known yet 600 process = Task(uid, tgid, tgid, "_Unknown_", True, tick) 601 if tgid == -1: # kernel 602 kernel_uid = 0 603 uid += 1 604 process_dict[tgid] = process 605 process_list.append(process) 606 else: 607 if tgid == pid: 608 if process_dict[tgid].task_name == "_Unknown_": 609 if args.verbose: 610 print "new process", \ 611 process_dict[tgid].uid, pid, tgid, task_name 612 process_dict[tgid].task_name = task_name 613 if process_dict[tgid].task_name != task_name and tgid != 0: 614 process_dict[tgid].task_name = task_name 615 616 if not pid in thread_dict: 617 if args.verbose: 618 print "new thread", \ 619 uid, process_dict[tgid].uid, pid, tgid, task_name 620 thread = Task(uid, pid, tgid, task_name, False, tick) 621 uid += 1 622 thread_dict[pid] = thread 623 process_dict[tgid].children.append(thread) 624 else: 625 if thread_dict[pid].task_name != task_name: 626 thread_dict[pid].task_name = task_name 627 628 if args.verbose: 629 print tick, uid, cpu_id, pid, tgid, task_name 630 631 task = thread_dict[pid] 632 event = Event(tick, task) 633 event_list[cpu_id].append(event) 634 unified_event_list.append(event) 635 636 if len(unified_event_list) == num_events: 637 print "Truncating at", num_events, "events!" 638 break 639 print "Found %d events." % len(unified_event_list) 640 641 for process in process_list: 642 if process.pid > 9990: # fix up framebuffer ticks 643 process.tick = start_tick 644 print process.uid, process.pid, process.tgid, \ 645 process.task_name, str(process.tick) 646 for thread in process.children: 647 if thread.pid > 9990: 648 thread.tick = start_tick 649 print "\t", thread.uid, thread.pid, thread.tgid, \ 650 thread.task_name, str(thread.tick) 651 652 end_tick = tick 653 654 print "Start tick:", start_tick 655 print "End tick: ", end_tick 656 print "" 657 658 return 659 660 661def initOutput(output_path): 662 if not os.path.exists(output_path): 663 os.mkdir(output_path) 664 665def ticksToNs(tick): 666 if ticks_in_ns < 0: 667 print "ticks_in_ns not set properly!" 668 sys.exit(1) 669 670 return tick / ticks_in_ns 671 672def writeXmlFile(xml, filename): 673 f = open(filename, "w") 674 txt = ET.tostring(xml) 675 f.write(minidom.parseString(txt).toprettyxml()) 676 f.close() 677 678 679# StatsEntry that contains individual statistics 680class StatsEntry(object): 681 def __init__(self, name, group, group_index, per_cpu, per_switchcpu, key): 682 683 # Full name of statistics 684 self.name = name 685 686 # Streamline group name that statistic will belong to 687 self.group = group 688 689 # Index of statistics within group (used to change colors within groups) 690 self.group_index = group_index 691 692 # Shorter name with "system" stripped off 693 # and symbols converted to alphanumerics 694 self.short_name = re.sub("system\.", "", name) 695 self.short_name = re.sub(":", "_", name) 696 697 # Regex for this stat (string version used to construct union regex) 698 self.regex_string = "^" + name + "\s+([\d\.]+)" 699 self.regex = re.compile("^" + name + "\s+([\d\.e\-]+)\s+# (.*)$", re.M) 700 self.description = "" 701 702 # Whether this stat is use per CPU or not 703 self.per_cpu = per_cpu 704 self.per_switchcpu = per_switchcpu 705 706 # Key used in .apc protocol (as described in captured.xml) 707 self.key = key 708 709 # List of values of stat per timestamp 710 self.values = [] 711 712 # Whether this stat has been found for the current timestamp 713 self.found = False 714 715 # Whether this stat has been found at least once 716 # (to suppress too many warnings) 717 self.not_found_at_least_once = False 718 719 # Field used to hold ElementTree subelement for this stat 720 self.ET_element = None 721 722 # Create per-CPU stat name and regex, etc. 723 if self.per_cpu: 724 self.per_cpu_regex_string = [] 725 self.per_cpu_regex = [] 726 self.per_cpu_name = [] 727 self.per_cpu_found = [] 728 for i in range(num_cpus): 729 # Resuming from checkpoints results in using "switch_cpus" 730 if per_switchcpu: 731 per_cpu_name = "system.switch_cpus" 732 else: 733 per_cpu_name = "system.cpu" 734 735 # No CPU number appends if num_cpus == 1 736 if num_cpus > 1: 737 per_cpu_name += str(i) 738 per_cpu_name += "." + self.name 739 self.per_cpu_name.append(per_cpu_name) 740 print "\t", per_cpu_name 741 742 self.per_cpu_regex_string.\ 743 append("^" + per_cpu_name + "\s+[\d\.]+") 744 self.per_cpu_regex.append(re.compile("^" + per_cpu_name + \ 745 "\s+([\d\.e\-]+)\s+# (.*)$", re.M)) 746 self.values.append([]) 747 self.per_cpu_found.append(False) 748 749 def append_value(self, val, per_cpu_index = None): 750 if self.per_cpu: 751 self.values[per_cpu_index].append(str(val)) 752 else: 753 self.values.append(str(val)) 754 755# Global stats object that contains the list of stats entries 756# and other utility functions 757class Stats(object): 758 def __init__(self): 759 self.stats_list = [] 760 self.tick_list = [] 761 self.next_key = 1 762 763 def register(self, name, group, group_index, per_cpu, per_switchcpu): 764 print "registering stat:", name, "group:", group, group_index 765 self.stats_list.append(StatsEntry(name, group, group_index, per_cpu, \ 766 per_switchcpu, self.next_key)) 767 self.next_key += 1 768 769 # Union of all stats to accelerate parsing speed 770 def createStatsRegex(self): 771 regex_strings = []; 772 print "\nnum entries in stats_list", len(self.stats_list) 773 for entry in self.stats_list: 774 if entry.per_cpu: 775 for i in range(num_cpus): 776 regex_strings.append(entry.per_cpu_regex_string[i]) 777 else: 778 regex_strings.append(entry.regex_string) 779 780 self.regex = re.compile('|'.join(regex_strings)) 781 782 783def registerStats(config_file): 784 print "===============================" 785 print "Parsing stats config.ini file..." 786 print config_file 787 print "===============================" 788 789 config = ConfigParser() 790 if not config.read(config_file): 791 print "ERROR: config file '", config_file, "' not found!" 792 sys.exit(1) 793 794 print "\nRegistering Stats..." 795 796 stats = Stats() 797 798 per_cpu_stat_groups = config.options('PER_CPU_STATS') 799 for group in per_cpu_stat_groups: 800 i = 0 801 per_cpu_stats_list = config.get('PER_CPU_STATS', group).split('\n') 802 for item in per_cpu_stats_list: 803 if item: 804 stats.register(item, group, i, True, False) 805 i += 1 806 807 per_cpu_stat_groups = config.options('PER_SWITCHCPU_STATS') 808 for group in per_cpu_stat_groups: 809 i = 0 810 per_cpu_stats_list = \ 811 config.get('PER_SWITCHCPU_STATS', group).split('\n') 812 for item in per_cpu_stats_list: 813 if item: 814 stats.register(item, group, i, True, True) 815 i += 1 816 817 per_l2_stat_groups = config.options('PER_L2_STATS') 818 for group in per_l2_stat_groups: 819 i = 0 820 per_l2_stats_list = config.get('PER_L2_STATS', group).split('\n') 821 for item in per_l2_stats_list: 822 if item: 823 for l2 in range(num_l2): 824 name = item 825 prefix = "system.l2" 826 if num_l2 > 1: 827 prefix += str(l2) 828 prefix += "." 829 name = prefix + name 830 stats.register(name, group, i, False, False) 831 i += 1 832 833 other_stat_groups = config.options('OTHER_STATS') 834 for group in other_stat_groups: 835 i = 0 836 other_stats_list = config.get('OTHER_STATS', group).split('\n') 837 for item in other_stats_list: 838 if item: 839 stats.register(item, group, i, False, False) 840 i += 1 841 842 stats.createStatsRegex() 843 844 return stats 845 846# Parse and read in gem5 stats file 847# Streamline counters are organized per CPU 848def readGem5Stats(stats, gem5_stats_file): 849 print "\n===============================" 850 print "Parsing gem5 stats file..." 851 print gem5_stats_file 852 print "===============================\n" 853 ext = os.path.splitext(gem5_stats_file)[1] 854 855 window_start_regex = \ 856 re.compile("^---------- Begin Simulation Statistics ----------") 857 window_end_regex = \ 858 re.compile("^---------- End Simulation Statistics ----------") 859 final_tick_regex = re.compile("^final_tick\s+(\d+)") 860 861 global ticks_in_ns 862 sim_freq_regex = re.compile("^sim_freq\s+(\d+)") 863 sim_freq = -1 864 865 try: 866 if ext == ".gz": 867 f = gzip.open(gem5_stats_file, "r") 868 else: 869 f = open(gem5_stats_file, "r") 870 except: 871 print "ERROR opening stats file", gem5_stats_file, "!" 872 sys.exit(1) 873 874 stats_not_found_list = stats.stats_list[:] 875 window_num = 0 876 877 while (True): 878 error = False 879 try: 880 line = f.readline() 881 except IOError: 882 print "" 883 print "WARNING: IO error in stats file" 884 print "(gzip stream not closed properly?)...continuing for now" 885 error = True 886 if not line: 887 break 888 889 # Find out how many gem5 ticks in 1ns 890 if sim_freq < 0: 891 m = sim_freq_regex.match(line) 892 if m: 893 sim_freq = int(m.group(1)) # ticks in 1 sec 894 ticks_in_ns = int(sim_freq / 1e9) 895 print "Simulation frequency found! 1 tick == %e sec\n" \ 896 % (1.0 / sim_freq) 897 898 # Final tick in gem5 stats: current absolute timestamp 899 m = final_tick_regex.match(line) 900 if m: 901 tick = int(m.group(1)) 902 if tick > end_tick: 903 break 904 stats.tick_list.append(tick) 905 906 907 if (window_end_regex.match(line) or error): 908 if args.verbose: 909 print "new window" 910 for stat in stats.stats_list: 911 if stat.per_cpu: 912 for i in range(num_cpus): 913 if not stat.per_cpu_found[i]: 914 if not stat.not_found_at_least_once: 915 print "WARNING: stat not found in window #", \ 916 window_num, ":", stat.per_cpu_name[i] 917 print "suppressing further warnings for " + \ 918 "this stat" 919 stat.not_found_at_least_once = True 920 stat.values[i].append(str(0)) 921 stat.per_cpu_found[i] = False 922 else: 923 if not stat.found: 924 if not stat.not_found_at_least_once: 925 print "WARNING: stat not found in window #", \ 926 window_num, ":", stat.name 927 print "suppressing further warnings for this stat" 928 stat.not_found_at_least_once = True 929 stat.values.append(str(0)) 930 stat.found = False 931 stats_not_found_list = stats.stats_list[:] 932 window_num += 1 933 if error: 934 break 935 936 # Do a single regex of the union of all stats first for speed 937 if stats.regex.match(line): 938 # Then loop through only the stats we haven't seen in this window 939 for stat in stats_not_found_list[:]: 940 if stat.per_cpu: 941 for i in range(num_cpus): 942 m = stat.per_cpu_regex[i].match(line) 943 if m: 944 if stat.name == "ipc": 945 value = str(int(float(m.group(1)) * 1000)) 946 else: 947 value = str(int(float(m.group(1)))) 948 if args.verbose: 949 print stat.per_cpu_name[i], value 950 stat.values[i].append(value) 951 stat.per_cpu_found[i] = True 952 all_found = True 953 for j in range(num_cpus): 954 if not stat.per_cpu_found[j]: 955 all_found = False 956 if all_found: 957 stats_not_found_list.remove(stat) 958 if stat.description == "": 959 stat.description = m.group(2) 960 else: 961 m = stat.regex.match(line) 962 if m: 963 value = str(int(float(m.group(1)))) 964 if args.verbose: 965 print stat.name, value 966 stat.values.append(value) 967 stat.found = True 968 stats_not_found_list.remove(stat) 969 if stat.description == "": 970 stat.description = m.group(2) 971 f.close() 972 973 974# Create session.xml file in .apc folder 975def doSessionXML(output_path): 976 session_file = output_path + "/session.xml" 977 978 xml = ET.Element("session") 979 980 xml.set("version", "1") 981 xml.set("call_stack_unwinding", "no") 982 xml.set("parse_debug_info", "no") 983 xml.set("high_resolution", "yes") 984 xml.set("buffer_mode", "streaming") 985 xml.set("sample_rate", "low") 986 987 # Setting duration to zero for now. Doesn't affect visualization. 988 xml.set("duration", "0") 989 990 xml.set("target_host", "") 991 xml.set("target_port", "8080") 992 993 writeXmlFile(xml, session_file) 994 995 996# Create captured.xml file in .apc folder 997def doCapturedXML(output_path, stats): 998 captured_file = output_path + "/captured.xml" 999 1000 xml = ET.Element("captured") 1001 xml.set("version", "1")
| 460# Counter frame message 461# - timestamp: timestamp 462# - core: packed32 463# - key: packed32 464# - value: packed64 465def counterFrame(timestamp, core, key, value): 466 frame_type = "Counter" 467 body = timestampList(timestamp) + packed32(core) + packed32(key) + \ 468 packed64(value) 469 ret = addFrameHeader(frame_type, body, core) 470 return ret 471 472# Block Counter frame message 473# - key: packed32 474# - value: packed64 475def blockCounterFrame(core, key, value): 476 frame_type = "Block Counter" 477 body = packed32(key) + packed64(value) 478 ret = addFrameHeader(frame_type, body, core) 479 return ret 480 481# Annotate frame messages 482# - core: packed32 483# - tid: packed32 484# - timestamp: timestamp 485# - size: packed32 486# - body 487def annotateFrame(core, tid, timestamp, size, userspace_body): 488 frame_type = "Annotate" 489 body = packed32(core) + packed32(tid) + timestampList(timestamp) + \ 490 packed32(size) + userspace_body 491 ret = addFrameHeader(frame_type, body, core) 492 return ret 493 494# Scheduler Trace frame messages 495# Sched Switch 496# - Code: 1 497# - timestamp: timestamp 498# - pid: packed32 499# - tid: packed32 500# - cookie: packed32 501# - state: packed32 502def schedSwitchFrame(core, timestamp, pid, tid, cookie, state): 503 frame_type = "Sched Trace" 504 body = packed32(1) + timestampList(timestamp) + packed32(pid) + \ 505 packed32(tid) + packed32(cookie) + packed32(state) 506 ret = addFrameHeader(frame_type, body, core) 507 return ret 508 509# Sched Thread Exit 510# - Code: 2 511# - timestamp: timestamp 512# - tid: packed32 513def schedThreadExitFrame(core, timestamp, pid, tid, cookie, state): 514 frame_type = "Sched Trace" 515 body = packed32(2) + timestampList(timestamp) + packed32(tid) 516 ret = addFrameHeader(frame_type, body, core) 517 return ret 518 519# GPU Trace frame messages 520# - Not implemented yet 521def gpuTraceFrame(): 522 pass 523 524# Idle frame messages 525# Enter Idle 526# - code: 1 527# - timestamp: timestamp 528# - core: packed32 529def enterIdleFrame(timestamp, core): 530 frame_type = "Idle" 531 body = packed32(1) + timestampList(timestamp) + packed32(core) 532 ret = addFrameHeader(frame_type, body, core) 533 return ret 534 535# Exit Idle 536# - code: 2 537# - timestamp: timestamp 538# - core: packed32 539def exitIdleFrame(timestamp, core): 540 frame_type = "Idle" 541 body = packed32(2) + timestampList(timestamp) + packed32(core) 542 ret = addFrameHeader(frame_type, body, core) 543 return ret 544 545 546#################################################################### 547def parseProcessInfo(task_file): 548 print "\n===============================" 549 print "Parsing Task file..." 550 print task_file 551 print "===============================\n" 552 553 global start_tick, end_tick, num_cpus 554 global process_dict, thread_dict, process_list 555 global event_list, unified_event_list 556 global idle_uid, kernel_uid 557 558 event_list = [] 559 unified_event_list = [] 560 for cpu in range(num_cpus): 561 event_list.append([]) 562 563 uid = 1 # uid 0 is reserved for idle 564 565 # Dummy Tasks for frame buffers and system diagrams 566 process = Task(uid, 9999, 9999, "framebuffer", True, 0) 567 process_list.append(process) 568 uid += 1 569 thread = Task(uid, 9999, 9999, "framebuffer", False, 0) 570 process.children.append(thread) 571 uid += 1 572 process = Task(uid, 9998, 9998, "System", True, 0) 573 process_list.append(process) 574 # if we don't find the real kernel, use this to keep things going 575 kernel_uid = uid 576 uid += 1 577 thread = Task(uid, 9998, 9998, "System", False, 0) 578 process.children.append(thread) 579 uid += 1 580 581 ext = os.path.splitext(task_file)[1] 582 583 try: 584 if ext == ".gz": 585 process_file = gzip.open(task_file, 'rb') 586 else: 587 process_file = open(task_file, 'rb') 588 except: 589 print "ERROR opening task file:", task_file 590 print "Make sure context switch task dumping is enabled in gem5." 591 sys.exit(1) 592 593 process_re = re.compile("tick=(\d+)\s+(\d+)\s+cpu_id=(\d+)\s+" + 594 "next_pid=([-\d]+)\s+next_tgid=([-\d]+)\s+next_task=(.*)") 595 596 task_name_failure_warned = False 597 598 for line in process_file: 599 match = re.match(process_re, line) 600 if match: 601 tick = int(match.group(1)) 602 if (start_tick < 0): 603 start_tick = tick 604 cpu_id = int(match.group(3)) 605 pid = int(match.group(4)) 606 tgid = int(match.group(5)) 607 task_name = match.group(6) 608 609 if not task_name_failure_warned: 610 if task_name == "FailureIn_curTaskName": 611 print "-------------------------------------------------" 612 print "WARNING: Task name not set correctly!" 613 print "Process/Thread info will not be displayed correctly" 614 print "Perhaps forgot to apply m5struct.patch to kernel?" 615 print "-------------------------------------------------" 616 task_name_failure_warned = True 617 618 if not tgid in process_dict: 619 if tgid == pid: 620 # new task is parent as well 621 if args.verbose: 622 print "new process", uid, pid, tgid, task_name 623 if tgid == 0: 624 # new process is the "idle" task 625 process = Task(uid, pid, tgid, "idle", True, tick) 626 idle_uid = 0 627 else: 628 process = Task(uid, pid, tgid, task_name, True, tick) 629 else: 630 if tgid == 0: 631 process = Task(uid, tgid, tgid, "idle", True, tick) 632 idle_uid = 0 633 else: 634 # parent process name not known yet 635 process = Task(uid, tgid, tgid, "_Unknown_", True, tick) 636 if tgid == -1: # kernel 637 kernel_uid = 0 638 uid += 1 639 process_dict[tgid] = process 640 process_list.append(process) 641 else: 642 if tgid == pid: 643 if process_dict[tgid].task_name == "_Unknown_": 644 if args.verbose: 645 print "new process", \ 646 process_dict[tgid].uid, pid, tgid, task_name 647 process_dict[tgid].task_name = task_name 648 if process_dict[tgid].task_name != task_name and tgid != 0: 649 process_dict[tgid].task_name = task_name 650 651 if not pid in thread_dict: 652 if args.verbose: 653 print "new thread", \ 654 uid, process_dict[tgid].uid, pid, tgid, task_name 655 thread = Task(uid, pid, tgid, task_name, False, tick) 656 uid += 1 657 thread_dict[pid] = thread 658 process_dict[tgid].children.append(thread) 659 else: 660 if thread_dict[pid].task_name != task_name: 661 thread_dict[pid].task_name = task_name 662 663 if args.verbose: 664 print tick, uid, cpu_id, pid, tgid, task_name 665 666 task = thread_dict[pid] 667 event = Event(tick, task) 668 event_list[cpu_id].append(event) 669 unified_event_list.append(event) 670 671 if len(unified_event_list) == num_events: 672 print "Truncating at", num_events, "events!" 673 break 674 print "Found %d events." % len(unified_event_list) 675 676 for process in process_list: 677 if process.pid > 9990: # fix up framebuffer ticks 678 process.tick = start_tick 679 print process.uid, process.pid, process.tgid, \ 680 process.task_name, str(process.tick) 681 for thread in process.children: 682 if thread.pid > 9990: 683 thread.tick = start_tick 684 print "\t", thread.uid, thread.pid, thread.tgid, \ 685 thread.task_name, str(thread.tick) 686 687 end_tick = tick 688 689 print "Start tick:", start_tick 690 print "End tick: ", end_tick 691 print "" 692 693 return 694 695 696def initOutput(output_path): 697 if not os.path.exists(output_path): 698 os.mkdir(output_path) 699 700def ticksToNs(tick): 701 if ticks_in_ns < 0: 702 print "ticks_in_ns not set properly!" 703 sys.exit(1) 704 705 return tick / ticks_in_ns 706 707def writeXmlFile(xml, filename): 708 f = open(filename, "w") 709 txt = ET.tostring(xml) 710 f.write(minidom.parseString(txt).toprettyxml()) 711 f.close() 712 713 714# StatsEntry that contains individual statistics 715class StatsEntry(object): 716 def __init__(self, name, group, group_index, per_cpu, per_switchcpu, key): 717 718 # Full name of statistics 719 self.name = name 720 721 # Streamline group name that statistic will belong to 722 self.group = group 723 724 # Index of statistics within group (used to change colors within groups) 725 self.group_index = group_index 726 727 # Shorter name with "system" stripped off 728 # and symbols converted to alphanumerics 729 self.short_name = re.sub("system\.", "", name) 730 self.short_name = re.sub(":", "_", name) 731 732 # Regex for this stat (string version used to construct union regex) 733 self.regex_string = "^" + name + "\s+([\d\.]+)" 734 self.regex = re.compile("^" + name + "\s+([\d\.e\-]+)\s+# (.*)$", re.M) 735 self.description = "" 736 737 # Whether this stat is use per CPU or not 738 self.per_cpu = per_cpu 739 self.per_switchcpu = per_switchcpu 740 741 # Key used in .apc protocol (as described in captured.xml) 742 self.key = key 743 744 # List of values of stat per timestamp 745 self.values = [] 746 747 # Whether this stat has been found for the current timestamp 748 self.found = False 749 750 # Whether this stat has been found at least once 751 # (to suppress too many warnings) 752 self.not_found_at_least_once = False 753 754 # Field used to hold ElementTree subelement for this stat 755 self.ET_element = None 756 757 # Create per-CPU stat name and regex, etc. 758 if self.per_cpu: 759 self.per_cpu_regex_string = [] 760 self.per_cpu_regex = [] 761 self.per_cpu_name = [] 762 self.per_cpu_found = [] 763 for i in range(num_cpus): 764 # Resuming from checkpoints results in using "switch_cpus" 765 if per_switchcpu: 766 per_cpu_name = "system.switch_cpus" 767 else: 768 per_cpu_name = "system.cpu" 769 770 # No CPU number appends if num_cpus == 1 771 if num_cpus > 1: 772 per_cpu_name += str(i) 773 per_cpu_name += "." + self.name 774 self.per_cpu_name.append(per_cpu_name) 775 print "\t", per_cpu_name 776 777 self.per_cpu_regex_string.\ 778 append("^" + per_cpu_name + "\s+[\d\.]+") 779 self.per_cpu_regex.append(re.compile("^" + per_cpu_name + \ 780 "\s+([\d\.e\-]+)\s+# (.*)$", re.M)) 781 self.values.append([]) 782 self.per_cpu_found.append(False) 783 784 def append_value(self, val, per_cpu_index = None): 785 if self.per_cpu: 786 self.values[per_cpu_index].append(str(val)) 787 else: 788 self.values.append(str(val)) 789 790# Global stats object that contains the list of stats entries 791# and other utility functions 792class Stats(object): 793 def __init__(self): 794 self.stats_list = [] 795 self.tick_list = [] 796 self.next_key = 1 797 798 def register(self, name, group, group_index, per_cpu, per_switchcpu): 799 print "registering stat:", name, "group:", group, group_index 800 self.stats_list.append(StatsEntry(name, group, group_index, per_cpu, \ 801 per_switchcpu, self.next_key)) 802 self.next_key += 1 803 804 # Union of all stats to accelerate parsing speed 805 def createStatsRegex(self): 806 regex_strings = []; 807 print "\nnum entries in stats_list", len(self.stats_list) 808 for entry in self.stats_list: 809 if entry.per_cpu: 810 for i in range(num_cpus): 811 regex_strings.append(entry.per_cpu_regex_string[i]) 812 else: 813 regex_strings.append(entry.regex_string) 814 815 self.regex = re.compile('|'.join(regex_strings)) 816 817 818def registerStats(config_file): 819 print "===============================" 820 print "Parsing stats config.ini file..." 821 print config_file 822 print "===============================" 823 824 config = ConfigParser() 825 if not config.read(config_file): 826 print "ERROR: config file '", config_file, "' not found!" 827 sys.exit(1) 828 829 print "\nRegistering Stats..." 830 831 stats = Stats() 832 833 per_cpu_stat_groups = config.options('PER_CPU_STATS') 834 for group in per_cpu_stat_groups: 835 i = 0 836 per_cpu_stats_list = config.get('PER_CPU_STATS', group).split('\n') 837 for item in per_cpu_stats_list: 838 if item: 839 stats.register(item, group, i, True, False) 840 i += 1 841 842 per_cpu_stat_groups = config.options('PER_SWITCHCPU_STATS') 843 for group in per_cpu_stat_groups: 844 i = 0 845 per_cpu_stats_list = \ 846 config.get('PER_SWITCHCPU_STATS', group).split('\n') 847 for item in per_cpu_stats_list: 848 if item: 849 stats.register(item, group, i, True, True) 850 i += 1 851 852 per_l2_stat_groups = config.options('PER_L2_STATS') 853 for group in per_l2_stat_groups: 854 i = 0 855 per_l2_stats_list = config.get('PER_L2_STATS', group).split('\n') 856 for item in per_l2_stats_list: 857 if item: 858 for l2 in range(num_l2): 859 name = item 860 prefix = "system.l2" 861 if num_l2 > 1: 862 prefix += str(l2) 863 prefix += "." 864 name = prefix + name 865 stats.register(name, group, i, False, False) 866 i += 1 867 868 other_stat_groups = config.options('OTHER_STATS') 869 for group in other_stat_groups: 870 i = 0 871 other_stats_list = config.get('OTHER_STATS', group).split('\n') 872 for item in other_stats_list: 873 if item: 874 stats.register(item, group, i, False, False) 875 i += 1 876 877 stats.createStatsRegex() 878 879 return stats 880 881# Parse and read in gem5 stats file 882# Streamline counters are organized per CPU 883def readGem5Stats(stats, gem5_stats_file): 884 print "\n===============================" 885 print "Parsing gem5 stats file..." 886 print gem5_stats_file 887 print "===============================\n" 888 ext = os.path.splitext(gem5_stats_file)[1] 889 890 window_start_regex = \ 891 re.compile("^---------- Begin Simulation Statistics ----------") 892 window_end_regex = \ 893 re.compile("^---------- End Simulation Statistics ----------") 894 final_tick_regex = re.compile("^final_tick\s+(\d+)") 895 896 global ticks_in_ns 897 sim_freq_regex = re.compile("^sim_freq\s+(\d+)") 898 sim_freq = -1 899 900 try: 901 if ext == ".gz": 902 f = gzip.open(gem5_stats_file, "r") 903 else: 904 f = open(gem5_stats_file, "r") 905 except: 906 print "ERROR opening stats file", gem5_stats_file, "!" 907 sys.exit(1) 908 909 stats_not_found_list = stats.stats_list[:] 910 window_num = 0 911 912 while (True): 913 error = False 914 try: 915 line = f.readline() 916 except IOError: 917 print "" 918 print "WARNING: IO error in stats file" 919 print "(gzip stream not closed properly?)...continuing for now" 920 error = True 921 if not line: 922 break 923 924 # Find out how many gem5 ticks in 1ns 925 if sim_freq < 0: 926 m = sim_freq_regex.match(line) 927 if m: 928 sim_freq = int(m.group(1)) # ticks in 1 sec 929 ticks_in_ns = int(sim_freq / 1e9) 930 print "Simulation frequency found! 1 tick == %e sec\n" \ 931 % (1.0 / sim_freq) 932 933 # Final tick in gem5 stats: current absolute timestamp 934 m = final_tick_regex.match(line) 935 if m: 936 tick = int(m.group(1)) 937 if tick > end_tick: 938 break 939 stats.tick_list.append(tick) 940 941 942 if (window_end_regex.match(line) or error): 943 if args.verbose: 944 print "new window" 945 for stat in stats.stats_list: 946 if stat.per_cpu: 947 for i in range(num_cpus): 948 if not stat.per_cpu_found[i]: 949 if not stat.not_found_at_least_once: 950 print "WARNING: stat not found in window #", \ 951 window_num, ":", stat.per_cpu_name[i] 952 print "suppressing further warnings for " + \ 953 "this stat" 954 stat.not_found_at_least_once = True 955 stat.values[i].append(str(0)) 956 stat.per_cpu_found[i] = False 957 else: 958 if not stat.found: 959 if not stat.not_found_at_least_once: 960 print "WARNING: stat not found in window #", \ 961 window_num, ":", stat.name 962 print "suppressing further warnings for this stat" 963 stat.not_found_at_least_once = True 964 stat.values.append(str(0)) 965 stat.found = False 966 stats_not_found_list = stats.stats_list[:] 967 window_num += 1 968 if error: 969 break 970 971 # Do a single regex of the union of all stats first for speed 972 if stats.regex.match(line): 973 # Then loop through only the stats we haven't seen in this window 974 for stat in stats_not_found_list[:]: 975 if stat.per_cpu: 976 for i in range(num_cpus): 977 m = stat.per_cpu_regex[i].match(line) 978 if m: 979 if stat.name == "ipc": 980 value = str(int(float(m.group(1)) * 1000)) 981 else: 982 value = str(int(float(m.group(1)))) 983 if args.verbose: 984 print stat.per_cpu_name[i], value 985 stat.values[i].append(value) 986 stat.per_cpu_found[i] = True 987 all_found = True 988 for j in range(num_cpus): 989 if not stat.per_cpu_found[j]: 990 all_found = False 991 if all_found: 992 stats_not_found_list.remove(stat) 993 if stat.description == "": 994 stat.description = m.group(2) 995 else: 996 m = stat.regex.match(line) 997 if m: 998 value = str(int(float(m.group(1)))) 999 if args.verbose: 1000 print stat.name, value 1001 stat.values.append(value) 1002 stat.found = True 1003 stats_not_found_list.remove(stat) 1004 if stat.description == "": 1005 stat.description = m.group(2) 1006 f.close() 1007 1008 1009# Create session.xml file in .apc folder 1010def doSessionXML(output_path): 1011 session_file = output_path + "/session.xml" 1012 1013 xml = ET.Element("session") 1014 1015 xml.set("version", "1") 1016 xml.set("call_stack_unwinding", "no") 1017 xml.set("parse_debug_info", "no") 1018 xml.set("high_resolution", "yes") 1019 xml.set("buffer_mode", "streaming") 1020 xml.set("sample_rate", "low") 1021 1022 # Setting duration to zero for now. Doesn't affect visualization. 1023 xml.set("duration", "0") 1024 1025 xml.set("target_host", "") 1026 xml.set("target_port", "8080") 1027 1028 writeXmlFile(xml, session_file) 1029 1030 1031# Create captured.xml file in .apc folder 1032def doCapturedXML(output_path, stats): 1033 captured_file = output_path + "/captured.xml" 1034 1035 xml = ET.Element("captured") 1036 xml.set("version", "1")
|
1002 xml.set("protocol", "12")
| 1037 xml.set("protocol", "17") 1038 xml.set("backtrace_processing", "none")
|
1003 1004 target = ET.SubElement(xml, "target") 1005 target.set("name", "gem5") 1006 target.set("sample_rate", "1000") 1007 target.set("cores", str(num_cpus)) 1008 1009 counters = ET.SubElement(xml, "counters") 1010 for stat in stats.stats_list: 1011 s = ET.SubElement(counters, "counter") 1012 stat_name = re.sub("\.", "_", stat.short_name) 1013 s.set("title", stat.group) 1014 s.set("name", stat_name) 1015 s.set("color", "0x00000000") 1016 s.set("key", "0x%08x" % stat.key) 1017 s.set("type", stat_name) 1018 s.set("event", "0x00000000") 1019 if stat.per_cpu: 1020 s.set("per_cpu", "yes") 1021 else: 1022 s.set("per_cpu", "no") 1023 s.set("display", "") 1024 s.set("units", "") 1025 s.set("average_selection", "no") 1026 s.set("description", stat.description) 1027 1028 writeXmlFile(xml, captured_file) 1029 1030# Writes out Streamline cookies (unique IDs per process/thread) 1031def writeCookiesThreads(blob): 1032 thread_list = [] 1033 for process in process_list: 1034 if process.uid > 0: 1035 print "cookie", process.task_name, process.uid 1036 writeBinary(blob, cookieNameFrame(process.uid, process.task_name)) 1037 1038 # pid and tgid need to be positive values -- no longer true? 1039 for thread in process.children: 1040 thread_list.append(thread) 1041 1042 # Threads need to be sorted in timestamp order 1043 thread_list.sort(key = lambda x: x.tick) 1044 for thread in thread_list: 1045 print "thread", thread.task_name, (ticksToNs(thread.tick)),\ 1046 thread.tgid, thread.pid 1047 writeBinary(blob, threadNameFrame(ticksToNs(thread.tick),\ 1048 thread.pid, thread.task_name)) 1049 1050# Writes context switch info as Streamline scheduling events 1051def writeSchedEvents(blob): 1052 for cpu in range(num_cpus): 1053 for event in event_list[cpu]: 1054 timestamp = ticksToNs(event.tick) 1055 pid = event.task.tgid 1056 tid = event.task.pid 1057 if process_dict.has_key(event.task.tgid): 1058 cookie = process_dict[event.task.tgid].uid 1059 else: 1060 cookie = 0 1061 1062 # State: 1063 # 0: waiting on other event besides I/O 1064 # 1: Contention/pre-emption 1065 # 2: Waiting on I/O 1066 # 3: Waiting on mutex 1067 # Hardcoding to 0 for now. Other states not implemented yet. 1068 state = 0 1069 1070 if args.verbose: 1071 print cpu, timestamp, pid, tid, cookie 1072 1073 writeBinary(blob,\ 1074 schedSwitchFrame(cpu, timestamp, pid, tid, cookie, state)) 1075 1076# Writes selected gem5 statistics as Streamline counters 1077def writeCounters(blob, stats): 1078 timestamp_list = [] 1079 for tick in stats.tick_list: 1080 if tick > end_tick: 1081 break 1082 timestamp_list.append(ticksToNs(tick)) 1083 1084 for stat in stats.stats_list: 1085 if stat.per_cpu: 1086 stat_length = len(stat.values[0]) 1087 else: 1088 stat_length = len(stat.values) 1089 1090 for n in range(len(timestamp_list)): 1091 for stat in stats.stats_list: 1092 if stat.per_cpu: 1093 for i in range(num_cpus): 1094 writeBinary(blob, counterFrame(timestamp_list[n], i, \ 1095 stat.key, int(float(stat.values[i][n])))) 1096 else: 1097 writeBinary(blob, counterFrame(timestamp_list[n], 0, \ 1098 stat.key, int(float(stat.values[n])))) 1099 1100# Streamline can display LCD frame buffer dumps (gzipped bmp) 1101# This function converts the frame buffer dumps to the Streamline format 1102def writeVisualAnnotations(blob, input_path, output_path): 1103 frame_path = input_path + "/frames_system.vncserver" 1104 if not os.path.exists(frame_path): 1105 return 1106 1107 frame_count = 0 1108 file_list = os.listdir(frame_path) 1109 file_list.sort() 1110 re_fb = re.compile("fb\.(\d+)\.(\d+)\.bmp.gz") 1111 1112 # Use first non-negative pid to tag visual annotations 1113 annotate_pid = -1 1114 for e in unified_event_list: 1115 pid = e.task.pid 1116 if pid >= 0: 1117 annotate_pid = pid 1118 break 1119 1120 for fn in file_list: 1121 m = re_fb.match(fn) 1122 if m: 1123 seq = m.group(1) 1124 tick = int(m.group(2)) 1125 if tick > end_tick: 1126 break 1127 frame_count += 1 1128 1129 userspace_body = [] 1130 userspace_body += packed32(0x1C) # escape code 1131 userspace_body += packed32(0x04) # visual code 1132 1133 text_annotation = "image_" + str(ticksToNs(tick)) + ".bmp.gz" 1134 userspace_body += int16(len(text_annotation)) 1135 userspace_body += utf8StringList(text_annotation) 1136 1137 if gzipped_bmp_supported: 1138 # copy gzipped bmp directly 1139 bytes_read = open(frame_path + "/" + fn, "rb").read() 1140 else: 1141 # copy uncompressed bmp 1142 bytes_read = gzip.open(frame_path + "/" + fn, "rb").read() 1143 1144 userspace_body += int32(len(bytes_read)) 1145 userspace_body += bytes_read 1146 1147 writeBinary(blob, annotateFrame(0, annotate_pid, ticksToNs(tick), \ 1148 len(userspace_body), userspace_body)) 1149 1150 print "\nfound", frame_count, "frames for visual annotation.\n" 1151 1152 1153def createApcProject(input_path, output_path, stats): 1154 initOutput(output_path) 1155 1156 blob = open(output_path + "/0000000000", "wb") 1157 1158 # Summary frame takes current system time and system uptime. 1159 # Filling in with random values for now. 1160 writeBinary(blob, summaryFrame(1234, 5678)) 1161 1162 writeCookiesThreads(blob) 1163 1164 print "writing Events" 1165 writeSchedEvents(blob) 1166 1167 print "writing Counters" 1168 writeCounters(blob, stats) 1169 1170 print "writing Visual Annotations" 1171 writeVisualAnnotations(blob, input_path, output_path) 1172 1173 doSessionXML(output_path) 1174 doCapturedXML(output_path, stats) 1175 1176 blob.close() 1177 1178 1179 1180####################### 1181# Main Routine 1182 1183input_path = args.input_path 1184output_path = args.output_path 1185 1186#### 1187# Make sure input path exists 1188#### 1189if not os.path.exists(input_path): 1190 print "ERROR: Input path %s does not exist!" % input_path 1191 sys.exit(1) 1192 1193#### 1194# Parse gem5 configuration file to find # of CPUs and L2s 1195#### 1196(num_cpus, num_l2) = parseConfig(input_path + "/config.ini") 1197 1198#### 1199# Parse task file to find process/thread info 1200#### 1201parseProcessInfo(input_path + "/system.tasks.txt") 1202 1203#### 1204# Parse stat config file and register stats 1205#### 1206stat_config_file = args.stat_config_file 1207stats = registerStats(stat_config_file) 1208 1209#### 1210# Parse gem5 stats 1211#### 1212# Check if both stats.txt and stats.txt.gz exist and warn if both exist 1213if os.path.exists(input_path + "/stats.txt") and \ 1214 os.path.exists(input_path + "/stats.txt.gz"): 1215 print "WARNING: Both stats.txt.gz and stats.txt exist. \ 1216 Using stats.txt.gz by default." 1217 1218gem5_stats_file = input_path + "/stats.txt.gz" 1219if not os.path.exists(gem5_stats_file): 1220 gem5_stats_file = input_path + "/stats.txt" 1221if not os.path.exists(gem5_stats_file): 1222 print "ERROR: stats.txt[.gz] file does not exist in %s!" % input_path 1223 sys.exit(1) 1224 1225readGem5Stats(stats, gem5_stats_file) 1226 1227#### 1228# Create Streamline .apc project folder 1229#### 1230createApcProject(input_path, output_path, stats) 1231 1232print "All done!"
| 1039 1040 target = ET.SubElement(xml, "target") 1041 target.set("name", "gem5") 1042 target.set("sample_rate", "1000") 1043 target.set("cores", str(num_cpus)) 1044 1045 counters = ET.SubElement(xml, "counters") 1046 for stat in stats.stats_list: 1047 s = ET.SubElement(counters, "counter") 1048 stat_name = re.sub("\.", "_", stat.short_name) 1049 s.set("title", stat.group) 1050 s.set("name", stat_name) 1051 s.set("color", "0x00000000") 1052 s.set("key", "0x%08x" % stat.key) 1053 s.set("type", stat_name) 1054 s.set("event", "0x00000000") 1055 if stat.per_cpu: 1056 s.set("per_cpu", "yes") 1057 else: 1058 s.set("per_cpu", "no") 1059 s.set("display", "") 1060 s.set("units", "") 1061 s.set("average_selection", "no") 1062 s.set("description", stat.description) 1063 1064 writeXmlFile(xml, captured_file) 1065 1066# Writes out Streamline cookies (unique IDs per process/thread) 1067def writeCookiesThreads(blob): 1068 thread_list = [] 1069 for process in process_list: 1070 if process.uid > 0: 1071 print "cookie", process.task_name, process.uid 1072 writeBinary(blob, cookieNameFrame(process.uid, process.task_name)) 1073 1074 # pid and tgid need to be positive values -- no longer true? 1075 for thread in process.children: 1076 thread_list.append(thread) 1077 1078 # Threads need to be sorted in timestamp order 1079 thread_list.sort(key = lambda x: x.tick) 1080 for thread in thread_list: 1081 print "thread", thread.task_name, (ticksToNs(thread.tick)),\ 1082 thread.tgid, thread.pid 1083 writeBinary(blob, threadNameFrame(ticksToNs(thread.tick),\ 1084 thread.pid, thread.task_name)) 1085 1086# Writes context switch info as Streamline scheduling events 1087def writeSchedEvents(blob): 1088 for cpu in range(num_cpus): 1089 for event in event_list[cpu]: 1090 timestamp = ticksToNs(event.tick) 1091 pid = event.task.tgid 1092 tid = event.task.pid 1093 if process_dict.has_key(event.task.tgid): 1094 cookie = process_dict[event.task.tgid].uid 1095 else: 1096 cookie = 0 1097 1098 # State: 1099 # 0: waiting on other event besides I/O 1100 # 1: Contention/pre-emption 1101 # 2: Waiting on I/O 1102 # 3: Waiting on mutex 1103 # Hardcoding to 0 for now. Other states not implemented yet. 1104 state = 0 1105 1106 if args.verbose: 1107 print cpu, timestamp, pid, tid, cookie 1108 1109 writeBinary(blob,\ 1110 schedSwitchFrame(cpu, timestamp, pid, tid, cookie, state)) 1111 1112# Writes selected gem5 statistics as Streamline counters 1113def writeCounters(blob, stats): 1114 timestamp_list = [] 1115 for tick in stats.tick_list: 1116 if tick > end_tick: 1117 break 1118 timestamp_list.append(ticksToNs(tick)) 1119 1120 for stat in stats.stats_list: 1121 if stat.per_cpu: 1122 stat_length = len(stat.values[0]) 1123 else: 1124 stat_length = len(stat.values) 1125 1126 for n in range(len(timestamp_list)): 1127 for stat in stats.stats_list: 1128 if stat.per_cpu: 1129 for i in range(num_cpus): 1130 writeBinary(blob, counterFrame(timestamp_list[n], i, \ 1131 stat.key, int(float(stat.values[i][n])))) 1132 else: 1133 writeBinary(blob, counterFrame(timestamp_list[n], 0, \ 1134 stat.key, int(float(stat.values[n])))) 1135 1136# Streamline can display LCD frame buffer dumps (gzipped bmp) 1137# This function converts the frame buffer dumps to the Streamline format 1138def writeVisualAnnotations(blob, input_path, output_path): 1139 frame_path = input_path + "/frames_system.vncserver" 1140 if not os.path.exists(frame_path): 1141 return 1142 1143 frame_count = 0 1144 file_list = os.listdir(frame_path) 1145 file_list.sort() 1146 re_fb = re.compile("fb\.(\d+)\.(\d+)\.bmp.gz") 1147 1148 # Use first non-negative pid to tag visual annotations 1149 annotate_pid = -1 1150 for e in unified_event_list: 1151 pid = e.task.pid 1152 if pid >= 0: 1153 annotate_pid = pid 1154 break 1155 1156 for fn in file_list: 1157 m = re_fb.match(fn) 1158 if m: 1159 seq = m.group(1) 1160 tick = int(m.group(2)) 1161 if tick > end_tick: 1162 break 1163 frame_count += 1 1164 1165 userspace_body = [] 1166 userspace_body += packed32(0x1C) # escape code 1167 userspace_body += packed32(0x04) # visual code 1168 1169 text_annotation = "image_" + str(ticksToNs(tick)) + ".bmp.gz" 1170 userspace_body += int16(len(text_annotation)) 1171 userspace_body += utf8StringList(text_annotation) 1172 1173 if gzipped_bmp_supported: 1174 # copy gzipped bmp directly 1175 bytes_read = open(frame_path + "/" + fn, "rb").read() 1176 else: 1177 # copy uncompressed bmp 1178 bytes_read = gzip.open(frame_path + "/" + fn, "rb").read() 1179 1180 userspace_body += int32(len(bytes_read)) 1181 userspace_body += bytes_read 1182 1183 writeBinary(blob, annotateFrame(0, annotate_pid, ticksToNs(tick), \ 1184 len(userspace_body), userspace_body)) 1185 1186 print "\nfound", frame_count, "frames for visual annotation.\n" 1187 1188 1189def createApcProject(input_path, output_path, stats): 1190 initOutput(output_path) 1191 1192 blob = open(output_path + "/0000000000", "wb") 1193 1194 # Summary frame takes current system time and system uptime. 1195 # Filling in with random values for now. 1196 writeBinary(blob, summaryFrame(1234, 5678)) 1197 1198 writeCookiesThreads(blob) 1199 1200 print "writing Events" 1201 writeSchedEvents(blob) 1202 1203 print "writing Counters" 1204 writeCounters(blob, stats) 1205 1206 print "writing Visual Annotations" 1207 writeVisualAnnotations(blob, input_path, output_path) 1208 1209 doSessionXML(output_path) 1210 doCapturedXML(output_path, stats) 1211 1212 blob.close() 1213 1214 1215 1216####################### 1217# Main Routine 1218 1219input_path = args.input_path 1220output_path = args.output_path 1221 1222#### 1223# Make sure input path exists 1224#### 1225if not os.path.exists(input_path): 1226 print "ERROR: Input path %s does not exist!" % input_path 1227 sys.exit(1) 1228 1229#### 1230# Parse gem5 configuration file to find # of CPUs and L2s 1231#### 1232(num_cpus, num_l2) = parseConfig(input_path + "/config.ini") 1233 1234#### 1235# Parse task file to find process/thread info 1236#### 1237parseProcessInfo(input_path + "/system.tasks.txt") 1238 1239#### 1240# Parse stat config file and register stats 1241#### 1242stat_config_file = args.stat_config_file 1243stats = registerStats(stat_config_file) 1244 1245#### 1246# Parse gem5 stats 1247#### 1248# Check if both stats.txt and stats.txt.gz exist and warn if both exist 1249if os.path.exists(input_path + "/stats.txt") and \ 1250 os.path.exists(input_path + "/stats.txt.gz"): 1251 print "WARNING: Both stats.txt.gz and stats.txt exist. \ 1252 Using stats.txt.gz by default." 1253 1254gem5_stats_file = input_path + "/stats.txt.gz" 1255if not os.path.exists(gem5_stats_file): 1256 gem5_stats_file = input_path + "/stats.txt" 1257if not os.path.exists(gem5_stats_file): 1258 print "ERROR: stats.txt[.gz] file does not exist in %s!" % input_path 1259 sys.exit(1) 1260 1261readGem5Stats(stats, gem5_stats_file) 1262 1263#### 1264# Create Streamline .apc project folder 1265#### 1266createApcProject(input_path, output_path, stats) 1267 1268print "All done!"
|