Index: pkg/fletchc/lib/program_info.dart |
diff --git a/pkg/fletchc/lib/program_info.dart b/pkg/fletchc/lib/program_info.dart |
deleted file mode 100644 |
index dfdecba005ab7536855b665341cf46a5054befef..0000000000000000000000000000000000000000 |
--- a/pkg/fletchc/lib/program_info.dart |
+++ /dev/null |
@@ -1,876 +0,0 @@ |
-// Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE.md file. |
- |
-library fletchc.program_info; |
- |
-import 'dart:async' show |
- Future, |
- Stream, |
- StreamSink; |
- |
-import 'dart:io' as io; |
- |
-import 'dart:io' show |
- BytesBuilder; |
- |
-import 'dart:convert' show |
- JSON, |
- LineSplitter, |
- UTF8; |
- |
-import 'dart:typed_data' show |
- Int32List, |
- Uint8List, |
- ByteData, |
- Endianness; |
- |
-import 'package:persistent/persistent.dart' show |
- Pair; |
- |
-import 'vm_commands.dart' show |
- WriteSnapshotResult; |
- |
-import 'fletch_system.dart' show |
- FletchClass, |
- FletchFunction, |
- FletchSystem; |
- |
-import 'src/fletch_selector.dart' show |
- FletchSelector; |
- |
-enum Configuration { |
- Offset64BitsDouble, |
- Offset64BitsFloat, |
- Offset32BitsDouble, |
- Offset32BitsFloat, |
-} |
- |
-class ProgramInfo { |
- final List<String> _strings; |
- |
- // Maps selector-id -> string-id |
- final List<int> _selectorNames; |
- |
- // Maps configuration -> offset -> string-id |
- final Map<Configuration, Map<int, int>> _classNames; |
- |
- // Maps configuration -> offset -> string-id |
- final Map<Configuration, Map<int, int>> _functionNames; |
- |
- // Snapshot hashtag for validation. |
- final hashtag; |
- |
- ProgramInfo(this._strings, this._selectorNames, |
- this._classNames, this._functionNames, |
- this.hashtag); |
- |
- String classNameOfFunction(Configuration conf, int functionOffset) { |
- return _getString(_classNames[conf][functionOffset]); |
- } |
- |
- String functionName(Configuration conf, int functionOffset) { |
- return _getString(_functionNames[conf][functionOffset]); |
- } |
- |
- String className(Configuration conf, int classOffset) { |
- return _getString(_classNames[conf][classOffset]); |
- } |
- |
- String selectorName(FletchSelector selector) { |
- return _getString(_selectorNames[selector.id]); |
- } |
- |
- String _getString(int stringId) { |
- String name = null; |
- if (stringId != null && stringId != -1) { |
- name = _strings[stringId]; |
- if (name == '') name = null; |
- } |
- return name; |
- } |
-} |
- |
-abstract class ProgramInfoJson { |
- static String encode(ProgramInfo info, {List<Configuration> enabledConfigs}) { |
- if (enabledConfigs == null) { |
- enabledConfigs = Configuration.values; |
- } |
- |
- Map<String, List<int>> buildTables( |
- Map<Configuration, Map<int, int>> offset2stringIds) { |
- |
- List<int> convertMap(Configuration conf) { |
- if (enabledConfigs.contains(conf)) { |
- var map = offset2stringIds[conf]; |
- List<int> list = new List<int>(map.length * 2); |
- int offset = 0; |
- map.forEach((int a, int b) { |
- list[offset++] = a; |
- list[offset++] = b; |
- }); |
- return list; |
- } else { |
- return const []; |
- } |
- } |
- |
- return { |
- 'b64double': convertMap(Configuration.Offset64BitsDouble), |
- 'b64float': convertMap(Configuration.Offset64BitsFloat), |
- 'b32double': convertMap(Configuration.Offset32BitsDouble), |
- 'b32float': convertMap(Configuration.Offset32BitsFloat), |
- }; |
- } |
- |
- return JSON.encode({ |
- 'strings': info._strings, |
- 'selectors': info._selectorNames, |
- 'class-names': buildTables(info._classNames), |
- 'function-names' : buildTables(info._functionNames), |
- 'hashtag': info.hashtag, |
- }); |
- } |
- |
- static ProgramInfo decode(String string) { |
- var json = JSON.decode(string); |
- |
- Map<int, int> convertList(List<int> list) { |
- Map<int, int> map = {}; |
- for (int i = 0; i < list.length; i += 2) { |
- map[list[i]] = list[i + 1]; |
- } |
- return map; |
- } |
- |
- var classNames = { |
- Configuration.Offset64BitsDouble : |
- convertList(json['class-names']['b64double']), |
- Configuration.Offset64BitsFloat: |
- convertList(json['class-names']['b64float']), |
- Configuration.Offset32BitsDouble : |
- convertList(json['class-names']['b32double']), |
- Configuration.Offset32BitsFloat : |
- convertList(json['class-names']['b32float']), |
- }; |
- |
- var functionNames = { |
- Configuration.Offset64BitsDouble : |
- convertList(json['function-names']['b64double']), |
- Configuration.Offset64BitsFloat: |
- convertList(json['function-names']['b64float']), |
- Configuration.Offset32BitsDouble : |
- convertList(json['function-names']['b32double']), |
- Configuration.Offset32BitsFloat : |
- convertList(json['function-names']['b32float']), |
- }; |
- |
- return new ProgramInfo( |
- json['strings'], json['selectors'], |
- classNames, functionNames, |
- json['hashtag']); |
- } |
-} |
- |
-abstract class ProgramInfoBinary { |
- static const int _INVALID_INDEX = 0xffffff; |
- static final int _HEADER_LENGTH = 4 * (2 + 2 * Configuration.values.length); |
- |
- static List<int> encode(ProgramInfo info, |
- {List<Configuration> enabledConfigs}) { |
- if (enabledConfigs == null) { |
- enabledConfigs = Configuration.values; |
- } |
- |
- List<int> stringOffsetsInStringTable = []; |
- |
- void ensureEncodableAs24Bit(int number) { |
- if (number >= ((1 << 24) - 1)) { |
- throw new Exception( |
- "The binary program information format cannot encode offsets " |
- "larger than 16 MB (24 bits) at the moment."); |
- } |
- } |
- |
- List<int> buildStringTable() { |
- BytesBuilder builder = new BytesBuilder(); |
- for (int i = 0; i < info._strings.length; i++) { |
- stringOffsetsInStringTable.add(builder.length); |
- String name = info._strings[i]; |
- if (name != null) { |
- builder.add(UTF8.encode(name)); |
- } |
- builder.addByte(0); |
- } |
- return builder.takeBytes(); |
- } |
- |
- List<int> buildSelectorTable(List<int> stringIds) { |
- Uint8List bytes = new Uint8List(3 * stringIds.length); |
- |
- int offset = 0; |
- |
- void writeByte(int byte) { |
- bytes[offset++] = byte; |
- } |
- |
- void writeNumber(int number) { |
- ensureEncodableAs24Bit(number); |
- |
- // 0xffffff means -1 |
- if (number == -1) number = _INVALID_INDEX; |
- |
- writeByte(number & 0xff); |
- writeByte((number >> 8) & 0xff); |
- writeByte((number >> 16) & 0xff); |
- } |
- |
- // We write tuples [program-offset, string-table-offset]. |
- // So the user can use binary search using a program offset to find the |
- // string offset. |
- for (int i = 0; i < stringIds.length; i++) { |
- int stringId = stringIds[i]; |
- |
- if (stringId != -1) { |
- int stringOffset = stringOffsetsInStringTable[stringId]; |
- writeNumber(stringOffset); |
- } else { |
- writeNumber(-1); |
- } |
- } |
- |
- assert(offset == bytes.length); |
- |
- return bytes; |
- } |
- |
- List<int> buildOffsetTable(Map<int, int> map) { |
- Uint8List bytes = new Uint8List(2 * 3 * map.length); |
- |
- int offset = 0; |
- |
- void writeByte(int byte) { |
- bytes[offset++] = byte; |
- } |
- |
- void writeNumber(int number) { |
- // 0xffffff means -1 |
- ensureEncodableAs24Bit(number); |
- |
- if (number == -1) number = _INVALID_INDEX; |
- |
- writeByte(number & 0xff); |
- writeByte((number >> 8) & 0xff); |
- writeByte((number >> 16) & 0xff); |
- } |
- |
- // We write tuples [program-offset, string-table-offset]. |
- // So the user can use binary search using a program offset to find the |
- // string offset. |
- List<int> offsets = map.keys.toList()..sort(); |
- for (int i = 0; i < offsets.length; i++) { |
- int offset = offsets[i]; |
- int stringId = map[offset]; |
- |
- // We only make an entry for [offset] if there is a known string. |
- if (stringId != -1) { |
- int stringOffset = stringOffsetsInStringTable[stringId]; |
- writeNumber(offset); |
- writeNumber(stringOffset); |
- } |
- } |
- |
- return new Uint8List.view(bytes.buffer, 0, offset); |
- } |
- |
- List<List<int>> tables = []; |
- tables.add(buildStringTable()); |
- tables.add(buildSelectorTable(info._selectorNames)); |
- for (Configuration conf in Configuration.values) { |
- if (enabledConfigs.contains(conf)) { |
- List<int> offsetTable = buildOffsetTable(info._classNames[conf]); |
- tables.add(offsetTable); |
- } else { |
- tables.add([]); |
- } |
- } |
- for (Configuration conf in Configuration.values) { |
- if (enabledConfigs.contains(conf)) { |
- List<int> offsetTable = buildOffsetTable(info._functionNames[conf]); |
- tables.add(offsetTable); |
- } else { |
- tables.add([]); |
- } |
- } |
- |
- int tableLengths = tables.map((t) => t.length).fold(0, (a, b) => a + b); |
- int length = _HEADER_LENGTH + tableLengths; |
- |
- Uint8List bytes = new Uint8List(length); |
- ByteData header = new ByteData.view(bytes.buffer); |
- |
- int offset = 0; |
- writeLength(List<int> data) { |
- header.setUint32(offset, data.length, Endianness.LITTLE_ENDIAN); |
- offset += 4; |
- } |
- writeTable(List<int> data) { |
- bytes.setRange(offset, offset + data.length, data); |
- offset += data.length; |
- } |
- |
- tables.forEach(writeLength); |
- assert(offset == _HEADER_LENGTH); |
- tables.forEach(writeTable); |
- |
- return bytes; |
- } |
- |
- static ProgramInfo decode(List<int> data) { |
- Uint8List bytes = new Uint8List.fromList(data); |
- ByteData header = new ByteData.view(bytes.buffer); |
- |
- int offset = 0; |
- int readLength() { |
- int length = header.getUint32(offset, Endianness.LITTLE_ENDIAN); |
- offset += 4; |
- return length; |
- } |
- List<int> readTable(int length) { |
- var view = new Uint8List.view(bytes.buffer, offset, length); |
- offset += length; |
- return view; |
- } |
- |
- Map<int, int> stringOffsetToIndex = {}; |
- List<String> buildStringDecodingTable(List<int> data) { |
- List<String> strings = []; |
- int start = 0; |
- while (start < data.length) { |
- stringOffsetToIndex[start] = strings.length; |
- |
- int end = start; |
- while (data[end] != 0) end++; |
- |
- strings.add(UTF8.decode(data.sublist(start, end))); |
- start = end + 1; |
- } |
- return strings; |
- } |
- |
- List<int> decodeSelectors(List<int> data) { |
- assert(data.length % 3 == 0); |
- List<int> indices = new List(data.length ~/ 3); |
- for (int offset = 0; offset < data.length; offset += 3) { |
- int number = |
- data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16); |
- if (number == _INVALID_INDEX) number = -1; |
- if (number != -1) number = stringOffsetToIndex[number]; |
- indices[offset ~/ 3] = number; |
- } |
- return indices; |
- } |
- |
- Map<int, int> decodeTable(List<int> data) { |
- assert(data.length % 6 == 0); |
- Map<int, int> offset2stringId = {}; |
- for (int offset = 0; offset < data.length; offset += 6) { |
- int programOffset = |
- data[offset] | |
- (data[offset + 1] << 8) | |
- (data[offset + 2] << 16); |
- int stringOffset = |
- data[offset + 3] | |
- (data[offset + 4] << 8) | |
- (data[offset + 5] << 16); |
- |
- if (programOffset != _INVALID_INDEX && stringOffset != _INVALID_INDEX) { |
- offset2stringId[programOffset] = stringOffsetToIndex[stringOffset]; |
- } else { |
- offset2stringId[programOffset] = -1; |
- } |
- } |
- return offset2stringId; |
- } |
- |
- int stringTableLength = readLength(); |
- int selectorTableLength = readLength(); |
- |
- Map<Configuration, int> classTableLengths = {}; |
- Configuration.values.forEach( |
- (conf) => classTableLengths[conf] = readLength()); |
- |
- Map<Configuration, int> functionTableLengths = {}; |
- Configuration.values.forEach( |
- (conf) => functionTableLengths[conf] = readLength()); |
- |
- List<int> stringTable = readTable(stringTableLength); |
- |
- List<String> strings = buildStringDecodingTable(stringTable); |
- List<int> selectorTable = decodeSelectors(readTable(selectorTableLength)); |
- |
- Map<Configuration, Map<int, int>> classNames = {}; |
- Configuration.values.forEach((conf) { |
- classNames[conf] = decodeTable(readTable(classTableLengths[conf])); |
- }); |
- |
- Map<Configuration, Map<int, int>> functionNames = {}; |
- Configuration.values.forEach((conf) { |
- functionNames[conf] = decodeTable(readTable(functionTableLengths[conf])); |
- }); |
- |
- assert(offset == (_HEADER_LENGTH + |
- stringTableLength + |
- selectorTableLength + |
- classTableLengths.values.fold(0, (a, b) => a + b) + |
- functionTableLengths.values.fold(0, (a, b) => a + b))); |
- |
- return new ProgramInfo( |
- strings, |
- selectorTable, |
- classNames, |
- functionNames, |
- 0); // hashtag for shapshot is not supported for binary format. |
- } |
-} |
- |
-ProgramInfo buildProgramInfo(FletchSystem system, WriteSnapshotResult result) { |
- List<String> strings = []; |
- Map<String, int> stringIndices = {}; |
- List<int> selectors = []; |
- |
- int newName(String name) { |
- if (name == null) return -1; |
- |
- var index = stringIndices[name]; |
- if (index == null) { |
- index = strings.length; |
- strings.add(name); |
- stringIndices[name] = index; |
- } |
- return index; |
- } |
- |
- void setIndex(List<int> list, int index, value) { |
- while (list.length <= index) { |
- list.add(-1); |
- } |
- list[index] = value; |
- } |
- |
- system.symbolByFletchSelectorId.forEach((Pair<int, String> pair) { |
- setIndex(selectors, pair.fst, newName(pair.snd)); |
- }); |
- |
- Map<int, FletchClass> functionId2Class = {}; |
- system.classesById.forEach((Pair<int, FletchClass> pair) { |
- FletchClass klass = pair.snd; |
- klass.methodTable.forEach((Pair<int, int> pair) { |
- int functionId = pair.snd; |
- functionId2Class[functionId] = klass; |
- }); |
- }); |
- |
- Map<Configuration, Map<int, int>> newTable() { |
- return <Configuration, Map<int, int>>{ |
- Configuration.Offset64BitsDouble : <int,int>{}, |
- Configuration.Offset64BitsFloat : <int,int>{}, |
- Configuration.Offset32BitsDouble : <int,int>{}, |
- Configuration.Offset32BitsFloat : <int,int>{}, |
- }; |
- } |
- |
- fillTable(Map<Configuration, Map<int, int>> dst, |
- Int32List list, |
- String symbol(int id)) { |
- for (int offset = 0; offset < list.length; offset += 5) { |
- int id = list[offset + 0]; |
- int stringId = newName(symbol(id)); |
- |
- if (stringId != -1) { |
- dst[Configuration.Offset64BitsDouble][list[offset + 1]] = stringId; |
- dst[Configuration.Offset64BitsFloat][list[offset + 2]] = stringId; |
- dst[Configuration.Offset32BitsDouble][list[offset + 3]] = stringId; |
- dst[Configuration.Offset32BitsFloat][list[offset + 4]] = stringId; |
- } |
- } |
- } |
- |
- var functionNames = newTable(); |
- var classNames = newTable(); |
- |
- fillTable(functionNames, |
- result.functionOffsetTable, |
- (id) => system.functionsById[id].name); |
- fillTable(classNames, |
- result.classOffsetTable, |
- (id) { |
- FletchClass klass = system.classesById[id]; |
- if (klass == null) { |
- // Why do we get here? |
- return null; |
- } |
- return klass.name; |
- }); |
- fillTable(classNames, |
- result.functionOffsetTable, |
- (id) { |
- FletchClass klass = functionId2Class[id]; |
- if (klass != null) return klass.name; |
- return null; |
- }); |
- |
- return new ProgramInfo(strings, selectors, classNames, |
- functionNames, result.hashtag); |
-} |
- |
-final RegExp _FrameRegexp = |
- new RegExp(r'^Frame +([0-9]+): Function\(([0-9]+)\)$'); |
- |
-final RegExp _NSMRegexp = |
- new RegExp(r'^NoSuchMethodError\(([0-9]+), ([0-9]+)\)$'); |
- |
-Stream<String> decodeStackFrames(Configuration conf, |
- ProgramInfo info, |
- Stream<String> input) async* { |
- await for (String line in input) { |
- Match frameMatch = _FrameRegexp.firstMatch(line); |
- Match nsmMatch = _NSMRegexp.firstMatch(line); |
- if (frameMatch != null) { |
- String frameNr = frameMatch.group(1); |
- int functionOffset = int.parse(frameMatch.group(2)); |
- |
- String className = info.classNameOfFunction(conf, functionOffset); |
- String functionName = info.functionName(conf, functionOffset); |
- |
- if (className == null) { |
- yield ' $frameNr: $functionName\n'; |
- } else { |
- yield ' $frameNr: $className.$functionName\n'; |
- } |
- } else if (nsmMatch != null) { |
- int classOffset = int.parse(nsmMatch.group(1)); |
- FletchSelector selector = |
- new FletchSelector(int.parse(nsmMatch.group(2))); |
- String functionName = info.selectorName(selector); |
- String className = info.className(conf, classOffset); |
- |
- if (className != null && functionName != null) { |
- yield 'NoSuchMethodError: $className.$functionName\n'; |
- } else if (functionName != null) { |
- yield 'NoSuchMethodError: $functionName\n'; |
- } else { |
- yield 'NoSuchMethodError: <unknown method>\n'; |
- } |
- } else { |
- yield '$line\n'; |
- } |
- } |
-} |
- |
-Future<int> decodeProgramMain( |
- List<String> arguments, |
- Stream<List<int>> input, |
- StreamSink<List<int>> output) async { |
- |
- usage(message) { |
- print("Invalid arguments: $message"); |
- print("Usage: ${io.Platform.script} " |
- "<32/64> <float/double> <snapshot.info.{json/bin}>"); |
- } |
- |
- if (arguments.length != 3) { |
- usage("Exactly 3 arguments must be supplied"); |
- return 1; |
- } |
- |
- String bits = arguments[0]; |
- if (!['32', '64'].contains(bits)) { |
- usage("Bit width must be 32 or 64."); |
- return 1; |
- } |
- |
- String floatOrDouble = arguments[1]; |
- if (!['float', 'double'].contains(floatOrDouble)) { |
- usage("Floating point argument must be 'float' or 'double'."); |
- return 1; |
- } |
- |
- String filename = arguments[2]; |
- bool isJsonFile = filename.endsWith('.json'); |
- bool isBinFile = filename.endsWith('.bin'); |
- if (!isJsonFile && !isBinFile) { |
- usage("The program info file must end in '.bin' or '.json' " |
- "(was: '$filename')."); |
- return 1; |
- } |
- |
- io.File file = new io.File(filename); |
- if (!await file.exists()) { |
- usage("The file '$filename' does not exist."); |
- return 1; |
- } |
- |
- ProgramInfo info; |
- |
- if (isJsonFile) { |
- info = ProgramInfoJson.decode(await file.readAsString()); |
- } else { |
- info = ProgramInfoBinary.decode(await file.readAsBytes()); |
- } |
- |
- Stream<String> inputLines = |
- input.transform(UTF8.decoder).transform(new LineSplitter()); |
- |
- Configuration conf = _getConfiguration(bits, floatOrDouble); |
- Stream<String> decodedFrames = decodeStackFrames(conf, info, inputLines); |
- await decodedFrames.transform(UTF8.encoder).pipe(output); |
- |
- return 0; |
-} |
- |
- |
-// We are only interested in two kind of lines in the fletch.ticks file. |
-final RegExp tickRegexp = |
- new RegExp(r'^0x([0-9a-f]+),0x([0-9a-f]+),0x([0-9a-f]+)'); |
-final RegExp propertyRegexp = new RegExp(r'^(\w+)=(.*$)'); |
- |
-// Tick contains information from a line matching tickRegexp. |
-class Tick { |
- final int pc; // The actual program counter where the tick occurred. |
- final int bcp; // The bytecode pointer relative to program heap start. |
- final int hashtag; |
- Tick(this.pc, this.bcp,this.hashtag); |
-} |
- |
-// Property contains information from a line matching propertyRegexp. |
-class Property { |
- final String name; |
- final String value; |
- Property(this.name, this.value); |
-} |
- |
-// FunctionInfo captures profiler information for a function. |
-class FunctionInfo { |
- int ticks = 0; // Accumulated number of ticks. |
- final String name; // Name that indentifies the function. |
- |
- FunctionInfo(this.name); |
- |
- int Percent(int total_ticks) => ticks * 100 ~/ total_ticks; |
- |
- void Print(int total_ticks) { |
- print(" -${Percent(total_ticks).toString().padLeft(3, ' ')}% $name"); |
- } |
- |
- static String ComputeName(String function_name, String class_name) { |
- if (class_name == null) return function_name; |
- return "$class_name.$function_name"; |
- } |
-} |
- |
-Stream decode(Stream<List<int>> input) async* { |
- Stream<String> inputLines = |
- input.transform(UTF8.decoder).transform(new LineSplitter()); |
- await for (String line in inputLines) { |
- Match t = tickRegexp.firstMatch(line); |
- if (t != null) { |
- int pc = int.parse(t.group(1), radix: 16); |
- int offset = 0; |
- int hashtag = 0; |
- if (t.groupCount > 1) { |
- offset = int.parse(t.group(2), radix: 16); |
- hashtag = int.parse(t.group(3), radix: 16); |
- } |
- yield new Tick(pc, offset, hashtag); |
- } else { |
- t = propertyRegexp.firstMatch(line); |
- if (t != null) yield new Property(t.group(1), t.group(2)); |
- } |
- } |
-} |
- |
-// Binary search for named entry start. |
-NamedEntry findEntry(List<NamedEntry> functions, Tick t) { |
- int low = 0; |
- int high = functions.length - 1; |
- while (low + 1 < high) { |
- int i = low + ((high - low) ~/ 2); |
- NamedEntry current = functions[i]; |
- if (current.offset < t.bcp) { |
- low = i; |
- } else { |
- high = i; |
- } |
- } |
- return functions[low]; |
-} |
- |
-// NamedEntry captures a named entry caputred in the .info.json file. |
-class NamedEntry { |
- final int offset; |
- final String name; |
- NamedEntry(this.offset, this.name); |
-} |
- |
-class Profile { |
- final String sample_filename; |
- final String info_filename; |
- Profile(this.sample_filename,this.info_filename); |
- |
- // Tick information. |
- int total_ticks = 0; |
- int runtime_ticks = 0; |
- int interpreter_ticks = 0; |
- int discarded_ticks = 0; |
- int other_snapshot_ticks = 0; |
- |
- // Memory model. |
- String model; |
- |
- // All the ticks. |
- List<Tick> ticks = <Tick>[]; |
- |
- // The resulting histogram. |
- List<FunctionInfo> histogram; |
- |
- void Print() { |
- print("# Tick based profiler result."); |
- |
- for (FunctionInfo func in histogram) { |
- if (func.Percent(total_ticks) < 2) break; |
- func.Print(total_ticks); |
- } |
- |
- print("# ticks in interpreter=${interpreter_ticks}"); |
- if (runtime_ticks > 0) print(" runtime=${runtime_ticks}"); |
- if (discarded_ticks > 0) print(" discarded=${discarded_ticks}"); |
- if (other_snapshot_ticks> 0) { |
- print(" other_snapshot=${other_snapshot_ticks}"); |
- } |
- } |
-} |
- |
-Future<Profile> decodeTickSamples( |
- List<String> arguments, |
- Stream<List<int>> input, |
- StreamSink<List<int>> output) async { |
- |
- usage(message) { |
- print("Invalid arguments: $message"); |
- print("Usage: ${io.Platform.script} <fletch.ticks> <snapshot.info.json>"); |
- } |
- |
- if (arguments.length != 2) { |
- usage("Exactly 2 arguments must be supplied"); |
- return null; |
- } |
- |
- String sample_filename = arguments[0]; |
- io.File sample_file = new io.File(sample_filename); |
- if (!await sample_file.exists()) { |
- usage("The file '$sample_filename' does not exist."); |
- return null; |
- } |
- |
- String info_filename = arguments[1]; |
- if (!info_filename.endsWith('.info.json')) { |
- usage("The program info file must end in '.info.json' " |
- "(was: '$info_filename')."); |
- return null; |
- } |
- |
- io.File info_file = new io.File(info_filename); |
- if (!await info_file.exists()) { |
- usage("The file '$info_filename' does not exist."); |
- return null; |
- } |
- |
- ProgramInfo info = ProgramInfoJson.decode(await info_file.readAsString()); |
- Profile profile = new Profile(sample_filename, info_filename); |
- |
- // Process the tick sample file. |
- await for (var t in decode(sample_file.openRead())) { |
- if (t is Tick) { |
- profile.ticks.add(t); |
- } else if (t is Property) { |
- if (t.name == 'discarded') profile.discarded_ticks = int.parse(t.value); |
- if (t.name == 'model') profile.model = t.value; |
- } |
- } |
- if (profile.model == null) { |
- print("Memory model absent in sample file."); |
- return null; |
- } |
- |
- // Compute the configuration key based on the memory model. |
- Configuration conf; |
- String model = profile.model; |
- if (model == 'b64double') { |
- conf = Configuration.Offset64BitsDouble; |
- } else if (model == 'b64float') { |
- conf = Configuration.Offset64BitsFloat; |
- } else if (model == 'b32double') { |
- conf = Configuration.Offset32BitsDouble; |
- } else if (model == 'b32float') { |
- conf = Configuration.Offset32BitsFloat; |
- } else { |
- print("Memory model in sample file ${model} cannot be recognized."); |
- return null; |
- } |
- |
- // Compute a offset sorted list of Function entries. |
- List<NamedEntry> functions = new List<NamedEntry>(); |
- Map<int,int> fnames = info._functionNames[conf]; |
- fnames.forEach((key, value) { |
- functions.add(new NamedEntry(key, info._getString(value))); |
- }); |
- functions.sort((a, b) => a.offset - b.offset); |
- |
- // Compute a offset sorted list of Class entries. |
- List<NamedEntry> classes = new List<NamedEntry>(); |
- Map<int,int> cnames = info._classNames[conf]; |
- cnames.forEach((key, value) { |
- classes.add(new NamedEntry(key, info._getString(value))); |
- }); |
- classes.sort((a, b) => a.offset - b.offset); |
- |
- Map<String,FunctionInfo> results = <String,FunctionInfo>{}; |
- for (Tick t in profile.ticks) { |
- profile.total_ticks++; |
- if (t.bcp == 0) { |
- profile.runtime_ticks++; |
- } else if (t.hashtag != info.hashtag) { |
- profile.other_snapshot_ticks++; |
- } else { |
- profile.interpreter_ticks++; |
- NamedEntry fe = findEntry(functions, t); |
- if (fe?.name != null) { |
- NamedEntry ce = findEntry(classes, t); |
- String key = FunctionInfo.ComputeName(fe.name, ce?.name); |
- FunctionInfo f = |
- results.putIfAbsent(key, () => new FunctionInfo(key)); |
- f.ticks++; |
- } |
- } |
- } |
- |
- // Sort the values into the histogram. |
- List<FunctionInfo> histogram = |
- new List<FunctionInfo>.from(results.values); |
- histogram.sort((a,b) { return b.ticks - a.ticks; }); |
- profile.histogram = histogram; |
- |
- return profile; |
-} |
- |
-Configuration _getConfiguration(bits, floatOrDouble) { |
- if (bits == '64') { |
- if (floatOrDouble == 'float') return Configuration.Offset64BitsFloat; |
- else if (floatOrDouble == 'double') return Configuration.Offset64BitsDouble; |
- } else if (bits == '32') { |
- if (floatOrDouble == 'float') return Configuration.Offset32BitsFloat; |
- else if (floatOrDouble == 'double') return Configuration.Offset32BitsDouble; |
- } |
- throw 'Invalid arguments'; |
-} |