Skip to content

Commit

Permalink
chore!: Remove GlanceConfiguration.modulePathFilters and optimize the…
Browse files Browse the repository at this point in the history
… performance of aggregateStacks

choreRemove GlanceConfiguration.modulePathFilters and optimize the performance of aggregateStacks
  • Loading branch information
littleGnAl committed Oct 1, 2024
1 parent ecf54c5 commit a474c7d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 307 deletions.
14 changes: 1 addition & 13 deletions lib/src/glance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,14 @@ class GlanceConfiguration {
this.reporters = const [],
List<String> modulePathFilters = const [],
this.sampleRateInMilliseconds = kDefaultSampleRateInMilliseconds,
}) : _modulePathFilters = modulePathFilters;
});

/// The threshold in milliseconds for detecting UI jank. Defaults to [kDefaultJankThreshold].
final int jankThreshold;

/// A list of reporters that will handle the reporting of UI jank.
final List<GlanceReporter> reporters;

/// Filters with regex patterns for module paths.
/// If not set, [kAndroidDefaultModulePathFilters] is used on Android, and [kIOSDefaultModulePathFilters] is used on iOS.
final List<String> _modulePathFilters;
List<String> get modulePathFilters {
if (_modulePathFilters.isNotEmpty) {
return _modulePathFilters;
}
return defaultTargetPlatform == TargetPlatform.android
? kAndroidDefaultModulePathFilters
: kIOSDefaultModulePathFilters;
}

