Commit 19f23ae9 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[heap] Add per-context accounting of external bytes

The existing legacy performance.memory API accounts external string
and array buffer backing store bytes. This CL adds per-context tracking
of external bytes

Bug: chromium:973627
Change-Id: I2b308dc540454e7b0b66406b83a18bf8f8d55d8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2025369Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66018}
parent 410ca4c5
......@@ -449,8 +449,8 @@ void ConcurrentMarking::Run(int task_id, TaskState* task_state) {
}
size_t visited_size = visitor.Visit(map, object);
if (is_per_context_mode) {
native_context_stats.IncrementSize(marking_worklists.Context(),
visited_size);
native_context_stats.IncrementSize(marking_worklists.Context(), map,
object, visited_size);
}
current_marked_bytes += visited_size;
}
......
......@@ -1591,6 +1591,7 @@ void MarkCompactCollector::MarkStringTable(
string_table.IteratePrefix(custom_root_body_visitor);
if (marking_worklists()->IsPerContextMode()) {
native_context_stats_.IncrementSize(MarkingWorklists::kSharedContext,
string_table.map(), string_table,
string_table.Size());
}
}
......@@ -1848,8 +1849,8 @@ size_t MarkCompactCollector::ProcessMarkingWorklist(size_t bytes_to_process) {
}
size_t visited_size = marking_visitor_->Visit(map, object);
if (is_per_context_mode) {
native_context_stats_.IncrementSize(marking_worklists()->Context(),
visited_size);
native_context_stats_.IncrementSize(marking_worklists()->Context(), map,
object, visited_size);
}
bytes_processed += visited_size;
if (bytes_to_process && bytes_processed >= bytes_to_process) {
......
......@@ -8,6 +8,8 @@
#include "src/heap/memory-measurement.h"
#include "src/objects/contexts-inl.h"
#include "src/objects/contexts.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/instance-type.h"
#include "src/objects/map-inl.h"
#include "src/objects/map.h"
......@@ -38,6 +40,21 @@ bool NativeContextInferrer::Infer(Isolate* isolate, Map map, HeapObject object,
}
}
V8_INLINE bool NativeContextStats::HasExternalBytes(Map map) {
InstanceType instance_type = map.instance_type();
return (instance_type == JS_ARRAY_BUFFER_TYPE ||
InstanceTypeChecker::IsExternalString(instance_type));
}
V8_INLINE void NativeContextStats::IncrementSize(Address context, Map map,
HeapObject object,
size_t size) {
size_by_context_[context] += size;
if (HasExternalBytes(map)) {
IncrementExternalSize(context, map, object);
}
}
} // namespace internal
} // namespace v8
......
......@@ -351,5 +351,18 @@ void NativeContextStats::Merge(const NativeContextStats& other) {
}
}
void NativeContextStats::IncrementExternalSize(Address context, Map map,
HeapObject object) {
InstanceType instance_type = map.instance_type();
size_t external_size = 0;
if (instance_type == JS_ARRAY_BUFFER_TYPE) {
external_size = JSArrayBuffer::cast(object).allocation_length();
} else {
DCHECK(InstanceTypeChecker::IsExternalString(instance_type));
external_size = ExternalString::cast(object).ExternalPayloadSize();
}
size_by_context_[context] += external_size;
}
} // namespace internal
} // namespace v8
......@@ -76,9 +76,8 @@ class V8_EXPORT_PRIVATE NativeContextInferrer {
// Maintains mapping from native contexts to their sizes.
class V8_EXPORT_PRIVATE NativeContextStats {
public:
void IncrementSize(Address context, size_t size) {
size_by_context_[context] += size;
}
V8_INLINE void IncrementSize(Address context, Map map, HeapObject object,
size_t size);
size_t Get(Address context) const {
const auto it = size_by_context_.find(context);
......@@ -89,6 +88,8 @@ class V8_EXPORT_PRIVATE NativeContextStats {
void Merge(const NativeContextStats& other);
private:
V8_INLINE bool HasExternalBytes(Map map);
void IncrementExternalSize(Address context, Map map, HeapObject object);
std::unordered_map<Address, size_t> size_by_context_;
};
......
......@@ -64,6 +64,11 @@ V8_INLINE bool IsInternalizedString(InstanceType instance_type) {
(kStringTag | kInternalizedTag);
}
V8_INLINE bool IsExternalString(InstanceType instance_type) {
return (instance_type & (kIsNotStringMask | kStringRepresentationMask)) ==
kExternalStringTag;
}
} // namespace InstanceTypeChecker
// TODO(v8:7786): For instance types that have a single map instance on the
......
......@@ -221,7 +221,9 @@ V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(V) \
TORQUE_INSTANCE_CHECKERS_RANGE_ONLY_DECLARED(V)
#define INSTANCE_TYPE_CHECKERS_CUSTOM(V) V(InternalizedString)
#define INSTANCE_TYPE_CHECKERS_CUSTOM(V) \
V(ExternalString) \
V(InternalizedString)
#define INSTANCE_TYPE_CHECKERS(V) \
INSTANCE_TYPE_CHECKERS_SINGLE(V) \
......
......@@ -211,11 +211,6 @@ DEF_GETTER(HeapObject, IsSeqTwoByteString, bool) {
String::cast(*this).IsTwoByteRepresentation(isolate);
}
DEF_GETTER(HeapObject, IsExternalString, bool) {
if (!IsString(isolate)) return false;
return StringShape(String::cast(*this).map(isolate)).IsExternal();
}
DEF_GETTER(HeapObject, IsExternalOneByteString, bool) {
if (!IsString(isolate)) return false;
return StringShape(String::cast(*this).map(isolate)).IsExternal() &&
......
......@@ -64,12 +64,70 @@ TEST(NativeContextInferrerJSObject) {
}
TEST(NativeContextStatsMerge) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
v8::Local<v8::Value> result = CompileRun("({a : 10})");
Handle<HeapObject> object =
Handle<HeapObject>::cast(Utils::OpenHandle(*result));
NativeContextStats stats1, stats2;
Address object = 0;
stats1.IncrementSize(object, 10);
stats2.IncrementSize(object, 20);
stats1.IncrementSize(native_context->ptr(), object->map(), *object, 10);
stats2.IncrementSize(native_context->ptr(), object->map(), *object, 20);
stats1.Merge(stats2);
CHECK_EQ(30, stats1.Get(object));
CHECK_EQ(30, stats1.Get(native_context->ptr()));
}
TEST(NativeContextStatsArrayBuffers) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
v8::Local<v8::ArrayBuffer> array_buffer =
v8::ArrayBuffer::New(CcTest::isolate(), 1000);
Handle<JSArrayBuffer> i_array_buffer = Utils::OpenHandle(*array_buffer);
NativeContextStats stats;
stats.IncrementSize(native_context->ptr(), i_array_buffer->map(),
*i_array_buffer, 10);
CHECK_EQ(1010, stats.Get(native_context->ptr()));
}
namespace {
class TestResource : public v8::String::ExternalStringResource {
public:
explicit TestResource(uint16_t* data) : data_(data), length_(0) {
while (data[length_]) ++length_;
}
~TestResource() override { i::DeleteArray(data_); }
const uint16_t* data() const override { return data_; }
size_t length() const override { return length_; }
private:
uint16_t* data_;
size_t length_;
};
} // anonymous namespace
TEST(NativeContextStatsExternalString) {
LocalContext env;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<NativeContext> native_context = GetNativeContext(isolate, env.local());
const char* c_source = "0123456789";
uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
TestResource* resource = new TestResource(two_byte_source);
Local<v8::String> string =
v8::String::NewExternalTwoByte(CcTest::isolate(), resource)
.ToLocalChecked();
Handle<String> i_string = Utils::OpenHandle(*string);
NativeContextStats stats;
stats.IncrementSize(native_context->ptr(), i_string->map(), *i_string, 10);
CHECK_EQ(10 + 10 * 2, stats.Get(native_context->ptr()));
}
} // namespace heap
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment