[VM/Service] Add private _invalidateIdZone RPC

TEST=pkg/vm_service/test/id_zones_test.dart, CI

Issue: https://github.com/dart-lang/sdk/issues/55869
Change-Id: I02fcb2502b698066885b3f090435e43a34ed6fcd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/379820
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Derek Xu
2024-08-28 16:41:50 +00:00
committed by Commit Queue
parent 6b978a8339
commit daa8cbb29e
5 changed files with 102 additions and 6 deletions
+60
View File
@@ -85,6 +85,36 @@ final tests = <IsolateTest>[
final cInstance2 = await service.getObject(isolateId, cObjectId2);
expect(cInstance2.type, 'Instance');
await service.callMethod(
'_invalidateIdZone',
isolateId: isolateId,
args: {
'_idZoneId': idZone1['id'],
},
);
try {
await service.getObject(isolateId, cObjectId1);
fail('successfully retrieved object using expired ID');
} on SentinelException catch (e) {
expect(e.sentinel.kind, startsWith('Expired'));
expect(e.sentinel.valueAsString, equals('<expired>'));
}
// Ensure that the zone can be reused after it was invalidated.
final cInstanceRef3 = (await service.callMethod(
'evaluateInFrame',
isolateId: isolateId,
args: {
'frameIndex': 0,
'expression': 'c',
'_idZoneId': idZone1['id'],
},
))
.json!;
expect(cInstanceRef3['type'], '@Instance');
expect(cInstanceRef3['id'], 'objects/0/1');
},
// Test the behaviour of an ID Zone with a `backingBufferKind` of `Ring`, an
@@ -202,6 +232,36 @@ final tests = <IsolateTest>[
final cInstance2 = await service.getObject(isolateId, cObjectId2);
expect(cInstance2.type, 'Instance');
await service.callMethod(
'_invalidateIdZone',
isolateId: isolateId,
args: {
'_idZoneId': idZone3['id'],
},
);
try {
await service.getObject(isolateId, cObjectId1);
fail('successfully retrieved object using expired ID');
} on SentinelException catch (e) {
expect(e.sentinel.kind, startsWith('Expired'));
expect(e.sentinel.valueAsString, equals('<expired>'));
}
// Ensure that the zone can be reused after it was invalidated.
final cInstanceRef3 = (await service.callMethod(
'evaluateInFrame',
isolateId: isolateId,
args: {
'frameIndex': 0,
'expression': 'c',
'_idZoneId': idZone3['id'],
},
))
.json!;
expect(cInstanceRef3['type'], '@Instance');
expect(cInstanceRef3['id'], 'objects/0/3');
},
resumeIsolate,
];
+13 -6
View File
@@ -18,9 +18,17 @@ ObjectIdRing::~ObjectIdRing() {
table_ = nullptr;
}
void ObjectIdRing::Invalidate() {
serial_num_ = 0;
wrapped_ = false;
for (int32_t i = 0; i < capacity_; i++) {
table_[i] = Object::null();
}
}
int32_t ObjectIdRing::GetIdForObject(ObjectPtr object, IdPolicy policy) {
// We do not allow inserting null because null is how we detect as entry was
// reclaimed by the GC.
// We do not allow inserting |Object::null()| because we use it as a
// placeholder for unpopulated slots in |table_|.
ASSERT(object != Object::null());
if (policy == kAllocateId) {
return AllocateNewId(object);
@@ -51,10 +59,9 @@ ObjectPtr ObjectIdRing::GetObjectForId(int32_t id, LookupResult* kind) {
}
ASSERT(index >= 0);
ASSERT(index < capacity_);
if (table_[index] == Object::null()) {
*kind = kCollected;
return Object::null();
}
// The `index == kInvalidId` check above should make it impossible for
// `table_[index]` to be |Object::null()|.
ASSERT(table_[index] != Object::null());
*kind = kValid;
ASSERT(IdOfIndex(index) == id);
return table_[index];
+3
View File
@@ -44,6 +44,9 @@ class ObjectIdRing {
explicit ObjectIdRing(int32_t capacity);
~ObjectIdRing();
// Invalidate all the Service IDs currently living in this ring.
void Invalidate();
// Adds the argument to the ring and returns its id. Note we do not allow
// adding Object::null().
int32_t GetIdForObject(ObjectPtr raw_obj, IdPolicy policy = kAllocateId);
+23
View File
@@ -395,6 +395,10 @@ char* RingServiceIdZone::GetServiceId(const Object& obj) {
id());
}
void RingServiceIdZone::Invalidate() {
ring_.Invalidate();
}
void RingServiceIdZone::VisitPointers(ObjectPointerVisitor& visitor) const {
ring_.VisitPointers(&visitor);
}
@@ -2682,6 +2686,23 @@ static void GetReachableSize(Thread* thread, JSONStream* js) {
result.PrintJSON(js, true);
}
static const MethodParameter* const invalidate_id_zone_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
nullptr,
};
static void InvalidateIdZone(Thread* thread, JSONStream* js) {
ASSERT(thread != nullptr);
ASSERT(js != nullptr);
Isolate* isolate = thread->isolate();
ASSERT(isolate != nullptr);
js->id_zone().Invalidate();
PrintSuccess(js);
}
static const MethodParameter* const invoke_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
nullptr,
@@ -6087,6 +6108,8 @@ static const ServiceMethodDescriptor service_methods_[] = {
get_vm_timeline_flags_params },
{ "getVMTimelineMicros", GetVMTimelineMicros,
get_vm_timeline_micros_params },
{ "_invalidateIdZone", InvalidateIdZone,
invalidate_id_zone_params },
{ "invoke", Invoke, invoke_params },
{ "kill", Kill, kill_params },
{ "pause", Pause,
+3
View File
@@ -48,6 +48,8 @@ class ServiceIdZone {
ObjectIdRing::LookupResult* kind) = 0;
// Returned string will be zone allocated.
virtual char* GetServiceId(const Object& obj) = 0;
// Invalidate all the Service IDs currently living in this zone.
virtual void Invalidate() = 0;
virtual void VisitPointers(ObjectPointerVisitor& visitor) const = 0;
virtual void PrintJSON(JSONStream& js) const = 0;
@@ -77,6 +79,7 @@ class RingServiceIdZone final : public ServiceIdZone {
ObjectPtr GetObjectForId(int32_t id, ObjectIdRing::LookupResult* kind) final;
// Returned string will be zone allocated.
char* GetServiceId(const Object& obj) final;
void Invalidate() final;
void VisitPointers(ObjectPointerVisitor& visitor) const final;
void PrintJSON(JSONStream& js) const final;