/// The interval in milliseconds for capture the stack traces. Defaults to [kDefaultSampleRateInMilliseconds].
/// Lower value will capture more accuracy stack traces, but will impace the performance.
final int sampleRateInMilliseconds;
Expand Down
2 changes: 0 additions & 2 deletions lib/src/glance_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,9 @@ class GlanceImpl implements Glance {
final jankThreshold = config.jankThreshold;
final sampleRateInMilliseconds = config.sampleRateInMilliseconds;
_reporters = List.of(config.reporters, growable: false);
final List<String> modulePathFilters = config.modulePathFilters;

_sampler ??= await Sampler.create(SamplerConfig(
jankThreshold: jankThreshold,
modulePathFilters: modulePathFilters,
sampleRateInMilliseconds: sampleRateInMilliseconds,
));

Expand Down
91 changes: 42 additions & 49 deletions lib/src/sampler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,11 @@ class SamplerConfig {
SamplerConfig({
required this.jankThreshold,
this.sampleRateInMilliseconds = kDefaultSampleRateInMilliseconds,
required this.modulePathFilters,
this.samplerProcessorFactory = _defaultSamplerProcessorFactory,
});

final int jankThreshold;

/// e.g., libapp.so, libflutter.so
final List<String> modulePathFilters;

final int sampleRateInMilliseconds;

/// The factory used to create a [SamplerProcessor]. This allows us to inject
Expand Down Expand Up @@ -200,21 +196,12 @@ class SamplerProcessor {
SendPort sendPort,
int messageId,
List<int> timestampRange,
) {
) async {
assert(isRunning);
assert(_buffer != null, 'Make sure you call `loop` first');

final args = [sendPort, _config, _buffer!, timestampRange, messageId];
return compute((args) {
final sendPort = (args as List)[0] as SendPort;
final config = args[1] as SamplerConfig;
final buffer = args[2] as RingBuffer<NativeStack>;
final timestampRange = args[3] as List<int>;
final id = args[4] as int;

final stacktrace = aggregateStacks(config, buffer, timestampRange);
sendPort.send(GetSamplesResponse(id, stacktrace));
}, args);
final stacktrace = aggregateStacks(_config, _buffer!, timestampRange);
sendPort.send(GetSamplesResponse(messageId, stacktrace));
}

/// Start an infinite loop to capture the [NativeStack] at intervals specified
Expand Down Expand Up @@ -255,22 +242,9 @@ class SamplerProcessor {
) {
void addOrUpdateAggregatedNativeFrame(
SamplerConfig config,
List<int> timestampRange,
LinkedHashMap<int, AggregatedNativeFrame> aggregatedFrameMap,
NativeFrame frame) {
List<String> modulePathFilters = config.modulePathFilters;

int start = timestampRange[0];
int end = timestampRange[1];

final isInclude = frame.module != null &&
frame.timestamp >= start &&
frame.timestamp <= end &&
modulePathFilters.any((pathFilter) {
return RegExp(pathFilter).hasMatch(frame.module!.path);
});

if (!isInclude) {
if (frame.module == null) {
return;
}
final pc = frame.pc;
Expand All @@ -285,17 +259,28 @@ class SamplerProcessor {
}
}

int startTimestamp = timestampRange[0];
int endTimestamp = timestampRange[1];

final maxOccurTimes =
config.jankThreshold / config.sampleRateInMilliseconds;

final parentFrameMap = LinkedHashMap<int,
LinkedHashMap<int, AggregatedNativeFrame>>.identity();

for (final nativeStack in buffer.readAll().reversed) {
if (nativeStack?.frames.isEmpty == true) {
for (final nativeStack in buffer.readAllReversed()) {
if (nativeStack.frames.isEmpty) {
continue;
}

final parentFrame = nativeStack.frames.last;
bool isInclude = parentFrame.timestamp >= startTimestamp &&
parentFrame.timestamp <= endTimestamp;
if (!isInclude) {
continue;
}
int parentFramePc = nativeStack!.frames.last.pc;

int parentFramePc = parentFrame.pc;
bool isContainParentFrame = parentFrameMap.containsKey(parentFramePc);

if (isContainParentFrame) {
Expand All @@ -305,31 +290,31 @@ class SamplerProcessor {
// Aggregate from parent.
for (int i = frames.length - 1; i >= 0; --i) {
addOrUpdateAggregatedNativeFrame(
config, timestampRange, aggregatedFrameMap, frames[i]);
config, aggregatedFrameMap, frames[i]);
}
} else {
final aggregatedFrameMap =
LinkedHashMap<int, AggregatedNativeFrame>.identity();
final frames = nativeStack.frames;
for (int i = frames.length - 1; i >= 0; --i) {
addOrUpdateAggregatedNativeFrame(
config, timestampRange, aggregatedFrameMap, frames[i]);
config, aggregatedFrameMap, frames[i]);
}
parentFrameMap.putIfAbsent(parentFramePc, () => aggregatedFrameMap);
}
}

final allFrameList = <AggregatedNativeFrame>[];
List<AggregatedNativeFrame> allFrameList = <AggregatedNativeFrame>[];
for (final entry in parentFrameMap.entries) {
final jankFrames =
entry.value.values.where((e) => e.occurTimes > maxOccurTimes);
if (jankFrames.isNotEmpty) {
allFrameList.addAll(jankFrames.toList().reversed);
}
}
for (final jankFrame in entry.value.values.toList().reversed) {
if (jankFrame.occurTimes > maxOccurTimes) {
allFrameList.add(jankFrame);
}

if (allFrameList.length > kMaxStackTraces) {
return allFrameList.sublist(0, kMaxStackTraces);
if (allFrameList.length >= kMaxStackTraces) {
return allFrameList;
}
}
}

return allFrameList;
Expand Down Expand Up @@ -373,13 +358,21 @@ class RingBuffer<T extends Object> {
return value;
}

List<T?> readAll() {
List<T?> result = [];
int current = _head;
List<T> readAllReversed() {
List<T> result = [];
int current = _tail == 0 ? _buffer.length - 1 : _tail - 1;

while (current != _tail || (_isFull && result.length < _buffer.length)) {
result.add(_buffer[current]);
current = (current + 1) % _buffer.length;
final buffer = _buffer[current];
if (buffer != null) {
result.add(buffer);
}
if (current == _head) {
break; // Stop if we've reached the _head
}

current =
(current - 1 + _buffer.length) % _buffer.length; // Move backward
}

return result;
Expand Down
28 changes: 0 additions & 28 deletions test/glance_test.dart

This file was deleted.

Loading

0 comments on commit a474c7d

Please sign in to comment.