Remove support for logging into a memory buffer.

The only usage of it was in logging tests, I've switched them for
using a file.

I've left out support for "--logfile=*" for now, as Chromium uses it.
Will be removed after the next V8 roll.

R=sgjesse@chromium.org
BUG=859
TEST=mjsunit/log-*

Review URL: http://codereview.chromium.org/7310025

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8629 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent b2e8d72b
......@@ -2983,31 +2983,6 @@ class V8EXPORT V8 {
*/
static bool IsProfilerPaused();
/**
* If logging is performed into a memory buffer (via --logfile=*), allows to
* retrieve previously written messages. This can be used for retrieving
* profiler log data in the application. This function is thread-safe.
*
* Caller provides a destination buffer that must exist during GetLogLines
* call. Only whole log lines are copied into the buffer.
*
* \param from_pos specified a point in a buffer to read from, 0 is the
* beginning of a buffer. It is assumed that caller updates its current
* position using returned size value from the previous call.
* \param dest_buf destination buffer for log data.
* \param max_size size of the destination buffer.
* \returns actual size of log data copied into buffer.
*/
static int GetLogLines(int from_pos, char* dest_buf, int max_size);
/**
* The minimum allowed size for a log lines buffer. If the size of
* the buffer given will not be enough to hold a line of the maximum
* length, an attempt to find a log line end in GetLogLines will
* fail, and an empty result will be returned.
*/
static const int kMinimumSizeForLogLinesBuffer = 2048;
/**
* Retrieve the V8 thread id of the calling thread.
*
......
......@@ -4842,12 +4842,6 @@ bool V8::IsProfilerPaused() {
}
int V8::GetLogLines(int from_pos, char* dest_buf, int max_size) {
ASSERT(max_size >= kMinimumSizeForLogLinesBuffer);
return LOGGER->GetLogLines(from_pos, dest_buf, max_size);
}
int V8::GetCurrentThreadId() {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "V8::GetCurrentThreadId()");
......
......@@ -33,99 +33,14 @@
namespace v8 {
namespace internal {
LogDynamicBuffer::LogDynamicBuffer(
int block_size, int max_size, const char* seal, int seal_size)
: block_size_(block_size),
max_size_(max_size - (max_size % block_size_)),
seal_(seal),
seal_size_(seal_size),
blocks_(max_size_ / block_size_ + 1),
write_pos_(0), block_index_(0), block_write_pos_(0), is_sealed_(false) {
ASSERT(BlocksCount() > 0);
AllocateBlock(0);
for (int i = 1; i < BlocksCount(); ++i) {
blocks_[i] = NULL;
}
}
LogDynamicBuffer::~LogDynamicBuffer() {
for (int i = 0; i < BlocksCount(); ++i) {
DeleteArray(blocks_[i]);
}
}
int LogDynamicBuffer::Read(int from_pos, char* dest_buf, int buf_size) {
if (buf_size == 0) return 0;
int read_pos = from_pos;
int block_read_index = BlockIndex(from_pos);
int block_read_pos = PosInBlock(from_pos);
int dest_buf_pos = 0;
// Read until dest_buf is filled, or write_pos_ encountered.
while (read_pos < write_pos_ && dest_buf_pos < buf_size) {
const int read_size = Min(write_pos_ - read_pos,
Min(buf_size - dest_buf_pos, block_size_ - block_read_pos));
memcpy(dest_buf + dest_buf_pos,
blocks_[block_read_index] + block_read_pos, read_size);
block_read_pos += read_size;
dest_buf_pos += read_size;
read_pos += read_size;
if (block_read_pos == block_size_) {
block_read_pos = 0;
++block_read_index;
}
}
return dest_buf_pos;
}
int LogDynamicBuffer::Seal() {
WriteInternal(seal_, seal_size_);
is_sealed_ = true;
return 0;
}
int LogDynamicBuffer::Write(const char* data, int data_size) {
if (is_sealed_) {
return 0;
}
if ((write_pos_ + data_size) <= (max_size_ - seal_size_)) {
return WriteInternal(data, data_size);
} else {
return Seal();
}
}
const char* Log::kLogToTemporaryFile = "&";
int LogDynamicBuffer::WriteInternal(const char* data, int data_size) {
int data_pos = 0;
while (data_pos < data_size) {
const int write_size =
Min(data_size - data_pos, block_size_ - block_write_pos_);
memcpy(blocks_[block_index_] + block_write_pos_, data + data_pos,
write_size);
block_write_pos_ += write_size;
data_pos += write_size;
if (block_write_pos_ == block_size_) {
block_write_pos_ = 0;
AllocateBlock(++block_index_);
}
}
write_pos_ += data_size;
return data_size;
}
// Must be the same message as in Logger::PauseProfiler.
const char* const Log::kDynamicBufferSeal = "profiler,\"pause\"\n";
Log::Log(Logger* logger)
: write_to_file_(false),
is_stopped_(false),
: is_stopped_(false),
output_handle_(NULL),
ll_output_handle_(NULL),
output_buffer_(NULL),
mutex_(NULL),
message_buffer_(NULL),
logger_(logger) {
......@@ -163,19 +78,19 @@ void Log::Initialize() {
FLAG_prof_auto = false;
}
bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
bool open_log_file = FLAG_log || FLAG_log_runtime || FLAG_log_api
|| FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
|| FLAG_log_regexp || FLAG_log_state_changes || FLAG_ll_prof;
bool open_log_file = start_logging || FLAG_prof_lazy;
// If we're logging anything, we need to open the log file.
if (open_log_file) {
if (strcmp(FLAG_logfile, "-") == 0) {
OpenStdout();
} else if (strcmp(FLAG_logfile, "*") == 0) {
OpenMemoryBuffer();
} else {
// Does nothing for now. Will be removed.
} else if (strcmp(FLAG_logfile, kLogToTemporaryFile) == 0) {
OpenTemporaryFile();
} else {
if (strchr(FLAG_logfile, '%') != NULL ||
!Isolate::Current()->IsDefaultIsolate()) {
// If there's a '%' in the log file name we have to expand
......@@ -225,7 +140,12 @@ void Log::Initialize() {
void Log::OpenStdout() {
ASSERT(!IsEnabled());
output_handle_ = stdout;
write_to_file_ = true;
}
void Log::OpenTemporaryFile() {
ASSERT(!IsEnabled());
output_handle_ = i::OS::OpenTemporaryFile();
}
......@@ -240,7 +160,6 @@ static const int kLowLevelLogBufferSize = 2 * MB;
void Log::OpenFile(const char* name) {
ASSERT(!IsEnabled());
output_handle_ = OS::FOpen(name, OS::LogFileOpenMode);
write_to_file_ = true;
if (FLAG_ll_prof) {
// Open the low-level log file.
size_t len = strlen(name);
......@@ -253,25 +172,18 @@ void Log::OpenFile(const char* name) {
}
void Log::OpenMemoryBuffer() {
ASSERT(!IsEnabled());
output_buffer_ = new LogDynamicBuffer(
kDynamicBufferBlockSize, kMaxDynamicBufferSize,
kDynamicBufferSeal, StrLength(kDynamicBufferSeal));
write_to_file_ = false;
}
void Log::Close() {
if (write_to_file_) {
if (output_handle_ != NULL) fclose(output_handle_);
output_handle_ = NULL;
if (ll_output_handle_ != NULL) fclose(ll_output_handle_);
ll_output_handle_ = NULL;
} else {
delete output_buffer_;
output_buffer_ = NULL;
FILE* Log::Close() {
FILE* result = NULL;
if (output_handle_ != NULL) {
if (strcmp(FLAG_logfile, kLogToTemporaryFile) != 0) {
fclose(output_handle_);
} else {
result = output_handle_;
}
}
output_handle_ = NULL;
if (ll_output_handle_ != NULL) fclose(ll_output_handle_);
ll_output_handle_ = NULL;
DeleteArray(message_buffer_);
message_buffer_ = NULL;
......@@ -280,27 +192,7 @@ void Log::Close() {
mutex_ = NULL;
is_stopped_ = false;
}
int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) {
if (write_to_file_) return 0;
ASSERT(output_buffer_ != NULL);
ASSERT(from_pos >= 0);
ASSERT(max_size >= 0);
int actual_size = output_buffer_->Read(from_pos, dest_buf, max_size);
ASSERT(actual_size <= max_size);
if (actual_size == 0) return 0;
// Find previous log line boundary.
char* end_pos = dest_buf + actual_size - 1;
while (end_pos >= dest_buf && *end_pos != '\n') --end_pos;
actual_size = static_cast<int>(end_pos - dest_buf + 1);
// If the assertion below is hit, it means that there was no line end
// found --- something wrong has happened.
ASSERT(actual_size > 0);
ASSERT(actual_size <= max_size);
return actual_size;
return result;
}
......@@ -409,9 +301,7 @@ void LogMessageBuilder::AppendStringPart(const char* str, int len) {
void LogMessageBuilder::WriteToLogFile() {
ASSERT(pos_ <= Log::kMessageBufferSize);
const int written = log_->write_to_file_ ?
log_->WriteToFile(log_->message_buffer_, pos_) :
log_->WriteToMemory(log_->message_buffer_, pos_);
const int written = log_->WriteToFile(log_->message_buffer_, pos_);
if (written != pos_) {
log_->stop();
log_->logger_->LogFailure();
......
......@@ -35,65 +35,9 @@ namespace internal {
class Logger;
// A memory buffer that increments its size as you write in it. Size
// is incremented with 'block_size' steps, never exceeding 'max_size'.
// During growth, memory contents are never copied. At the end of the
// buffer an amount of memory specified in 'seal_size' is reserved.
// When writing position reaches max_size - seal_size, buffer auto-seals
// itself with 'seal' and allows no further writes. Data pointed by
// 'seal' must be available during entire LogDynamicBuffer lifetime.
//
// An instance of this class is created dynamically by Log.
class LogDynamicBuffer {
public:
LogDynamicBuffer(
int block_size, int max_size, const char* seal, int seal_size);
~LogDynamicBuffer();
// Reads contents of the buffer starting from 'from_pos'. Upon
// return, 'dest_buf' is filled with the data. Actual amount of data
// filled is returned, it is <= 'buf_size'.
int Read(int from_pos, char* dest_buf, int buf_size);
// Writes 'data' to the buffer, making it larger if necessary. If
// data is too big to fit in the buffer, it doesn't get written at
// all. In that case, buffer auto-seals itself and stops to accept
// any incoming writes. Returns amount of data written (it is either
// 'data_size', or 0, if 'data' is too big).
int Write(const char* data, int data_size);
private:
void AllocateBlock(int index) {
blocks_[index] = NewArray<char>(block_size_);
}
int BlockIndex(int pos) const { return pos / block_size_; }
int BlocksCount() const { return BlockIndex(max_size_) + 1; }
int PosInBlock(int pos) const { return pos % block_size_; }
int Seal();
int WriteInternal(const char* data, int data_size);
const int block_size_;
const int max_size_;
const char* seal_;
const int seal_size_;
ScopedVector<char*> blocks_;
int write_pos_;
int block_index_;
int block_write_pos_;
bool is_sealed_;
};
// Functions and data for performing output of log messages.
class Log {
public:
// Performs process-wide initialization.
void Initialize();
......@@ -101,18 +45,21 @@ class Log {
void stop() { is_stopped_ = true; }
// Frees all resources acquired in Initialize and Open... functions.
void Close();
// See description in include/v8.h.
int GetLogLines(int from_pos, char* dest_buf, int max_size);
// When a temporary file is used for the log, returns its stream descriptor,
// leaving the file open.
FILE* Close();
// Returns whether logging is enabled.
bool IsEnabled() {
return !is_stopped_ && (output_handle_ != NULL || output_buffer_ != NULL);
return !is_stopped_ && output_handle_ != NULL;
}
// Size of buffer used for formatting log messages.
static const int kMessageBufferSize = v8::V8::kMinimumSizeForLogLinesBuffer;
static const int kMessageBufferSize = 2048;
// This mode is only used in tests, as temporary files are automatically
// deleted on close and thus can't be accessed afterwards.
static const char* kLogToTemporaryFile;
private:
explicit Log(Logger* logger);
......@@ -123,8 +70,8 @@ class Log {
// Opens file for logging.
void OpenFile(const char* name);
// Opens memory buffer for logging.
void OpenMemoryBuffer();
// Opens a temporary file for logging.
void OpenTemporaryFile();
// Implementation of writing to a log file.
int WriteToFile(const char* msg, int length) {
......@@ -136,38 +83,16 @@ class Log {
return length;
}
// Implementation of writing to a memory buffer.
int WriteToMemory(const char* msg, int length) {
ASSERT(output_buffer_ != NULL);
return output_buffer_->Write(msg, length);
}
bool write_to_file_;
// Whether logging is stopped (e.g. due to insufficient resources).
bool is_stopped_;
// When logging is active, either output_handle_ or output_buffer_ is used
// to store a pointer to log destination. If logging was opened via OpenStdout
// or OpenFile, then output_handle_ is used. If logging was opened
// via OpenMemoryBuffer, then output_buffer_ is used.
// mutex_ should be acquired before using output_handle_ or output_buffer_.
// When logging is active output_handle_ is used to store a pointer to log
// destination. mutex_ should be acquired before using output_handle_.
FILE* output_handle_;
// Used when low-level profiling is active.
FILE* ll_output_handle_;
LogDynamicBuffer* output_buffer_;
// Size of dynamic buffer block (and dynamic buffer initial size).
static const int kDynamicBufferBlockSize = 65536;
// Maximum size of dynamic buffer.
static const int kMaxDynamicBufferSize = 50 * 1024 * 1024;
// Message to "seal" dynamic buffer with.
static const char* const kDynamicBufferSeal;
// mutex_ is a Mutex used for enforcing exclusive
// access to the formatting buffer and the log file or log memory buffer.
Mutex* mutex_;
......
......@@ -1281,7 +1281,6 @@ void Logger::PauseProfiler() {
ticker_->Stop();
}
FLAG_log_code = false;
// Must be the same message as Log::kDynamicBufferSeal.
LOG(ISOLATE, UncheckedStringEvent("profiler", "pause"));
}
--logging_nesting_;
......@@ -1323,11 +1322,6 @@ bool Logger::IsProfilerSamplerActive() {
}
int Logger::GetLogLines(int from_pos, char* dest_buf, int max_size) {
return log_->GetLogLines(from_pos, dest_buf, max_size);
}
class EnumerateOptimizedFunctionsVisitor: public OptimizedFunctionVisitor {
public:
EnumerateOptimizedFunctionsVisitor(Handle<SharedFunctionInfo>* sfis,
......@@ -1683,8 +1677,8 @@ void Logger::EnsureTickerStopped() {
}
void Logger::TearDown() {
if (!is_initialized_) return;
FILE* Logger::TearDown() {
if (!is_initialized_) return NULL;
is_initialized_ = false;
// Stop the profiler before closing the file.
......@@ -1700,7 +1694,7 @@ void Logger::TearDown() {
delete ticker_;
ticker_ = NULL;
log_->Close();
return log_->Close();
}
......
......@@ -157,7 +157,9 @@ class Logger {
Sampler* sampler();
// Frees resources acquired in Setup.
void TearDown();
// When a temporary file is used for the log, returns its stream descriptor,
// leaving the file open.
FILE* TearDown();
// Enable the computation of a sliding window of states.
void EnableSlidingStateWindow();
......@@ -279,10 +281,6 @@ class Logger {
void ResumeProfiler();
bool IsProfilerPaused();
// If logging is performed into a memory buffer, allows to
// retrieve previously written messages. See v8.h.
int GetLogLines(int from_pos, char* dest_buf, int max_size);
// Logs all compiled functions found in the heap.
void LogCompiledFunctions();
// Logs all accessor callbacks found in the heap.
......
......@@ -147,6 +147,11 @@ bool OS::Remove(const char* path) {
}
FILE* OS::OpenTemporaryFile() {
return tmpfile();
}
const char* const OS::LogFileOpenMode = "w";
......
......@@ -740,6 +740,24 @@ bool OS::Remove(const char* path) {
}
FILE* OS::OpenTemporaryFile() {
// tmpfile_s tries to use the root dir, don't use it.
char tempPathBuffer[MAX_PATH];
DWORD path_result = 0;
path_result = GetTempPath(MAX_PATH, tempPathBuffer);
if (path_result > MAX_PATH || path_result == 0) return NULL;
UINT name_result = 0;
char tempNameBuffer[MAX_PATH];
name_result = GetTempFileName(tempPathBuffer, "", 0, tempNameBuffer);
if (name_result == 0) return NULL;
FILE* result = FOpen(tempNameBuffer, "w+"); // Same mode as tmpfile uses.
if (result != NULL) {
Remove(tempNameBuffer); // Delete on close.
}
return result;
}
// Open log file in binary mode to avoid /n -> /r/n conversion.
const char* const OS::LogFileOpenMode = "wb";
......
......@@ -177,6 +177,9 @@ class OS {
static FILE* FOpen(const char* path, const char* mode);
static bool Remove(const char* path);
// Opens a temporary file, the file is auto removed on close.
static FILE* OpenTemporaryFile();
// Log file open mode is platform-dependent due to line ends issues.
static const char* const LogFileOpenMode;
......
......@@ -110,11 +110,11 @@ char* ReadLine(const char* prompt) {
}
char* ReadCharsFromFile(const char* filename,
char* ReadCharsFromFile(FILE* file,
int* size,
int extra_space,
bool verbose) {
FILE* file = OS::FOpen(filename, "rb");
bool verbose,
const char* filename) {
if (file == NULL || fseek(file, 0, SEEK_END) != 0) {
if (verbose) {
OS::PrintError("Cannot read from file %s.\n", filename);
......@@ -127,16 +127,26 @@ char* ReadCharsFromFile(const char* filename,
rewind(file);
char* result = NewArray<char>(*size + extra_space);
for (int i = 0; i < *size;) {
for (int i = 0; i < *size && feof(file) == 0;) {
int read = static_cast<int>(fread(&result[i], 1, *size - i, file));
if (read <= 0) {
if (read != (*size - i) && ferror(file) != 0) {
fclose(file);
DeleteArray(result);
return NULL;
}
i += read;
}
fclose(file);
return result;
}
char* ReadCharsFromFile(const char* filename,
int* size,
int extra_space,
bool verbose) {
FILE* file = OS::FOpen(filename, "rb");
char* result = ReadCharsFromFile(file, size, extra_space, verbose, filename);
if (file != NULL) fclose(file);
return result;
}
......@@ -147,18 +157,34 @@ byte* ReadBytes(const char* filename, int* size, bool verbose) {
}
static Vector<const char> SetVectorContents(char* chars,
int size,
bool* exists) {
if (!chars) {
*exists = false;
return Vector<const char>::empty();
}
chars[size] = '\0';
*exists = true;
return Vector<const char>(chars, size);
}
Vector<const char> ReadFile(const char* filename,
bool* exists,
bool verbose) {
int size;
char* result = ReadCharsFromFile(filename, &size, 1, verbose);
if (!result) {
*exists = false;
return Vector<const char>::empty();
}
result[size] = '\0';
*exists = true;
return Vector<const char>(result, size);
return SetVectorContents(result, size, exists);
}
Vector<const char> ReadFile(FILE* file,
bool* exists,
bool verbose) {
int size;
char* result = ReadCharsFromFile(file, &size, 1, verbose, "");
return SetVectorContents(result, size, exists);
}
......
......@@ -188,6 +188,9 @@ class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource {
Vector<const char> ReadFile(const char* filename,
bool* exists,
bool verbose = true);
Vector<const char> ReadFile(FILE* file,
bool* exists,
bool verbose = true);
......
......@@ -65,7 +65,6 @@ SOURCES = {
'test-liveedit.cc',
'test-lock.cc',
'test-lockers.cc',
'test-log-utils.cc',
'test-log.cc',
'test-mark-compact.cc',
'test-parsing.cc',
......
......@@ -71,7 +71,6 @@
'test-lock.cc',
'test-lockers.cc',
'test-log.cc',
'test-log-utils.cc',
'test-mark-compact.cc',
'test-parsing.cc',
'test-profile-generator.cc',
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This is a supplementary file for test-log/EquivalenceOfLoggingAndTraversal.
function parseState(s) {
switch (s) {
case "": return Profile.CodeState.COMPILED;
case "~": return Profile.CodeState.OPTIMIZABLE;
case "*": return Profile.CodeState.OPTIMIZED;
}
throw new Error("unknown code state: " + s);
}
function LogProcessor() {
LogReader.call(this, {
'code-creation': {
parsers: [null, parseInt, parseInt, null, 'var-args'],
processor: this.processCodeCreation },
'code-move': { parsers: [parseInt, parseInt],
processor: this.processCodeMove },
'code-delete': { parsers: [parseInt],
processor: this.processCodeDelete },
'sfi-move': { parsers: [parseInt, parseInt],
processor: this.processFunctionMove },
'shared-library': null,
'profiler': null,
'tick': null });
this.profile = new Profile();
}
LogProcessor.prototype.__proto__ = LogReader.prototype;
LogProcessor.prototype.processCodeCreation = function(
type, start, size, name, maybe_func) {
if (type != "LazyCompile" && type != "Script" && type != "Function") return;
// Discard types to avoid discrepancies in "LazyCompile" vs. "Function".
type = "";
if (maybe_func.length) {
var funcAddr = parseInt(maybe_func[0]);
var state = parseState(maybe_func[1]);
this.profile.addFuncCode(type, name, start, size, funcAddr, state);
} else {
this.profile.addCode(type, name, start, size);
}
};
LogProcessor.prototype.processCodeMove = function(from, to) {
this.profile.moveCode(from, to);
};
LogProcessor.prototype.processCodeDelete = function(start) {
this.profile.deleteCode(start);
};
LogProcessor.prototype.processFunctionMove = function(from, to) {
this.profile.moveFunc(from, to);
};
function RunTest() {
// _log must be provided externally.
var log_lines = _log.split("\n");
var line, pos = 0, log_lines_length = log_lines.length;
if (log_lines_length < 2)
return "log_lines_length < 2";
var logging_processor = new LogProcessor();
for ( ; pos < log_lines_length; ++pos) {
line = log_lines[pos];
if (line === "test-logging-done,\"\"") {
++pos;
break;
}
logging_processor.processLogLine(line);
}
logging_processor.profile.cleanUpFuncEntries();
var logging_entries =
logging_processor.profile.codeMap_.getAllDynamicEntriesWithAddresses();
if (logging_entries.length === 0)
return "logging_entries.length === 0";
var traversal_processor = new LogProcessor();
for ( ; pos < log_lines_length; ++pos) {
line = log_lines[pos];
if (line === "test-traversal-done,\"\"") break;
traversal_processor.processLogLine(line);
}
var traversal_entries =
traversal_processor.profile.codeMap_.getAllDynamicEntriesWithAddresses();
if (traversal_entries.length === 0)
return "traversal_entries.length === 0";
function addressComparator(entryA, entryB) {
return entryA[0] < entryB[0] ? -1 : (entryA[0] > entryB[0] ? 1 : 0);
}
logging_entries.sort(addressComparator);
traversal_entries.sort(addressComparator);
function entityNamesEqual(entityA, entityB) {
if ("getRawName" in entityB &&
entityNamesEqual.builtins.indexOf(entityB.getRawName()) !== -1) {
return true;
}
if (entityNamesEqual.builtins.indexOf(entityB.getName()) !== -1) return true;
return entityA.getName() === entityB.getName();
}
entityNamesEqual.builtins =
["Boolean", "Function", "Number", "Object",
"Script", "String", "RegExp", "Date", "Error"];
function entitiesEqual(entityA, entityB) {
if (entityA === null && entityB !== null) return true;
if (entityA !== null && entityB === null) return false;
return entityA.size === entityB.size && entityNamesEqual(entityA, entityB);
}
var i = 0, j = 0, k = logging_entries.length, l = traversal_entries.length;
var comparison = [];
var equal = true;
// Do a merge-like comparison of entries. At the same address we expect to
// find the same entries. We skip builtins during log parsing, but compiled
// functions traversal may erroneously recognize them as functions, so we are
// expecting more functions in traversal vs. logging.
while (i < k && j < l) {
var entryA = logging_entries[i], entryB = traversal_entries[j];
var cmp = addressComparator(entryA, entryB);
var entityA = entryA[1], entityB = entryB[1];
var address = entryA[0];
if (cmp < 0) {
++i;
entityB = null;
} else if (cmp > 0) {
++j;
entityA = null;
address = entryB[0];
} else {
++i;
++j;
}
var entities_equal = entitiesEqual(entityA, entityB);
if (!entities_equal) equal = false;
comparison.push([entities_equal, address, entityA, entityB]);
}
if (i < k) equal = false;
while (i < k) {
var entryA = logging_entries[i++];
comparison.push([false, entryA[0], entryA[1], null]);
}
return [equal, comparison];
}
var result = RunTest();
if (typeof result !== "string") {
var out = [];
if (!result[0]) {
var comparison = result[1];
for (var i = 0, l = comparison.length; i < l; ++i) {
var c = comparison[i];
out.push((c[0] ? " " : "* ") +
c[1].toString(16) + " " +
(c[2] ? c[2] : "---") + " " +
(c[3] ? c[3] : "---"));
}
}
result[0] ? true : out.join("\n");
} else {
result;
}
// Copyright 2006-2009 the V8 project authors. All rights reserved.
//
// Tests of logging utilities from log-utils.h
#include "v8.h"
#include "log-utils.h"
#include "cctest.h"
using v8::internal::CStrVector;
using v8::internal::EmbeddedVector;
using v8::internal::LogDynamicBuffer;
using v8::internal::MutableCStrVector;
using v8::internal::ScopedVector;
using v8::internal::Vector;
using v8::internal::StrLength;
// Fills 'ref_buffer' with test data: a sequence of two-digit
// hex numbers: '0001020304...'. Then writes 'ref_buffer' contents to 'dynabuf'.
static void WriteData(LogDynamicBuffer* dynabuf, Vector<char>* ref_buffer) {
static const char kHex[] = "0123456789ABCDEF";
CHECK_GT(ref_buffer->length(), 0);
CHECK_GT(513, ref_buffer->length());
for (int i = 0, half_len = ref_buffer->length() >> 1; i < half_len; ++i) {
(*ref_buffer)[i << 1] = kHex[i >> 4];
(*ref_buffer)[(i << 1) + 1] = kHex[i & 15];
}
if (ref_buffer->length() & 1) {
ref_buffer->last() = kHex[ref_buffer->length() >> 5];
}
CHECK_EQ(ref_buffer->length(),
dynabuf->Write(ref_buffer->start(), ref_buffer->length()));
}
static int ReadData(
LogDynamicBuffer* dynabuf, int start_pos, i::Vector<char>* buffer) {
return dynabuf->Read(start_pos, buffer->start(), buffer->length());
}
// Helper function used by CHECK_EQ to compare Vectors. Templatized to
// accept both "char" and "const char" vector contents.
template <typename E, typename V>
static inline void CheckEqualsHelper(const char* file, int line,
const char* expected_source,
const Vector<E>& expected,
const char* value_source,
const Vector<V>& value) {
if (expected.length() != value.length()) {
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n"
"# Vectors lengths differ: %d expected, %d found\n"
"# Expected: %.*s\n"
"# Found: %.*s",
expected_source, value_source,
expected.length(), value.length(),
expected.length(), expected.start(),
value.length(), value.start());
}
if (strncmp(expected.start(), value.start(), expected.length()) != 0) {
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n"
"# Vectors contents differ:\n"
"# Expected: %.*s\n"
"# Found: %.*s",
expected_source, value_source,
expected.length(), expected.start(),
value.length(), value.start());
}
}
TEST(DynaBufSingleBlock) {
LogDynamicBuffer dynabuf(32, 32, "", 0);
EmbeddedVector<char, 32> ref_buf;
WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 32> buf;
CHECK_EQ(32, dynabuf.Read(0, buf.start(), buf.length()));
CHECK_EQ(32, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
// Verify that we can't read and write past the end.
CHECK_EQ(0, dynabuf.Read(32, buf.start(), buf.length()));
CHECK_EQ(0, dynabuf.Write(buf.start(), buf.length()));
}
TEST(DynaBufCrossBlocks) {
LogDynamicBuffer dynabuf(32, 128, "", 0);
EmbeddedVector<char, 48> ref_buf;
WriteData(&dynabuf, &ref_buf);
CHECK_EQ(48, dynabuf.Write(ref_buf.start(), ref_buf.length()));
// Verify that we can't write data when remaining buffer space isn't enough.
CHECK_EQ(0, dynabuf.Write(ref_buf.start(), ref_buf.length()));
EmbeddedVector<char, 48> buf;
CHECK_EQ(48, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
CHECK_EQ(48, ReadData(&dynabuf, 48, &buf));
CHECK_EQ(ref_buf, buf);
CHECK_EQ(0, ReadData(&dynabuf, 48 * 2, &buf));
}
TEST(DynaBufReadTruncation) {
LogDynamicBuffer dynabuf(32, 128, "", 0);
EmbeddedVector<char, 128> ref_buf;
WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 128> buf;
CHECK_EQ(128, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
// Try to read near the end with a buffer larger than remaining data size.
EmbeddedVector<char, 48> tail_buf;
CHECK_EQ(32, ReadData(&dynabuf, 128 - 32, &tail_buf));
CHECK_EQ(ref_buf.SubVector(128 - 32, 128), tail_buf.SubVector(0, 32));
}
TEST(DynaBufSealing) {
const char* seal = "Sealed";
const int seal_size = StrLength(seal);
LogDynamicBuffer dynabuf(32, 128, seal, seal_size);
EmbeddedVector<char, 100> ref_buf;
WriteData(&dynabuf, &ref_buf);
// Try to write data that will not fit in the buffer.
CHECK_EQ(0, dynabuf.Write(ref_buf.start(), 128 - 100 - seal_size + 1));
// Now the buffer is sealed, writing of any amount of data is forbidden.
CHECK_EQ(0, dynabuf.Write(ref_buf.start(), 1));
EmbeddedVector<char, 100> buf;
CHECK_EQ(100, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
// Check the seal.
EmbeddedVector<char, 50> seal_buf;
CHECK_EQ(seal_size, ReadData(&dynabuf, 100, &seal_buf));
CHECK_EQ(CStrVector(seal), seal_buf.SubVector(0, seal_size));
// Verify that there's no data beyond the seal.
CHECK_EQ(0, ReadData(&dynabuf, 100 + seal_size, &buf));
}
This diff is collapsed.
......@@ -26,7 +26,8 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Load source code files from <project root>/tools.
// Files: tools/consarray.js tools/profile.js tools/profile_view.js
// Files: tools/codemap.js tools/consarray.js tools/profile.js
// Files: tools/profile_view.js
function createNode(name, time, opt_parent) {
......
......@@ -210,6 +210,14 @@ CodeMap.prototype.getAllDynamicEntries = function() {
};
/**
* Returns an array of pairs of all dynamic code entries and their addresses.
*/
CodeMap.prototype.getAllDynamicEntriesWithAddresses = function() {
return this.dynamics_.exportKeysAndValues();
};
/**
* Returns an array of all static code entries.
*/
......
......@@ -162,8 +162,16 @@ Profile.prototype.addFuncCode = function(
// Function object has been overwritten with a new one.
func.name = name;
}
var entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
this.codeMap_.addCode(start, entry);
var entry = this.codeMap_.findDynamicEntryByStartAddress(start);
if (entry) {
if (entry.size === size && entry.func === func) {
// Entry state has changed.
entry.state = state;
}
} else {
entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
this.codeMap_.addCode(start, entry);
}
return entry;
};
......@@ -373,6 +381,31 @@ Profile.prototype.getFlatProfile = function(opt_label) {
};
/**
* Cleans up function entries that are not referenced by code entries.
*/
Profile.prototype.cleanUpFuncEntries = function() {
var referencedFuncEntries = [];
var entries = this.codeMap_.getAllDynamicEntriesWithAddresses();
for (var i = 0, l = entries.length; i < l; ++i) {
if (entries[i][1].constructor === Profile.FunctionEntry) {
entries[i][1].used = false;
}
}
for (var i = 0, l = entries.length; i < l; ++i) {
if ("func" in entries[i][1]) {
entries[i][1].func.used = true;
}
}
for (var i = 0, l = entries.length; i < l; ++i) {
if (entries[i][1].constructor === Profile.FunctionEntry &&
!entries[i][1].used) {
this.codeMap_.deleteCode(entries[i][0]);
}
}
};
/**
* Creates a dynamic code entry.
*
......@@ -408,6 +441,11 @@ Profile.DynamicCodeEntry.prototype.isJSFunction = function() {
};
Profile.DynamicCodeEntry.prototype.toString = function() {
return this.getName() + ': ' + this.size.toString(16);
};
/**
* Creates a dynamic code entry.
*
......@@ -448,6 +486,11 @@ Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function() {
};
Profile.DynamicFuncCodeEntry.prototype.toString = function() {
return this.getName() + ': ' + this.size.toString(16);
};
/**
* Creates a shared function object entry.
*
......@@ -473,6 +516,7 @@ Profile.FunctionEntry.prototype.getName = function() {
return name;
};
Profile.FunctionEntry.prototype.toString = CodeMap.CodeEntry.prototype.toString;
/**
* Constructs a call graph.
......
......@@ -190,6 +190,17 @@ SplayTree.prototype.findGreatestLessThan = function(key) {
};
/**
* @return {Array<*>} An array containing all the values of tree's nodes paired
* with keys.
*/
SplayTree.prototype.exportKeysAndValues = function() {
var result = [];
this.traverse_(function(node) { result.push([node.key, node.value]); });
return result;
};
/**
* @return {Array<*>} An array containing all the values of tree's nodes.
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment