Created
January 15, 2014 19:41
-
-
Save aperezdc/8443012 to your computer and use it in GitHub Desktop.
Example on how to measure memory usage using the V8 counters (or any other thing from the counters).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* long1.js | |
* Copyright (C) 2014 Adrian Perez <[email protected]> | |
* | |
* Distributed under terms of the MIT license. | |
*/ | |
function Foo() { | |
} | |
s = "Hello!"; | |
for (var i = 0; i < 1000000000; i++) { | |
var x = new Foo(); | |
if (i % 1000 == 0) { | |
s = s + " " + x; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* long1.js | |
* Copyright (C) 2014 Adrian Perez <[email protected]> | |
* | |
* Distributed under terms of the MIT license. | |
*/ | |
function Foo() { | |
} | |
var s = "Hello!"; | |
var t = ""; | |
for (var i = 0; i < 1000000000; i++) { | |
var x = new Foo(); | |
if (i % 1000 == 0) { | |
s = s + " " + x; | |
} | |
if (i % 10000000 == 0) { | |
t = t + s; | |
} | |
if (i - 5 % 100000 == 0) { | |
t = ""; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CXXFLAGS += -Wall -std=gnu++11 $(OPT_CXXFLAGS) | |
LDFLAGS += -lv8 | |
CC = $(CXX) | |
all: v8-mem-accounting | |
v8-mem-accounting: v8-mem-accounting.o | |
clean: | |
$(RM) v8-mem-accounting v8-mem-accounting.o |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* v8-mem-accounting.cc | |
* Copyright (C) 2014 Adrian Perez <[email protected]> | |
* | |
* Distributed under terms of the MIT license. | |
*/ | |
#include <cstring> | |
#include <iostream> | |
#include <fstream> | |
#include <string> | |
#include <thread> | |
#include <chrono> | |
#include <unordered_map> | |
#include <unordered_set> | |
#include <v8.h> | |
using namespace v8; | |
typedef std::unordered_map<std::string, int> Counters; | |
#ifdef DUMP_COUNTER_NAMES | |
static std::unordered_set<std::string> all_counter_names; | |
#endif // DUMP_COUNTER_NAMES | |
static std::unordered_map<Isolate*, Counters> counters; | |
static bool monitor_running = true; | |
static int* | |
lookup_counter(const char* name) | |
{ | |
#ifdef DUMP_COUNTER_NAMES | |
if (all_counter_names.find(name) == all_counter_names.end()) | |
all_counter_names.emplace(name); | |
#endif // DUMP_COUNTER_NAMES | |
// Only track the counters in this set. | |
static std::unordered_set<std::string> interesting_counters { | |
"c:V8.OsMemoryAllocated", | |
"c:V8.ZoneSegmentBytes", | |
}; | |
if (interesting_counters.find(name) == interesting_counters.end()) | |
return nullptr; | |
auto& counters_for_isolate = counters.find(Isolate::GetCurrent())->second; | |
auto iterator = counters_for_isolate.find(name); | |
if (iterator == counters_for_isolate.end()) | |
return &counters_for_isolate.emplace(name, 0).first->second; | |
else | |
return &iterator->second; | |
} | |
static void | |
setup_counters(Isolate* isolate) | |
{ | |
// Setting a function for looking up counters acts | |
// implicitly on the active Isolate. | |
isolate->Enter(); | |
if (counters.find(isolate) == counters.end()) | |
counters.emplace(isolate, Counters()); | |
V8::SetCounterFunction(lookup_counter); | |
isolate->Exit(); | |
} | |
static void | |
dump_counters(Isolate* isolate) | |
{ | |
auto counters_for_isolate = counters.find(isolate); | |
if (counters_for_isolate == counters.end()) | |
return; | |
for (auto& item : counters_for_isolate->second) | |
std::cout << ", '" << item.first << "': " << item.second; | |
} | |
static void | |
monitor_loop(Isolate* isolate1, Isolate* isolate2) | |
{ | |
HeapStatistics stats1, stats2; | |
while (monitor_running) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(150)); | |
// XXX Theoretically, one needs to lock the Isolate before | |
// calling GetHeapStatistics, but it seems to work just fine | |
// without locking -- no data modifications are done anyway. | |
{ | |
#ifdef LOCKING | |
Locker lock(isolate1); | |
Isolate::Scope scope(isolate1); | |
#endif // LOCKING | |
isolate1->GetHeapStatistics(&stats1); | |
} | |
{ | |
#ifdef LOCKING | |
Locker lock(isolate2); | |
Isolate::Scope scope(isolate2); | |
#endif // LOCKING | |
isolate2->GetHeapStatistics(&stats2); | |
} | |
std::cout << "[{ 'heap': " << stats1.total_heap_size(); | |
dump_counters(isolate1); | |
std::cout << " }, { 'heap': " << stats2.total_heap_size(); | |
dump_counters(isolate2); | |
std::cout << " }],\n"; | |
} | |
} | |
static void | |
run_script(Isolate* isolate, const std::string& script_text, const std::string& source_file) | |
{ | |
Isolate::Scope iscope(isolate); | |
HandleScope handle_scope(isolate); | |
Local<Context> context = Context::New(isolate); | |
Context::Scope context_scope(context); | |
Local<String> script_utf8 = String::NewFromUtf8(isolate, script_text.c_str()); | |
Local<String> source_utf8 = String::NewFromUtf8(isolate, source_file.c_str()); | |
Local<Script> script = Script::Compile(script_utf8, | |
static_cast<Local<Value>>(source_utf8)); | |
script->Run(); | |
} | |
// Reads an entire file into memory | |
static std::string | |
read_file(const std::string& path) | |
{ | |
std::ifstream input(path, std::ifstream::binary); | |
std::string str; | |
if (input) { | |
input.seekg(0, input.end); | |
auto length = input.tellg(); | |
input.seekg(0, input.beg); | |
str.resize(length, ' '); | |
input.read(&*str.begin(), length); | |
input.close(); | |
} | |
return str; | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
if (argc != 3) { | |
std::cerr << "Usage: " << argv[0] << " script1.js script2.js" << std::endl; | |
return EXIT_FAILURE; | |
} | |
std::string script1_text(read_file(argv[1])); | |
if (script1_text.empty()) { | |
std::cerr << "File '" << argv[1] << "' could not be loaded" << std::endl; | |
return EXIT_FAILURE; | |
} | |
std::string script2_text(read_file(argv[2])); | |
if (script2_text.empty()) { | |
std::cerr << "File '" << argv[2] << "' could not be loaded" << std::endl; | |
return EXIT_FAILURE; | |
} | |
V8::Initialize(); | |
V8::InitializeICU(); | |
Isolate* isolate1 = Isolate::New(); | |
setup_counters(isolate1); | |
Isolate* isolate2 = Isolate::New(); | |
setup_counters(isolate2); | |
std::thread monitor(monitor_loop, isolate1, isolate2); | |
std::thread script1(run_script, isolate1, script1_text, argv[1]); | |
std::thread script2(run_script, isolate2, script2_text, argv[2]); | |
script1.join(); | |
script2.join(); | |
monitor_running = false; | |
monitor.join(); | |
#ifdef DUMP_COUNTER_NAMES | |
for (auto& item : all_counter_names) | |
std::cerr << item << std::endl; | |
#endif // DUMP_COUNTER_NAMES | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment