sdk
sdk copied to clipboard
[DAP] "Sentinel kind: Expired" when looking at large maps
When running Flutter I came across this error, while looking at a data structure that was large. This happened while I was debugging and had put a breakpoint on as was wanting to investigate a large variable and its map values.
Steps to reproduce(in flutter, if you need I can provide the code here)
- I have recreated the problem. you can find the code here. https://github.com/QCIPaulCardno/SentinelKindExpiredIssue2024.git. just the main.dart is all you need.
- To reproduce. put a breakpoint on line 99. Run the app. Press the plus button When it stops. Goto the variables (run and debug). Expand out globals. expand out complexObject. Expand out dicByDayOfFloorIDs, expand "0", expand values. and then you will see the message "[Sentinel kind: Expired, valueAsString: ] from invoke()"
- If you reduce the size of this class by changing count from 10000 to 1000 line 113 in the for loop. and then run again it all works as expected.
Running on a mac Apple M1 Max 64 GB Macintosh HD 14.2.1 (23C71) macOS (desktop) • macos • darwin-arm64 • macOS 14.2.1 23C71 darwin-arm64
building for MacOS
here is flutter doctor -v [✓] Flutter (Channel beta, 3.19.0-0.4.pre, on macOS 14.2.1 23C71 darwin-arm64, locale en-NZ) • Flutter version 3.19.0-0.4.pre on channel beta at /Users/xxx • Upstream repository https://github.com/xxx • Framework revision https://github.com/flutter/flutter/commit/b7e7d46a046ba8a22897a514bf2311a0f81ab198 (10 days ago), 2024-02-02 08:21:06 -0600 • Engine revision 98820f0a77 • Dart version 3.3.0 (build 3.3.0-279.3.beta) • DevTools version 2.31.0
Let me know if you need any more information?
/cc @bkonyi
Unfortunately, this is probably working as intended. The VM service uses a ring buffer to allocate IDs to objects it sends over the service protocol but the ring buffer currently only has the capacity for 8192 objects. If the ring buffer wraps around, older object IDs are overwritten and will give an Expired sentinel response when accessed.
We can work around this by requesting subsets of large collections to avoid causing the object ID buffer to immediately wrap around. This is something we do in the DevTools debugger, but I'm not sure we do in the Dart-Code VSCode plugin (cc @DanTup to confirm).
We may end up increasing the size of the object ID ring buffer at some point. However, that would have implications on memory usage and possibly GC performance, and still wouldn't completely solve the problem for very large collections.
I think there are a few issues here.
- It looks like our paging of variables only works for Lists and not Maps. Lists look like this:
- By default we call
toString()on items for the debugger view, so if there are 10000 items in the map, we're going to generate 10000 strings. - An error invoking toString() for one item in the map should not be causing the entire set to error - I would expect the successful requests to still show their results (in fact, if we're lucky they might all show it, because the strings being evicted we already have from the initial response).
I'll look at 1+3. 2 can be disabled with the following VS Code setting:
"dart.evaluateToStringInDebugViews": false
This results in less-useful labels, but prevents the error:
Smaller repro for the issue without Flutter:
import 'dart:developer';
void main() {
final c = <String, A>{
for (int i = 0; i < 10000; i++) '$i': A(i),
};
debugger();
print(c);
}
class A {
final int i;
A(this.i);
}
I have a change at https://dart-review.googlesource.com/c/sdk/+/364860 that improves this slightly, by not failing the entire request just because we couldn't fetch the strings:
However, we should also support paging here and limiting the calls we make so that we're not blowing the buffer that results in sentinels like this.