Author: [email protected]
Date: Thu Mar 26 17:24:49 2009
New Revision: 1622
Modified:
branches/bleeding_edge/include/v8.h
branches/bleeding_edge/samples/shell.cc
branches/bleeding_edge/src/api.cc
branches/bleeding_edge/src/debug.cc
branches/bleeding_edge/src/debug.h
branches/bleeding_edge/src/flag-definitions.h
branches/bleeding_edge/src/flags.cc
branches/bleeding_edge/src/log.cc
branches/bleeding_edge/src/log.h
branches/bleeding_edge/src/platform-linux.cc
branches/bleeding_edge/src/v8.cc
branches/bleeding_edge/src/zone-inl.h
branches/bleeding_edge/src/zone.h
branches/bleeding_edge/test/cctest/cctest.cc
branches/bleeding_edge/test/cctest/test-api.cc
branches/bleeding_edge/test/cctest/test-strings.cc
branches/bleeding_edge/test/message/testcfg.py
Log:
Fixed a bunch of memory leaks in tests, including:
- String traversal test data (now in a zone)
- Debug message thread (now joined on exit)
- Threading test threads (now joined on exit)
- Changed message tests framework to cope with valgrind
Also, fixed a bug where we'd try to delete stack-allocated objects
when tearing down v8. Good times.
Modified: branches/bleeding_edge/include/v8.h
==============================================================================
--- branches/bleeding_edge/include/v8.h (original)
+++ branches/bleeding_edge/include/v8.h Thu Mar 26 17:24:49 2009
@@ -1080,13 +1080,13 @@
* access check info, the object cannot be accessed by anyone.
*/
void TurnOnAccessCheck();
-
+
/**
* Returns the identity hash for this object. The current implemenation
uses
* a hidden property on the object to store the identity hash.
*/
int GetIdentityHash();
-
+
/**
* Access hidden properties on JavaScript objects. These properties are
* hidden from the executing JavaScript and only accessible through the
V8
@@ -2032,6 +2032,17 @@
* See also PauseProfiler().
*/
static void ResumeProfiler();
+
+ /**
+ * Releases any resources used by v8 and stops any utility threads
+ * that may be running. Note that disposing v8 is permanent, it
+ * cannot be reinitialized.
+ *
+ * It should generally not be necessary to dispose v8 before exiting
+ * a process, this should happen automatically. It is only necessary
+ * to use if the process needs the resources taken up by v8.
+ */
+ static bool Dispose();
private:
V8();
Modified: branches/bleeding_edge/samples/shell.cc
==============================================================================
--- branches/bleeding_edge/samples/shell.cc (original)
+++ branches/bleeding_edge/samples/shell.cc Thu Mar 26 17:24:49 2009
@@ -45,7 +45,7 @@
void ReportException(v8::TryCatch* handler);
-int main(int argc, char* argv[]) {
+int RunMain(int argc, char* argv[]) {
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
v8::HandleScope handle_scope;
// Create a template for the global object.
@@ -95,8 +95,14 @@
return 1;
}
}
- if (run_shell) RunShell(context);
return 0;
+}
+
+
+int main(int argc, char* argv[]) {
+ int result = RunMain(argc, argv);
+ v8::V8::Dispose();
+ return result;
}
Modified: branches/bleeding_edge/src/api.cc
==============================================================================
--- branches/bleeding_edge/src/api.cc (original)
+++ branches/bleeding_edge/src/api.cc Thu Mar 26 17:24:49 2009
@@ -2272,6 +2272,12 @@
}
+bool v8::V8::Dispose() {
+ i::V8::TearDown();
+ return true;
+}
+
+
const char* v8::V8::GetVersion() {
return "1.1.4 (candidate)";
}
Modified: branches/bleeding_edge/src/debug.cc
==============================================================================
--- branches/bleeding_edge/src/debug.cc (original)
+++ branches/bleeding_edge/src/debug.cc Thu Mar 26 17:24:49 2009
@@ -1737,6 +1737,15 @@
}
+void Debugger::TearDown() {
+ if (message_thread_ != NULL) {
+ message_thread_->Stop();
+ delete message_thread_;
+ message_thread_ = NULL;
+ }
+}
+
+
void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void*
data) {
message_handler_ = handler;
message_handler_data_ = data;
@@ -1852,16 +1861,23 @@
DebugMessageThread::DebugMessageThread()
: host_running_(true),
command_queue_(kQueueInitialSize),
- message_queue_(kQueueInitialSize) {
+ message_queue_(kQueueInitialSize),
+ keep_running_(true) {
command_received_ = OS::CreateSemaphore(0);
message_received_ = OS::CreateSemaphore(0);
}
-// Does not free resources held by DebugMessageThread
-// because this cannot be done thread-safely.
+// Should only be done after the thread is done running.
DebugMessageThread::~DebugMessageThread() {
+ delete command_received_;
+ delete message_received_;
}
+void DebugMessageThread::Stop() {
+ keep_running_ = false;
+ SendMessage(Vector<uint16_t>(NULL, 0));
+ Join();
+}
// Puts an event coming from V8 on the queue. Creates
// a copy of the JSON formatted event string managed by the V8.
@@ -1909,7 +1925,7 @@
void DebugMessageThread::Run() {
// Sends debug events to an installed debugger message callback.
- while (true) {
+ while (keep_running_) {
// Wait and Get are paired so that semaphore count equals queue length.
message_received_->Wait();
Logger::DebugTag("Get message from event message_queue.");
Modified: branches/bleeding_edge/src/debug.h
==============================================================================
--- branches/bleeding_edge/src/debug.h (original)
+++ branches/bleeding_edge/src/debug.h Thu Mar 26 17:24:49 2009
@@ -429,6 +429,7 @@
bool auto_continue);
static void SetEventListener(Handle<Object> callback, Handle<Object>
data);
static void SetMessageHandler(v8::DebugMessageHandler handler, void*
data);
+ static void TearDown();
static void SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data);
static void SendMessage(Vector<uint16_t> message);
@@ -527,7 +528,7 @@
class DebugMessageThread: public Thread {
public:
DebugMessageThread(); // Called from API thread.
- virtual ~DebugMessageThread(); // Never called.
+ virtual ~DebugMessageThread();
// Called by V8 thread. Reports events from V8 VM.
// Also handles command processing in stopped state of V8,
// when host_running_ is false.
@@ -554,6 +555,7 @@
// Check whether there are commands in the queue.
bool HasCommands() { return !command_queue_.IsEmpty(); }
+ void Stop();
bool host_running_; // Is the debugging host running or stopped?
Semaphore* command_received_; // Non-zero when command queue is
non-empty.
@@ -564,6 +566,7 @@
static const int kQueueInitialSize = 4;
LockingMessageQueue command_queue_;
LockingMessageQueue message_queue_;
+ bool keep_running_;
DISALLOW_COPY_AND_ASSIGN(DebugMessageThread);
};
Modified: branches/bleeding_edge/src/flag-definitions.h
==============================================================================
--- branches/bleeding_edge/src/flag-definitions.h (original)
+++ branches/bleeding_edge/src/flag-definitions.h Thu Mar 26 17:24:49 2009
@@ -62,7 +62,7 @@
// printing / etc in the flag parser code. We only do this for writable
flags.
#elif defined(FLAG_MODE_META)
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
- { Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt },
+ { Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt, false
},
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
#else
Modified: branches/bleeding_edge/src/flags.cc
==============================================================================
--- branches/bleeding_edge/src/flags.cc (original)
+++ branches/bleeding_edge/src/flags.cc Thu Mar 26 17:24:49 2009
@@ -58,6 +58,7 @@
void* valptr_; // Pointer to the global flag variable.
const void* defptr_; // Pointer to the default value.
const char* cmt_; // A comment about the flags purpose.
+ bool owns_ptr_; // Does the flag own its string value?
FlagType type() const { return type_; }
@@ -80,9 +81,17 @@
return reinterpret_cast<double*>(valptr_);
}
- const char** string_variable() const {
+ const char* string_value() const {
ASSERT(type_ == TYPE_STRING);
- return reinterpret_cast<const char**>(valptr_);
+ return *reinterpret_cast<const char**>(valptr_);
+ }
+
+ void set_string_value(const char *value, bool owns_ptr) {
+ ASSERT(type_ == TYPE_STRING);
+ const char **ptr = reinterpret_cast<const char **>(valptr_);
+ if (owns_ptr_ && *ptr != NULL) DeleteArray(*ptr);
+ *ptr = value;
+ owns_ptr_ = owns_ptr;
}
JSArguments* args_variable() const {
@@ -125,7 +134,7 @@
case TYPE_FLOAT:
return *float_variable() == float_default();
case TYPE_STRING: {
- const char* str1 = *string_variable();
+ const char* str1 = string_value();
const char* str2 = string_default();
if (str2 == NULL) return str1 == NULL;
if (str1 == NULL) return str2 == NULL;
@@ -151,7 +160,7 @@
*float_variable() = float_default();
break;
case TYPE_STRING:
- *string_variable() = string_default();
+ set_string_value(string_default(), false);
break;
case TYPE_ARGS:
*args_variable() = args_default();
@@ -197,7 +206,7 @@
buffer.Add("%f", FmtElm(*flag->float_variable()));
break;
case Flag::TYPE_STRING: {
- const char* str = *flag->string_variable();
+ const char* str = flag->string_value();
buffer.Add("%s", str ? str : "NULL");
break;
}
@@ -389,17 +398,17 @@
*flag->float_variable() = strtod(value, &endp);
break;
case Flag::TYPE_STRING:
- *flag->string_variable() = value;
+ flag->set_string_value(value ? StrDup(value) : NULL, true);
break;
case Flag::TYPE_ARGS: {
int start_pos = (value == NULL) ? i : i - 1;
int js_argc = *argc - start_pos;
const char** js_argv = NewArray<const char*>(js_argc);
if (value != NULL) {
- js_argv[0] = value;
+ js_argv[0] = StrDup(value);
}
for (int k = i; k < *argc; k++) {
- js_argv[k - start_pos] = argv[k];
+ js_argv[k - start_pos] = StrDup(argv[k]);
}
*flag->args_variable() = JSArguments(js_argc, js_argv);
i = *argc; // Consume all arguments
@@ -491,11 +500,7 @@
// cleanup
DeleteArray(argv);
- // don't delete copy0 since the substrings
- // may be pointed to by FLAG variables!
- // (this is a memory leak, but it's minor since this
- // code is only used for debugging, or perhaps once
- // during initialization).
+ DeleteArray(copy0);
return result;
}
Modified: branches/bleeding_edge/src/log.cc
==============================================================================
--- branches/bleeding_edge/src/log.cc (original)
+++ branches/bleeding_edge/src/log.cc Thu Mar 26 17:24:49 2009
@@ -407,6 +407,7 @@
Profiler* Logger::profiler_ = NULL;
Mutex* Logger::mutex_ = NULL;
VMState* Logger::current_state_ = NULL;
+VMState Logger::bottom_state_(OTHER);
SlidingStateWindow* Logger::sliding_state_window_ = NULL;
#endif // ENABLE_LOGGING_AND_PROFILING
@@ -1017,7 +1018,7 @@
mutex_ = OS::CreateMutex();
}
- current_state_ = new VMState(OTHER);
+ current_state_ = &bottom_state_;
// as log is initialized early with V8, we can assume that JS execution
// frames can never reach this point on stack
@@ -1052,8 +1053,6 @@
profiler_ = NULL;
}
- // Deleting the current_state_ has the side effect of assigning to it(!).
- while (current_state_) delete current_state_;
delete sliding_state_window_;
delete ticker_;
Modified: branches/bleeding_edge/src/log.h
==============================================================================
--- branches/bleeding_edge/src/log.h (original)
+++ branches/bleeding_edge/src/log.h Thu Mar 26 17:24:49 2009
@@ -83,7 +83,7 @@
#endif
-class VMState {
+class VMState BASE_EMBEDDED {
#ifdef ENABLE_LOGGING_AND_PROFILING
public:
explicit VMState(StateTag state);
@@ -251,6 +251,9 @@
// A stack of VM states.
static VMState* current_state_;
+
+ // Singleton bottom or default vm state.
+ static VMState bottom_state_;
// SlidingStateWindow instance keeping a sliding window of the most
// recent VM states.
Modified: branches/bleeding_edge/src/platform-linux.cc
==============================================================================
--- branches/bleeding_edge/src/platform-linux.cc (original)
+++ branches/bleeding_edge/src/platform-linux.cc Thu Mar 26 17:24:49 2009
@@ -417,6 +417,7 @@
case ThreadHandle::INVALID: thread_ = kNoThread; break;
}
}
+
pthread_t thread_; // Thread handle for pthread.
};
Modified: branches/bleeding_edge/src/v8.cc
==============================================================================
--- branches/bleeding_edge/src/v8.cc (original)
+++ branches/bleeding_edge/src/v8.cc Thu Mar 26 17:24:49 2009
@@ -111,6 +111,8 @@
Heap::TearDown();
Logger::TearDown();
+ Debugger::TearDown();
+
has_been_setup_ = false;
has_been_disposed_ = true;
}
Modified: branches/bleeding_edge/src/zone-inl.h
==============================================================================
--- branches/bleeding_edge/src/zone-inl.h (original)
+++ branches/bleeding_edge/src/zone-inl.h Thu Mar 26 17:24:49 2009
@@ -50,6 +50,12 @@
}
+template <typename T>
+T* Zone::NewArray(int length) {
+ return static_cast<T*>(Zone::New(length * sizeof(T)));
+}
+
+
bool Zone::excess_allocation() {
return segment_bytes_allocated_ > zone_excess_limit_;
}
Modified: branches/bleeding_edge/src/zone.h
==============================================================================
--- branches/bleeding_edge/src/zone.h (original)
+++ branches/bleeding_edge/src/zone.h Thu Mar 26 17:24:49 2009
@@ -58,6 +58,9 @@
// allocating new segments of memory on demand using malloc().
static inline void* New(int size);
+ template <typename T>
+ static inline T* NewArray(int length);
+
// Delete all objects and free all memory allocated in the Zone.
static void DeleteAll();
Modified: branches/bleeding_edge/test/cctest/cctest.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/cctest.cc (original)
+++ branches/bleeding_edge/test/cctest/cctest.cc Thu Mar 26 17:24:49 2009
@@ -30,6 +30,7 @@
#include <cstring>
#include <cstdio>
#include "cctest.h"
+#include "debug.h"
CcTest* CcTest::last_ = NULL;
@@ -120,5 +121,6 @@
}
if (print_run_count && tests_run != 1)
printf("Ran %i tests.\n", tests_run);
+ v8::V8::Dispose();
return 0;
}
Modified: branches/bleeding_edge/test/cctest/test-api.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-api.cc (original)
+++ branches/bleeding_edge/test/cctest/test-api.cc Thu Mar 26 17:24:49 2009
@@ -154,7 +154,7 @@
class RegisterThreadedTest {
public:
explicit RegisterThreadedTest(CcTest::TestFunction* callback)
- : callback_(callback) {
+ : fuzzer_(NULL), callback_(callback) {
prev_ = first_;
first_ = this;
count_++;
@@ -5098,6 +5098,10 @@
void ApiTestFuzzer::TearDown() {
fuzzing_ = false;
+ for (int i = 0; i < RegisterThreadedTest::count(); i++) {
+ ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
+ if (fuzzer != NULL) fuzzer->Join();
+ }
}
Modified: branches/bleeding_edge/test/cctest/test-strings.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-strings.cc (original)
+++ branches/bleeding_edge/test/cctest/test-strings.cc Thu Mar 26 17:24:49
2009
@@ -11,6 +11,7 @@
#include "factory.h"
#include "cctest.h"
+#include "zone-inl.h"
unsigned int seed = 123;
@@ -48,6 +49,8 @@
static void InitializeBuildingBlocks(
Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS]) {
+ // A list of pointers that we don't have any interest in cleaning up.
+ // If they are reachable from a root then leak detection won't complain.
for (int i = 0; i < NUMBER_OF_BUILDING_BLOCKS; i++) {
int len = gen() % 16;
if (len > 14) {
@@ -80,7 +83,7 @@
}
case 2: {
class Resource: public v8::String::ExternalStringResource,
- public Malloced {
+ public ZoneObject {
public:
explicit Resource(Vector<const uc16> string):
data_(string.start()) {
length_ = string.length();
@@ -92,7 +95,7 @@
const uc16* data_;
size_t length_;
};
- uc16* buf = NewArray<uc16>(len);
+ uc16* buf = Zone::NewArray<uc16>(len);
for (int j = 0; j < len; j++) {
buf[j] = gen() % 65536;
}
@@ -212,6 +215,7 @@
InitializeVM();
v8::HandleScope scope;
Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS];
+ ZoneScope zone(DELETE_ON_EXIT);
InitializeBuildingBlocks(building_blocks);
Handle<String> flat = ConstructBalanced(building_blocks);
FlattenString(flat);
@@ -303,6 +307,7 @@
InitializeVM();
v8::HandleScope scope;
Handle<String> building_blocks[NUMBER_OF_BUILDING_BLOCKS];
+ ZoneScope zone(DELETE_ON_EXIT);
InitializeBuildingBlocks(building_blocks);
seed = 42;
Modified: branches/bleeding_edge/test/message/testcfg.py
==============================================================================
--- branches/bleeding_edge/test/message/testcfg.py (original)
+++ branches/bleeding_edge/test/message/testcfg.py Thu Mar 26 17:24:49 2009
@@ -41,6 +41,11 @@
self.config = config
self.mode = mode
+ def IgnoreLine(self, str):
+ """Ignore empty lines and valgrind output."""
+ if not str: return True
+ else: return str.startswith('==') or str.startswith('**')
+
def IsFailureOutput(self, output):
f = file(self.expected)
# Skip initial '#' comment and spaces
@@ -58,7 +63,8 @@
pattern = '^%s$' % pattern
patterns.append(pattern)
# Compare actual output with the expected
- outlines = [ s for s in output.stdout.split('\n') if s ]
+ raw_lines = output.stdout.split('\n')
+ outlines = [ s for s in raw_lines if not self.IgnoreLine(s) ]
if len(outlines) != len(patterns):
return True
for i in xrange(len(patterns)):
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---