v8/test/unittests/profiler/strings-storage-unittest.cc
Andrew Comminos 58b543ab73 [cpu-profiler] Add support for refcounting to StringsStorage
In order to implement CodeEntry deallocation when profiles are stopped,
we need to be able to effectively deallocate strings. Introduce a simple
imperative refcounting API using the existing HashMap slots for
StringsStorage to enable this.

Design doc: https://docs.google.com/document/d/1OTwlBnAMXZEaOICtuz16c01QnkPPdqHBoHpfGwnk5SY/edit

Bug: chromium:956688
Change-Id: Iaa1142925f40aa66c064d011b2a0630de72037fe
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2121575
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Commit-Queue: Andrew Comminos <acomminos@fb.com>
Cr-Commit-Position: refs/heads/master@{#66911}
2020-03-30 19:08:06 +00:00

162 lines
4.9 KiB
C++

// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/profiler/strings-storage.h"
#include <cstdio>
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
using StringsStorageWithIsolate = TestWithIsolate;
bool StringEq(const char* left, const char* right) {
return strcmp(left, right) == 0;
}
TEST_F(StringsStorageWithIsolate, GetNameFromString) {
StringsStorage storage;
// One char strings are canonical on the v8 heap so use a 2 char string here.
Handle<String> str = isolate()->factory()->NewStringFromAsciiChecked("xy");
const char* stored_str = storage.GetName(*str);
CHECK(StringEq("xy", stored_str));
// The storage should de-duplicate the underlying char arrays and return the
// exact same pointer for equivalent input strings.
const char* stored_str_twice = storage.GetName(*str);
CHECK_EQ(stored_str, stored_str_twice);
// Even if the input string was a different one on the v8 heap, if the char
// array is the same, it should be de-duplicated.
Handle<String> str2 = isolate()->factory()->NewStringFromAsciiChecked("xy");
CHECK_NE(*str, *str2);
const char* stored_str_thrice = storage.GetName(*str2);
CHECK_EQ(stored_str_twice, stored_str_thrice);
}
TEST_F(StringsStorageWithIsolate, GetNameFromSymbol) {
StringsStorage storage;
Handle<Symbol> symbol = isolate()->factory()->NewSymbol();
const char* stored_symbol = storage.GetName(*symbol);
CHECK(StringEq("<symbol>", stored_symbol));
Handle<Symbol> symbol2 = isolate()->factory()->NewSymbol();
CHECK_NE(*symbol, *symbol2);
const char* stored_symbol2 = storage.GetName(*symbol2);
CHECK_EQ(stored_symbol, stored_symbol2);
}
TEST_F(StringsStorageWithIsolate, GetConsName) {
StringsStorage storage;
Handle<String> str = isolate()->factory()->NewStringFromAsciiChecked("xy");
const char* empty_prefix_str = storage.GetConsName("", *str);
CHECK(StringEq("xy", empty_prefix_str));
const char* get_str = storage.GetConsName("get ", *str);
CHECK(StringEq("get xy", get_str));
}
TEST_F(StringsStorageWithIsolate, GetNameFromInt) {
StringsStorage storage;
const char* stored_str = storage.GetName(0);
CHECK(StringEq("0", stored_str));
stored_str = storage.GetName(2147483647);
CHECK(StringEq("2147483647", stored_str));
stored_str = storage.GetName(std::numeric_limits<int>::min());
char str_negative_int[12];
snprintf(str_negative_int, sizeof(str_negative_int), "%d",
std::numeric_limits<int>::min());
CHECK(StringEq(str_negative_int, stored_str));
}
TEST_F(StringsStorageWithIsolate, Format) {
StringsStorage storage;
const char* xy = "xy";
const char* stored_str = storage.GetFormatted("%s", xy);
CHECK(StringEq("xy", stored_str));
// Check that the string is copied.
CHECK_NE(xy, stored_str);
const char* formatted_str = storage.GetFormatted("%s / %s", xy, xy);
CHECK(StringEq("xy / xy", formatted_str));
// A different format specifier that results in the same string should share
// the string in storage.
const char* formatted_str2 = storage.GetFormatted("%s", "xy / xy");
CHECK_EQ(formatted_str, formatted_str2);
}
TEST_F(StringsStorageWithIsolate, FormatAndGetShareStorage) {
StringsStorage storage;
Handle<String> str = isolate()->factory()->NewStringFromAsciiChecked("xy");
const char* stored_str = storage.GetName(*str);
const char* formatted_str = storage.GetFormatted("%s", "xy");
CHECK_EQ(stored_str, formatted_str);
}
TEST_F(StringsStorageWithIsolate, Refcounting) {
StringsStorage storage;
const char* a = storage.GetCopy("12");
CHECK_EQ(storage.GetStringCountForTesting(), 1);
const char* b = storage.GetCopy("12");
CHECK_EQ(storage.GetStringCountForTesting(), 1);
// Ensure that we deduplicate the string.
CHECK_EQ(a, b);
CHECK(storage.Release(a));
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK(storage.Release(b));
CHECK_EQ(storage.GetStringCountForTesting(), 0);
#if !DEBUG
CHECK(!storage.Release("12"));
#endif // !DEBUG
// Verify that other constructors refcount as intended.
const char* c = storage.GetFormatted("%d", 12);
CHECK_EQ(storage.GetStringCountForTesting(), 1);
const char* d = storage.GetName(12);
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK_EQ(c, d);
CHECK(storage.Release(c));
CHECK_EQ(storage.GetStringCountForTesting(), 1);
CHECK(storage.Release(d));
CHECK_EQ(storage.GetStringCountForTesting(), 0);
#if !DEBUG
CHECK(!storage.Release("12"));
#endif // !DEBUG
}
TEST_F(StringsStorageWithIsolate, InvalidRelease) {
StringsStorage storage;
// If a refcount becomes invalid, throw in debug builds.
#ifdef DEBUG
ASSERT_DEATH_IF_SUPPORTED(storage.Release("12"), "check failed");
#else
CHECK(!storage.Release("12"));
#endif // DEBUG
}
} // namespace internal
} // namespace v8