Index: runtime/observatory/lib/src/service/object.dart |
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart |
index 7fde8e9b7aa07da273171808b4ebe839c3d0001d..c6b1808af895929f1bcdf395cdad248a2a63492f 100644 |
--- a/runtime/observatory/lib/src/service/object.dart |
+++ b/runtime/observatory/lib/src/service/object.dart |
@@ -577,11 +577,12 @@ abstract class VM extends ServiceObjectOwner { |
Future<ServiceObject> invokeRpc(String method, Map params) { |
return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
- var obj = new ServiceObject._fromMap(this, response); |
- if (obj.canCache) { |
- _cache.putIfAbsent(id, () => obj); |
- } |
- return obj; |
+ var obj = new ServiceObject._fromMap(this, response); |
+ if ((obj != null) && obj.canCache) { |
+ String objId = obj.id; |
+ _cache.putIfAbsent(objId, () => obj); |
+ } |
+ return obj; |
}); |
} |
@@ -777,47 +778,16 @@ class Isolate extends ServiceObjectOwner with Coverage { |
assert(owner is VM); |
} |
- static const TAG_ROOT_ID = 'code/tag-0'; |
- |
- /// Returns the Code object for the root tag. |
- Code tagRoot() { |
- // TODO(turnidge): Use get() here instead? |
- return _cache[TAG_ROOT_ID]; |
- } |
- |
- void processProfile(ServiceMap profile) { |
- assert(profile.type == 'CpuProfile'); |
- var codeTable = new List<Code>(); |
- var codeRegions = profile['codes']; |
- for (var codeRegion in codeRegions) { |
- Code code = codeRegion['code']; |
- assert(code != null); |
- codeTable.add(code); |
- } |
- _resetProfileData(); |
- _updateProfileData(profile, codeTable); |
- var exclusiveTrie = profile['exclusive_trie']; |
- if (exclusiveTrie != null) { |
- profileTrieRoot = _processProfileTrie(exclusiveTrie, codeTable); |
- } |
- } |
- |
- void _resetProfileData() { |
+ void resetCachedProfileData() { |
_cache.values.forEach((value) { |
- if (value is Code) { |
- Code code = value; |
- code.resetProfileData(); |
- } |
- }); |
- } |
- |
- void _updateProfileData(ServiceMap profile, List<Code> codeTable) { |
- var codeRegions = profile['codes']; |
- var sampleCount = profile['samples']; |
- for (var codeRegion in codeRegions) { |
- Code code = codeRegion['code']; |
- code.updateProfileData(codeRegion, codeTable, sampleCount); |
- } |
+ if (value is Code) { |
+ Code code = value; |
+ code.profile = null; |
+ } else if (value is ServiceFunction) { |
+ ServiceFunction function = value; |
+ function.profile = null; |
+ } |
+ }); |
} |
/// Fetches and builds the class hierarchy for this isolate. Returns the |
@@ -861,16 +831,16 @@ class Isolate extends ServiceObjectOwner with Coverage { |
if (map == null) { |
return null; |
} |
- String id = map['id']; |
- var obj = _cache[id]; |
+ String mapId = map['id']; |
+ var obj = (mapId != null) ? _cache[mapId] : null; |
if (obj != null) { |
// Consider calling update when map is not a reference. |
return obj; |
} |
// Build the object from the map directly. |
obj = new ServiceObject._fromMap(this, map); |
- if (obj != null && obj.canCache) { |
- _cache[id] = obj; |
+ if ((obj != null) && obj.canCache) { |
+ _cache[mapId] = obj; |
} |
return obj; |
} |
@@ -882,11 +852,12 @@ class Isolate extends ServiceObjectOwner with Coverage { |
Future<ServiceObject> invokeRpc(String method, Map params) { |
return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
- var obj = new ServiceObject._fromMap(this, response); |
- if (obj.canCache) { |
- _cache.putIfAbsent(id, () => obj); |
- } |
- return obj; |
+ var obj = new ServiceObject._fromMap(this, response); |
+ if ((obj != null) && obj.canCache) { |
+ String objId = obj.id; |
+ _cache.putIfAbsent(objId, () => obj); |
+ } |
+ return obj; |
}); |
} |
@@ -1042,51 +1013,6 @@ class Isolate extends ServiceObjectOwner with Coverage { |
}); |
} |
- @reflectable CodeTrieNode profileTrieRoot; |
- // The profile trie is serialized as a list of integers. Each node |
- // is recreated by consuming some portion of the list. The format is as |
- // follows: |
- // [0] index into codeTable of code object. |
- // [1] tick count (number of times this stack frame occured). |
- // [2] child node count |
- // Reading the trie is done by recursively reading the tree depth-first |
- // pre-order. |
- CodeTrieNode _processProfileTrie(List<int> data, List<Code> codeTable) { |
- // Setup state shared across calls to _readTrieNode. |
- _trieDataCursor = 0; |
- _trieData = data; |
- if (_trieData == null) { |
- return null; |
- } |
- if (_trieData.length < 3) { |
- // Not enough integers for 1 node. |
- return null; |
- } |
- // Read the tree, returns the root node. |
- return _readTrieNode(codeTable); |
- } |
- int _trieDataCursor; |
- List<int> _trieData; |
- CodeTrieNode _readTrieNode(List<Code> codeTable) { |
- // Read index into code table. |
- var index = _trieData[_trieDataCursor++]; |
- // Lookup code object. |
- var code = codeTable[index]; |
- // Frame counter. |
- var count = _trieData[_trieDataCursor++]; |
- // Create node. |
- var node = new CodeTrieNode(code, count); |
- // Number of children. |
- var children = _trieData[_trieDataCursor++]; |
- // Recursively read child nodes. |
- for (var i = 0; i < children; i++) { |
- var child = _readTrieNode(codeTable); |
- node.children.add(child); |
- node.summedChildCount += child.count; |
- } |
- return node; |
- } |
- |
ObservableList<Breakpoint> breakpoints = new ObservableList(); |
void _removeBreakpoint(Breakpoint bpt) { |
@@ -1868,7 +1794,7 @@ class FunctionKind { |
final String _strValue; |
FunctionKind._internal(this._strValue); |
toString() => _strValue; |
- bool isFake() => [kCollected, kNative, kTag, kReused].contains(this); |
+ bool isSynthetic() => [kCollected, kNative, kTag, kReused].contains(this); |
static FunctionKind fromJSON(String value) { |
switch(value) { |
@@ -1927,6 +1853,10 @@ class ServiceFunction extends ServiceObject with Coverage { |
@observable String qualifiedName; |
@observable int usageCounter; |
@observable bool isDart; |
+ @observable ProfileFunction profile; |
+ |
+ bool get canCache => true; |
+ bool get immutable => false; |
ServiceFunction._empty(ServiceObject owner) : super._empty(owner); |
@@ -1939,7 +1869,7 @@ class ServiceFunction extends ServiceObject with Coverage { |
owningClass = map.containsKey('owningClass') ? map['owningClass'] : null; |
owningLibrary = map.containsKey('owningLibrary') ? map['owningLibrary'] : null; |
kind = FunctionKind.fromJSON(map['kind']); |
- isDart = !kind.isFake(); |
+ isDart = !kind.isSynthetic(); |
if (parent == null) { |
qualifiedName = (owningClass != null) ? |
@@ -1949,7 +1879,9 @@ class ServiceFunction extends ServiceObject with Coverage { |
qualifiedName = "${parent.qualifiedName}.${name}"; |
} |
- if (mapIsRef) { return; } |
+ if (mapIsRef) { |
+ return; |
+ } |
isStatic = map['static']; |
isConst = map['const']; |
@@ -1963,7 +1895,6 @@ class ServiceFunction extends ServiceObject with Coverage { |
isInlinable = map['inlinable']; |
deoptimizations = map['deoptimizations']; |
usageCounter = map['usageCounter']; |
- |
} |
} |
@@ -2206,13 +2137,6 @@ class Script extends ServiceObject with Coverage { |
} |
} |
-class CodeTick { |
- final int address; |
- final int exclusiveTicks; |
- final int inclusiveTicks; |
- CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks); |
-} |
- |
class PcDescriptor extends Observable { |
final int pcOffset; |
@reflectable final int deoptId; |
@@ -2356,51 +2280,11 @@ class CodeInstruction extends Observable { |
@reflectable List<PcDescriptor> descriptors = |
new ObservableList<PcDescriptor>(); |
- static String formatPercent(num a, num total) { |
- var percent = 100.0 * (a / total); |
- return '${percent.toStringAsFixed(2)}%'; |
- } |
- |
CodeInstruction(this.address, this.pcOffset, this.machine, this.human); |
@reflectable bool get isComment => address == 0; |
@reflectable bool get hasDescriptors => descriptors.length > 0; |
- @reflectable String formattedAddress() { |
- if (address == 0) { |
- return ''; |
- } |
- return '0x${address.toRadixString(16)}'; |
- } |
- |
- @reflectable String formattedInclusive(Code code) { |
- if (code == null) { |
- return ''; |
- } |
- var tick = code.addressTicks[address]; |
- if (tick == null) { |
- return ''; |
- } |
- // Don't show inclusive ticks if they are the same as exclusive ticks. |
- if (tick.inclusiveTicks == tick.exclusiveTicks) { |
- return ''; |
- } |
- var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile); |
- return '$pcent (${tick.inclusiveTicks})'; |
- } |
- |
- @reflectable String formattedExclusive(Code code) { |
- if (code == null) { |
- return ''; |
- } |
- var tick = code.addressTicks[address]; |
- if (tick == null) { |
- return ''; |
- } |
- var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile); |
- return '$pcent (${tick.exclusiveTicks})'; |
- } |
- |
bool _isJumpInstruction() { |
return human.startsWith('j'); |
} |
@@ -2430,8 +2314,6 @@ class CodeInstruction extends Observable { |
} |
int address = _getJumpAddress(); |
if (address == 0) { |
- // Could not determine jump address. |
- Logger.root.severe('Could not determine jump address for $human'); |
return; |
} |
for (var i = 0; i < instructions.length; i++) { |
@@ -2441,8 +2323,6 @@ class CodeInstruction extends Observable { |
return; |
} |
} |
- Logger.root.severe( |
- 'Could not find instruction at ${address.toRadixString(16)}'); |
} |
} |
@@ -2473,55 +2353,33 @@ class CodeKind { |
static const Tag = const CodeKind._internal('Tag'); |
} |
-class CodeCallCount { |
- final Code code; |
- final int count; |
- CodeCallCount(this.code, this.count); |
-} |
- |
-class CodeTrieNode { |
- final Code code; |
- final int count; |
- final children = new List<CodeTrieNode>(); |
- int summedChildCount = 0; |
- CodeTrieNode(this.code, this.count); |
+class CodeInlineInterval { |
+ final int start; |
+ final int end; |
+ final List<ServiceFunction> functions = new List<ServiceFunction>(); |
+ bool contains(int pc) => (pc >= start) && (pc < end); |
+ CodeInlineInterval(this.start, this.end); |
} |
class Code extends ServiceObject { |
@observable CodeKind kind; |
- @observable int totalSamplesInProfile = 0; |
- @reflectable int exclusiveTicks = 0; |
- @reflectable int inclusiveTicks = 0; |
- @reflectable int startAddress = 0; |
- @reflectable int endAddress = 0; |
- @reflectable final callers = new List<CodeCallCount>(); |
- @reflectable final callees = new List<CodeCallCount>(); |
- @reflectable final instructions = new ObservableList<CodeInstruction>(); |
- @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); |
- @observable String formattedInclusiveTicks = ''; |
- @observable String formattedExclusiveTicks = ''; |
@observable Instance objectPool; |
@observable ServiceFunction function; |
@observable Script script; |
@observable bool isOptimized = false; |
- |
+ @reflectable int startAddress = 0; |
+ @reflectable int endAddress = 0; |
+ @reflectable final instructions = new ObservableList<CodeInstruction>(); |
+ @observable ProfileCode profile; |
+ final List<CodeInlineInterval> inlineIntervals = |
+ new List<CodeInlineInterval>(); |
+ final ObservableList<ServiceFunction> inlinedFunctions = |
+ new ObservableList<ServiceFunction>(); |
bool get canCache => true; |
bool get immutable => true; |
Code._empty(ServiceObjectOwner owner) : super._empty(owner); |
- // Reset all data associated with a profile. |
- void resetProfileData() { |
- totalSamplesInProfile = 0; |
- exclusiveTicks = 0; |
- inclusiveTicks = 0; |
- formattedInclusiveTicks = ''; |
- formattedExclusiveTicks = ''; |
- callers.clear(); |
- callees.clear(); |
- addressTicks.clear(); |
- } |
- |
void _updateDescriptors(Script script) { |
this.script = script; |
for (var instruction in instructions) { |
@@ -2570,49 +2428,6 @@ class Code extends ServiceObject { |
return new Future.value(this); |
} |
- void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) { |
- // Assert that this has been cleared. |
- assert(calls.length == 0); |
- // Resolve. |
- for (var i = 0; i < data.length; i += 2) { |
- var index = int.parse(data[i]); |
- var count = int.parse(data[i + 1]); |
- assert(index >= 0); |
- assert(index < codes.length); |
- calls.add(new CodeCallCount(codes[index], count)); |
- } |
- // Sort to descending count order. |
- calls.sort((a, b) => b.count - a.count); |
- } |
- |
- |
- static String formatPercent(num a, num total) { |
- var percent = 100.0 * (a / total); |
- return '${percent.toStringAsFixed(2)}%'; |
- } |
- |
- void updateProfileData(Map profileData, |
- List<Code> codeTable, |
- int sampleCount) { |
- // Assert we are handed profile data for this code object. |
- assert(profileData['code'] == this); |
- totalSamplesInProfile = sampleCount; |
- inclusiveTicks = int.parse(profileData['inclusive_ticks']); |
- exclusiveTicks = int.parse(profileData['exclusive_ticks']); |
- _resolveCalls(callers, profileData['callers'], codeTable); |
- _resolveCalls(callees, profileData['callees'], codeTable); |
- var ticks = profileData['ticks']; |
- if (ticks != null) { |
- _processTicks(ticks); |
- } |
- formattedInclusiveTicks = |
- '${formatPercent(inclusiveTicks, totalSamplesInProfile)} ' |
- '($inclusiveTicks)'; |
- formattedExclusiveTicks = |
- '${formatPercent(exclusiveTicks, totalSamplesInProfile)} ' |
- '($exclusiveTicks)'; |
- } |
- |
void _update(ObservableMap m, bool mapIsRef) { |
name = m['name']; |
vmName = (m.containsKey('vmName') ? m['vmName'] : name); |
@@ -2621,6 +2436,10 @@ class Code extends ServiceObject { |
startAddress = int.parse(m['start'], radix:16); |
endAddress = int.parse(m['end'], radix:16); |
function = isolate.getFromMap(m['function']); |
+ if (mapIsRef) { |
+ return; |
+ } |
+ _loaded = true; |
objectPool = isolate.getFromMap(m['objectPool']); |
var disassembly = m['disassembly']; |
if (disassembly != null) { |
@@ -2631,9 +2450,56 @@ class Code extends ServiceObject { |
descriptors = descriptors['members']; |
_processDescriptors(descriptors); |
} |
- // We are loaded if we have instructions or are not Dart code. |
- _loaded = (instructions.length != 0) || (kind != CodeKind.Dart); |
hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); |
+ inlinedFunctions.clear(); |
+ var inlinedFunctionsTable = m['inlinedFunctions']; |
+ var inlinedIntervals = m['inlinedIntervals']; |
+ if (inlinedFunctionsTable != null) { |
+ // Iterate and upgrade each ServiceFunction. |
+ for (var i = 0; i < inlinedFunctionsTable.length; i++) { |
+ // Upgrade each function and set it back in the list. |
+ var func = isolate.getFromMap(inlinedFunctionsTable[i]); |
+ inlinedFunctionsTable[i] = func; |
+ if (!inlinedFunctions.contains(func)) { |
+ inlinedFunctions.add(func); |
+ } |
+ } |
+ } |
+ if ((inlinedIntervals == null) || (inlinedFunctionsTable == null)) { |
+ // No inline information. |
+ inlineIntervals.clear(); |
+ return; |
+ } |
+ _processInline(inlinedFunctionsTable, inlinedIntervals); |
+ } |
+ |
+ CodeInlineInterval findInterval(int pc) { |
+ for (var i = 0; i < inlineIntervals.length; i++) { |
+ var interval = inlineIntervals[i]; |
+ if (interval.contains(pc)) { |
+ return interval; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ void _processInline(List<ServiceFunction> inlinedFunctionsTable, |
+ List<List<int>> inlinedIntervals) { |
+ for (var i = 0; i < inlinedIntervals.length; i++) { |
+ var inlinedInterval = inlinedIntervals[i]; |
+ var start = inlinedInterval[0] + startAddress; |
+ var end = inlinedInterval[1] + startAddress; |
+ var codeInlineInterval = new CodeInlineInterval(start, end); |
+ for (var i = 2; i < inlinedInterval.length - 1; i++) { |
+ var inline_id = inlinedInterval[i]; |
+ if (inline_id < 0) { |
+ continue; |
+ } |
+ var function = inlinedFunctionsTable[inline_id]; |
+ codeInlineInterval.functions.add(function); |
+ } |
+ inlineIntervals.add(codeInlineInterval); |
+ } |
} |
@observable bool hasDisassembly = false; |
@@ -2687,49 +2553,11 @@ class Code extends ServiceObject { |
} |
} |
- void _processTicks(List<String> profileTicks) { |
- assert(profileTicks != null); |
- assert((profileTicks.length % 3) == 0); |
- for (var i = 0; i < profileTicks.length; i += 3) { |
- var address = int.parse(profileTicks[i], radix:16); |
- var exclusive = int.parse(profileTicks[i + 1]); |
- var inclusive = int.parse(profileTicks[i + 2]); |
- var tick = new CodeTick(address, exclusive, inclusive); |
- addressTicks[address] = tick; |
- } |
- } |
- |
/// Returns true if [address] is contained inside [this]. |
bool contains(int address) { |
return (address >= startAddress) && (address < endAddress); |
} |
- /// Sum all caller counts. |
- int sumCallersCount() => _sumCallCount(callers); |
- /// Specific caller count. |
- int callersCount(Code code) => _callCount(callers, code); |
- /// Sum of callees count. |
- int sumCalleesCount() => _sumCallCount(callees); |
- /// Specific callee count. |
- int calleesCount(Code code) => _callCount(callees, code); |
- |
- int _sumCallCount(List<CodeCallCount> calls) { |
- var sum = 0; |
- for (CodeCallCount caller in calls) { |
- sum += caller.count; |
- } |
- return sum; |
- } |
- |
- int _callCount(List<CodeCallCount> calls, Code code) { |
- for (CodeCallCount caller in calls) { |
- if (caller.code == code) { |
- return caller.count; |
- } |
- } |
- return 0; |
- } |
- |
@reflectable bool get isDartCode => kind == CodeKind.Dart; |
} |