1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/profiler/strings-storage.h"
6
7 #include <memory>
8
9 #include "src/base/strings.h"
10 #include "src/objects/objects-inl.h"
11 #include "src/utils/allocation.h"
12
13 namespace v8 {
14 namespace internal {
15
StringsMatch(void * key1,void * key2)16 bool StringsStorage::StringsMatch(void* key1, void* key2) {
17 return strcmp(reinterpret_cast<char*>(key1), reinterpret_cast<char*>(key2)) ==
18 0;
19 }
20
StringsStorage()21 StringsStorage::StringsStorage() : names_(StringsMatch) {}
22
~StringsStorage()23 StringsStorage::~StringsStorage() {
24 for (base::HashMap::Entry* p = names_.Start(); p != nullptr;
25 p = names_.Next(p)) {
26 DeleteArray(reinterpret_cast<const char*>(p->key));
27 }
28 }
29
GetCopy(const char * src)30 const char* StringsStorage::GetCopy(const char* src) {
31 base::MutexGuard guard(&mutex_);
32 int len = static_cast<int>(strlen(src));
33 base::HashMap::Entry* entry = GetEntry(src, len);
34 if (entry->value == nullptr) {
35 base::Vector<char> dst = base::Vector<char>::New(len + 1);
36 base::StrNCpy(dst, src, len);
37 dst[len] = '\0';
38 entry->key = dst.begin();
39 string_size_ += len;
40 }
41 entry->value =
42 reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) + 1);
43 return reinterpret_cast<const char*>(entry->key);
44 }
45
GetFormatted(const char * format,...)46 const char* StringsStorage::GetFormatted(const char* format, ...) {
47 va_list args;
48 va_start(args, format);
49 const char* result = GetVFormatted(format, args);
50 va_end(args);
51 return result;
52 }
53
AddOrDisposeString(char * str,int len)54 const char* StringsStorage::AddOrDisposeString(char* str, int len) {
55 base::MutexGuard guard(&mutex_);
56 base::HashMap::Entry* entry = GetEntry(str, len);
57 if (entry->value == nullptr) {
58 // New entry added.
59 entry->key = str;
60 string_size_ += len;
61 } else {
62 DeleteArray(str);
63 }
64 entry->value =
65 reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) + 1);
66 return reinterpret_cast<const char*>(entry->key);
67 }
68
GetVFormatted(const char * format,va_list args)69 const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
70 base::Vector<char> str = base::Vector<char>::New(1024);
71 int len = base::VSNPrintF(str, format, args);
72 if (len == -1) {
73 DeleteArray(str.begin());
74 return GetCopy(format);
75 }
76 return AddOrDisposeString(str.begin(), len);
77 }
78
GetSymbol(Symbol sym)79 const char* StringsStorage::GetSymbol(Symbol sym) {
80 if (!sym.description().IsString()) {
81 return "<symbol>";
82 }
83 String description = String::cast(sym.description());
84 int length = std::min(FLAG_heap_snapshot_string_limit, description.length());
85 auto data = description.ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0,
86 length, &length);
87 if (sym.is_private_name()) {
88 return AddOrDisposeString(data.release(), length);
89 }
90 auto str_length = 8 + length + 1 + 1;
91 auto str_result = NewArray<char>(str_length);
92 snprintf(str_result, str_length, "<symbol %s>", data.get());
93 return AddOrDisposeString(str_result, str_length - 1);
94 }
95
GetName(Name name)96 const char* StringsStorage::GetName(Name name) {
97 if (name.IsString()) {
98 String str = String::cast(name);
99 int length = std::min(FLAG_heap_snapshot_string_limit, str.length());
100 int actual_length = 0;
101 std::unique_ptr<char[]> data = str.ToCString(
102 DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length, &actual_length);
103 return AddOrDisposeString(data.release(), actual_length);
104 } else if (name.IsSymbol()) {
105 return GetSymbol(Symbol::cast(name));
106 }
107 return "";
108 }
109
GetName(int index)110 const char* StringsStorage::GetName(int index) {
111 return GetFormatted("%d", index);
112 }
113
GetConsName(const char * prefix,Name name)114 const char* StringsStorage::GetConsName(const char* prefix, Name name) {
115 if (name.IsString()) {
116 String str = String::cast(name);
117 int length = std::min(FLAG_heap_snapshot_string_limit, str.length());
118 int actual_length = 0;
119 std::unique_ptr<char[]> data = str.ToCString(
120 DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length, &actual_length);
121
122 int cons_length = actual_length + static_cast<int>(strlen(prefix)) + 1;
123 char* cons_result = NewArray<char>(cons_length);
124 snprintf(cons_result, cons_length, "%s%s", prefix, data.get());
125
126 return AddOrDisposeString(cons_result, cons_length - 1);
127 } else if (name.IsSymbol()) {
128 return GetSymbol(Symbol::cast(name));
129 }
130 return "";
131 }
132
133 namespace {
134
ComputeStringHash(const char * str,int len)135 inline uint32_t ComputeStringHash(const char* str, int len) {
136 uint32_t raw_hash_field =
137 StringHasher::HashSequentialString(str, len, kZeroHashSeed);
138 return Name::HashBits::decode(raw_hash_field);
139 }
140
141 } // namespace
142
Release(const char * str)143 bool StringsStorage::Release(const char* str) {
144 base::MutexGuard guard(&mutex_);
145 int len = static_cast<int>(strlen(str));
146 uint32_t hash = ComputeStringHash(str, len);
147 base::HashMap::Entry* entry = names_.Lookup(const_cast<char*>(str), hash);
148
149 // If an entry wasn't found or the address of the found entry doesn't match
150 // the one passed in, this string wasn't managed by this StringsStorage
151 // instance (i.e. a constant). Ignore this.
152 if (!entry || entry->key != str) {
153 return false;
154 }
155
156 DCHECK(entry->value);
157 entry->value =
158 reinterpret_cast<void*>(reinterpret_cast<size_t>(entry->value) - 1);
159
160 if (entry->value == 0) {
161 string_size_ -= len;
162 names_.Remove(const_cast<char*>(str), hash);
163 DeleteArray(str);
164 }
165 return true;
166 }
167
GetStringCountForTesting() const168 size_t StringsStorage::GetStringCountForTesting() const {
169 return names_.occupancy();
170 }
171
GetStringSize()172 size_t StringsStorage::GetStringSize() {
173 base::MutexGuard guard(&mutex_);
174 return string_size_;
175 }
176
GetEntry(const char * str,int len)177 base::HashMap::Entry* StringsStorage::GetEntry(const char* str, int len) {
178 uint32_t hash = ComputeStringHash(str, len);
179 return names_.LookupOrInsert(const_cast<char*>(str), hash);
180 }
181
182 } // namespace internal
183 } // namespace v8
184