Skip to content

Instantly share code, notes, and snippets.

@aperezdc
Created January 15, 2014 19:41
Show Gist options
  • Save aperezdc/8443012 to your computer and use it in GitHub Desktop.
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).
/*
* 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;
}
}
/*
* 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 = "";
}
}
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
/*
* 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