Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1257,13 +1257,22 @@ class _CpuProfileTimelineTree {
return null;
}

String? get resolvedUrl => isCodeTree && _function is vm_service.FuncRef?
?
String? get resolvedUrl {
if (isCodeTree) {
if (_function is vm_service.FuncRef?) {
// TODO(bkonyi): not sure if this is a resolved URL or not, but it's not
// critical since this is only displayed when advanced developer mode is
// enabled.
(_function as vm_service.FuncRef?)?.location?.script?.uri
: samples.functions?[index].resolvedUrl;
return (_function as vm_service.FuncRef?)?.location?.script?.uri;
}
} else {
final functions = samples.functions;
if (functions == null || index >= functions.length) return null;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check index >= functions.length is not strictly necessary but I want to be extra safe.

return functions[index].resolvedUrl;
}

return null;
}

int? get sourceLine {
final function = _function;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.

import 'package:flutter/foundation.dart';
import 'package:vm_service/vm_service.dart';

import '../../service/vm_flags.dart' as vm_flags;
Expand All @@ -28,6 +29,19 @@ extension CpuProfilerExtension on VmService {
final cpuSamples = await serviceConnection.serviceManager.service!
.getCpuSamples(isolateId, startMicros, extentMicros);

return processCpuSamples(
cpuSamples: cpuSamples,
isolateId: isolateId,
advancedDeveloperModeEnabled: advancedDeveloperModeEnabled,
);
}

@visibleForTesting
Future<CpuProfilePair> processCpuSamples({
required CpuSamples cpuSamples,
required String isolateId,
required bool advancedDeveloperModeEnabled,
}) async {
// If advanced developer mode is enabled, getCpuSamples will also include
// code profile details automatically (e.g., code stacks and a list of code
// objects).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.

import 'dart:convert';
import 'dart:io';

import 'package:devtools_app/src/screens/profiler/cpu_profile_model.dart';
import 'package:devtools_app/src/screens/profiler/cpu_profile_service.dart';
import 'package:devtools_app/src/service/service_manager.dart';
import 'package:devtools_app/src/shared/globals.dart';
import 'package:devtools_app/src/shared/primitives/utils.dart';
import 'package:devtools_app_shared/utils.dart';
import 'package:devtools_test/devtools_test.dart';
Expand Down Expand Up @@ -40,6 +45,27 @@ void main() {
expect(filtered.profileMetaData.time!.end, 0);
});

// Regression test for https://github.com/flutter/devtools/issues/9526.
test(
'code profile loads in advanced developer mode (regression test)',
() async {
final cpuSamplesFile = File(
'test/test_infra/test_data/cpu_profiler/cpu_samples_with_code_profile.json',
);
final cpuSamplesJson = jsonDecode(cpuSamplesFile.readAsStringSync());
final cpuSamples = CpuSamples.parse(cpuSamplesJson)!;

final profilePair = await serviceConnection.serviceManager.service!
.processCpuSamples(
cpuSamples: cpuSamples,
isolateId: goldenSamplesIsolate,
advancedDeveloperModeEnabled: true,
);
expect(profilePair.codeProfile, isNotNull);
expect(profilePair.functionProfile, isNotNull);
},
);

test('init from parse', () {
expect(
cpuProfileData.stackFramesJson,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"type": "CpuSamples",
"samplePeriod": 50,
"maxStackDepth": 10,
"sampleCount": 1,
"timeOriginMicros": 0,
"timeExtentMicros": 100,
"pid": 12345,
"functions": [
{
"kind": "Dart",
"inclusiveTicks": 1,
"exclusiveTicks": 0,
"resolvedUrl": "file:///main.dart",
"function": {
"type": "@Function",
"id": "func-0",
"name": "main",
"owner": { "type": "@Library", "id": "lib-0", "name": "", "uri": "file:///main.dart" },
"isStatic": true,
"isConst": false,
"implicit": false
}
},
{
"kind": "Dart",
"inclusiveTicks": 1,
"exclusiveTicks": 1,
"resolvedUrl": "file:///main.dart",
"function": {
"type": "@Function",
"id": "func-1",
"name": "helperFunction",
"owner": { "type": "@Library", "id": "lib-0", "name": "", "uri": "file:///main.dart" },
"isStatic": true,
"isConst": false,
"implicit": false
}
}
],
"_codes": [
{
"kind": "Dart",
"inclusiveTicks": 1,
"exclusiveTicks": 0,
"code": {
"type": "@Code",
"kind": "Dart",
"name": "main",
"id": "code-0",
"function": {
"type": "@Function",
"id": "func-0",
"name": "main",
"owner": { "type": "@Library", "id": "lib-0", "name": "", "uri": "file:///main.dart" },
"isStatic": true,
"isConst": false,
"implicit": false
}
}
},
{
"kind": "Dart",
"inclusiveTicks": 1,
"exclusiveTicks": 1,
"code": {
"type": "@Code",
"kind": "Dart",
"name": "helperFunction",
"id": "code-1",
"function": {
"type": "@Function",
"id": "func-1",
"name": "helperFunction",
"owner": { "type": "@Library", "id": "lib-0", "name": "", "uri": "file:///main.dart" },
"isStatic": true,
"isConst": false,
"implicit": false
}
}
},
{
"kind": "Stub",
"inclusiveTicks": 1,
"exclusiveTicks": 0,
"code": {
"type": "@Code",
"kind": "Stub",
"name": "WriteBarrierStub",
"id": "code-2",
"function": {
"type": "NativeFunction",
"id": "native-2",
"name": "WriteBarrierStub"
}
}
}
],
"samples": [
{
"tid": 100,
"timestamp": 50,
"stack": [0, 1],
"_codeStack": [2]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before the change, the regression test would fail because we would try to index into functions[2] which only has 2 items

}
]
}
Loading