Actially, I remember I had problems with strtol when parsing addresses
starting with 0xf ("negatives" in terms of signed ints). OK, I'll
revisit this CL to be sure that it works on Windows.2009/5/20 Søren Gjesse <[email protected]>: > Mikhail, > > It looks as if strtoll is not available on Windows. For now strtol should > work. > > /Søren > > On Wed, May 20, 2009 at 11:04, <[email protected]> wrote: >> >> Author: [email protected] >> Date: Wed May 20 02:04:13 2009 >> New Revision: 2009 >> >> Modified: >> branches/bleeding_edge/src/compiler.cc >> branches/bleeding_edge/src/log.cc >> branches/bleeding_edge/src/log.h >> branches/bleeding_edge/test/cctest/test-log.cc >> >> Log: >> Introduce Logger::LogCompiledFunctions that logs current map of compiled >> code. >> >> The goal is to make possible having --prof flag always enabled in >> Chromium. Currently we can't do this because --prof causes compiler and >> gc >> to log code creations / moves / deletes which aren't needed until we start >> profiling. With LogCompiledFunctions it will be possible not to log >> anything until we start profiling. When started, the current map of >> compiled functions will be logged and compiler / gc logging will be >> enabled >> to update current state. When profling is stopped, logging will be turned >> off again. >> >> Funny that testing code is actually much longer and complex than function >> code. >> >> Review URL: http://codereview.chromium.org/112036 >> >> Modified: branches/bleeding_edge/src/compiler.cc >> >> ============================================================================== >> --- branches/bleeding_edge/src/compiler.cc (original) >> +++ branches/bleeding_edge/src/compiler.cc Wed May 20 02:04:13 2009 >> @@ -357,8 +357,8 @@ >> // name and line number. Check explicit whether logging is enabled as >> finding >> // the line number is not for free. >> if (Logger::is_enabled() || OProfileAgent::is_enabled()) { >> - Handle<String> func_name(lit->name()->length() > 0 ? >> - *lit->name() : shared->inferred_name()); >> + Handle<String> func_name(name->length() > 0 ? >> + *name : shared->inferred_name()); >> if (script->name()->IsString()) { >> int line_num = GetScriptLineNumber(script, start_position); >> if (line_num > 0) { >> >> Modified: branches/bleeding_edge/src/log.cc >> >> ============================================================================== >> --- branches/bleeding_edge/src/log.cc (original) >> +++ branches/bleeding_edge/src/log.cc Wed May 20 02:04:13 2009 >> @@ -1108,6 +1108,69 @@ >> return Log::GetLogLines(from_pos, dest_buf, max_size); >> } >> >> + >> +void Logger::LogCompiledFunctions() { >> + HandleScope scope; >> + Handle<SharedFunctionInfo>* sfis = NULL; >> + int compiled_funcs_count = 0; >> + >> + { >> + AssertNoAllocation no_alloc; >> + >> + HeapIterator iterator; >> + while (iterator.has_next()) { >> + HeapObject* obj = iterator.next(); >> + ASSERT(obj != NULL); >> + if (obj->IsSharedFunctionInfo() >> + && SharedFunctionInfo::cast(obj)->is_compiled()) { >> + ++compiled_funcs_count; >> + } >> + } >> + >> + sfis = NewArray< Handle<SharedFunctionInfo> >(compiled_funcs_count); >> + iterator.reset(); >> + >> + int i = 0; >> + while (iterator.has_next()) { >> + HeapObject* obj = iterator.next(); >> + ASSERT(obj != NULL); >> + if (obj->IsSharedFunctionInfo() >> + && SharedFunctionInfo::cast(obj)->is_compiled()) { >> + sfis[i++] = >> Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj)); >> + } >> + } >> + } >> + >> + // During iteration, there can be heap allocation due to >> + // GetScriptLineNumber call. >> + for (int i = 0; i < compiled_funcs_count; ++i) { >> + Handle<SharedFunctionInfo> shared = sfis[i]; >> + Handle<String> name(String::cast(shared->name())); >> + Handle<String> func_name(name->length() > 0 ? >> + *name : shared->inferred_name()); >> + if (shared->script()->IsScript()) { >> + Handle<Script> script(Script::cast(shared->script())); >> + if (script->name()->IsString()) { >> + Handle<String> script_name(String::cast(script->name())); >> + int line_num = GetScriptLineNumber(script, >> shared->start_position()); >> + if (line_num > 0) { >> + line_num += script->line_offset()->value() + 1; >> + LOG(CodeCreateEvent("LazyCompile", shared->code(), *func_name, >> + *script_name, line_num)); >> + } else { >> + // Can't distinguish enum and script here, so always use >> Script. >> + LOG(CodeCreateEvent("Script", shared->code(), *script_name)); >> + } >> + continue; >> + } >> + } >> + // If no script or script has no name. >> + LOG(CodeCreateEvent("LazyCompile", shared->code(), *func_name)); >> + } >> + >> + DeleteArray(sfis); >> +} >> + >> #endif >> >> >> >> Modified: branches/bleeding_edge/src/log.h >> >> ============================================================================== >> --- branches/bleeding_edge/src/log.h (original) >> +++ branches/bleeding_edge/src/log.h Wed May 20 02:04:13 2009 >> @@ -214,6 +214,9 @@ >> // retrieve previously written messages. See v8.h. >> static int GetLogLines(int from_pos, char* dest_buf, int max_size); >> >> + // Logs all compiled functions found in the heap. >> + static void LogCompiledFunctions(); >> + >> private: >> >> // Emits the source code of a regexp. Used by regexp events. >> >> Modified: branches/bleeding_edge/test/cctest/test-log.cc >> >> ============================================================================== >> --- branches/bleeding_edge/test/cctest/test-log.cc (original) >> +++ branches/bleeding_edge/test/cctest/test-log.cc Wed May 20 >> 02:04:13 2009 >> @@ -7,15 +7,17 @@ >> #include "v8.h" >> >> #include "log.h" >> - >> #include "cctest.h" >> >> +using v8::internal::Address; >> using v8::internal::Logger; >> >> +namespace i = v8::internal; >> + >> static void SetUp() { >> // Log to memory buffer. >> - v8::internal::FLAG_logfile = "*"; >> - v8::internal::FLAG_log = true; >> + i::FLAG_logfile = "*"; >> + i::FLAG_log = true; >> Logger::Setup(); >> } >> >> @@ -103,14 +105,383 @@ >> >> TEST(MemoryLoggingTurnedOff) { >> // Log to stdout >> - v8::internal::FLAG_logfile = "-"; >> - v8::internal::FLAG_log = true; >> + i::FLAG_logfile = "-"; >> + i::FLAG_log = true; >> Logger::Setup(); >> CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0)); >> CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0)); >> CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100)); >> CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100)); >> Logger::TearDown(); >> +} >> + >> + >> +static inline bool IsStringEqualTo(const char* r, const char* s) { >> + return strncmp(r, s, strlen(r)) == 0; >> +} >> + >> + >> +static bool Consume(const char* str, char** buf) { >> + if (IsStringEqualTo(str, *buf)) { >> + *buf += strlen(str); >> + return true; >> + } >> + return false; >> +} >> + >> + >> +static void ParseAddress(char* start, Address* min_addr, Address* >> max_addr) { >> + Address addr = reinterpret_cast<Address>(strtoll(start, NULL, 16)); >> + if (addr < *min_addr) *min_addr = addr; >> + if (addr > *max_addr) *max_addr = addr; >> +} >> + >> + >> +static Address ConsumeAddress( >> + char** start, Address min_addr, Address max_addr) { >> + char* end_ptr; >> + Address addr = reinterpret_cast<Address>(strtoll(*start, &end_ptr, >> 16)); >> + CHECK_GE(addr, min_addr); >> + CHECK_GE(max_addr, addr); >> + *start = end_ptr; >> + return addr; >> +} >> + >> + >> +namespace { >> + >> +// A code entity is a pointer to a position of code-creation event in >> buffer log >> +// offset to a point where entity size begins, i.e.: '255,"func"\n'. This >> makes >> +// comparing code entities pretty easy. >> +typedef char* CodeEntityInfo; >> + >> +// A structure used to return log parsing results. >> +class ParseLogResult { >> + public: >> + ParseLogResult() >> + : min_addr(reinterpret_cast<Address>(-1)), >> + max_addr(reinterpret_cast<Address>(0)), >> + entities_map(NULL), entities(NULL), >> + max_entities(0) {}; >> + >> + ~ParseLogResult() { >> + // See allocation code below. >> + if (entities_map != NULL) { >> + i::DeleteArray(entities_map - 1); >> + } >> + i::DeleteArray(entities); >> + } >> + >> + void AllocateEntities() { >> + // Make sure that the test doesn't operate on a bogus log. >> + CHECK_GT(max_entities, 0); >> + CHECK_GT(min_addr, 0); >> + CHECK_GT(max_addr, min_addr); >> + >> + entities = i::NewArray<CodeEntityInfo>(max_entities); >> + for (int i = 0; i < max_entities; ++i) { >> + entities[i] = NULL; >> + } >> + // We're adding fake items at [-1] and [size + 1] to simplify >> + // comparison code. >> + const int map_length = max_addr - min_addr + 1 + 2; // 2 fakes. >> + entities_map = i::NewArray<int>(map_length); >> + for (int i = 0; i < map_length; ++i) { >> + entities_map[i] = -1; >> + } >> + entities_map += 1; // Hide the -1 item, this is compensated on >> delete. >> + } >> + >> + // Minimal code entity address. >> + Address min_addr; >> + // Maximal code entity address. >> + Address max_addr; >> + // Memory map of entities start addresses. Biased by min_addr. >> + int* entities_map; >> + // An array of code entities. >> + CodeEntityInfo* entities; >> + // Maximal entities count. Actual entities count can be lower, >> + // empty entity slots are pointing to NULL. >> + int max_entities; >> +}; >> + >> +} // namespace >> + >> + >> +typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* >> result); >> + >> +static void ParserCycle( >> + char* start, char* end, ParseLogResult* result, >> + ParserBlock block_creation, ParserBlock block_delete, >> + ParserBlock block_move) { >> + >> + const char* code_creation = "code-creation,"; >> + const char* code_delete = "code-delete,"; >> + const char* code_move = "code-move,"; >> + >> + const char* lazy_compile = "LazyCompile,"; >> + const char* script = "Script,"; >> + const char* function = "Function,"; >> + >> + while (start < end) { >> + if (Consume(code_creation, &start)) { >> + if (Consume(lazy_compile, &start) >> + || Consume(script, &start) >> + || Consume(function, &start)) { >> + block_creation(start, end, result); >> + } >> + } else if (Consume(code_delete, &start)) { >> + block_delete(start, end, result); >> + } else if (Consume(code_move, &start)) { >> + block_move(start, end, result); >> + } >> + while (start < end && *start != '\n') ++start; >> + ++start; >> + } >> +} >> + >> + >> +static void Pass1CodeCreation(char* start, char* end, ParseLogResult* >> result) { >> + ParseAddress(start, &result->min_addr, &result->max_addr); >> + ++result->max_entities; >> +} >> + >> + >> +static void Pass1CodeDelete(char* start, char* end, ParseLogResult* >> result) { >> + ParseAddress(start, &result->min_addr, &result->max_addr); >> +} >> + >> + >> +static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) >> { >> + // Skip old address. >> + while (start < end && *start != ',') ++start; >> + CHECK_GT(end, start); >> + ++start; // Skip ','. >> + ParseAddress(start, &result->min_addr, &result->max_addr); >> +} >> + >> + >> +static void Pass2CodeCreation(char* start, char* end, ParseLogResult* >> result) { >> + Address addr = ConsumeAddress(&start, result->min_addr, >> result->max_addr); >> + CHECK_GT(end, start); >> + ++start; // Skip ','. >> + >> + int idx = addr - result->min_addr; >> + result->entities_map[idx] = -1; >> + for (int i = 0; i < result->max_entities; ++i) { >> + // Find an empty slot and fill it. >> + if (result->entities[i] == NULL) { >> + result->entities[i] = start; >> + result->entities_map[idx] = i; >> + break; >> + } >> + } >> + // Make sure that a slot was found. >> + CHECK_GE(result->entities_map[idx], 0); >> +} >> + >> + >> +static void Pass2CodeDelete(char* start, char* end, ParseLogResult* >> result) { >> + Address addr = ConsumeAddress(&start, result->min_addr, >> result->max_addr); >> + int idx = addr - result->min_addr; >> + // There can be code deletes that are not related to JS code. >> + if (result->entities_map[idx] >= 0) { >> + result->entities[result->entities_map[idx]] = NULL; >> + result->entities_map[idx] = -1; >> + } >> +} >> + >> + >> +static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) >> { >> + Address from_addr = ConsumeAddress( >> + &start, result->min_addr, result->max_addr); >> + CHECK_GT(end, start); >> + ++start; // Skip ','. >> + Address to_addr = ConsumeAddress(&start, result->min_addr, >> result->max_addr); >> + CHECK_GT(end, start); >> + >> + int from_idx = from_addr - result->min_addr; >> + int to_idx = to_addr - result->min_addr; >> + // There can be code moves that are not related to JS code. >> + if (from_idx != to_idx && result->entities_map[from_idx] >= 0) { >> + CHECK_EQ(-1, result->entities_map[to_idx]); >> + result->entities_map[to_idx] = result->entities_map[from_idx]; >> + result->entities_map[from_idx] = -1; >> + }; >> +} >> + >> + >> +static void ParseLog(char* start, char* end, ParseLogResult* result) { >> + // Pass 1: Calculate boundaries of addresses and entities count. >> + ParserCycle(start, end, result, >> + Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove); >> + >> + printf("min_addr: %p, max_addr: %p, entities: %d\n", >> + result->min_addr, result->max_addr, result->max_entities); >> + >> + result->AllocateEntities(); >> + >> + // Pass 2: Fill in code entries data. >> + ParserCycle(start, end, result, >> + Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove); >> +} >> + >> + >> +static inline void PrintCodeEntityInfo(CodeEntityInfo entity) { >> + const int max_len = 50; >> + if (entity != NULL) { >> + char* eol = strchr(entity, '\n'); >> + int len = eol - entity; >> + len = len <= max_len ? len : max_len; >> + printf("%-*.*s ", max_len, len, entity); >> + } else { >> + printf("%*s", max_len + 1, ""); >> + } >> +} >> + >> + >> +static void PrintCodeEntitiesInfo( >> + bool is_equal, Address addr, >> + CodeEntityInfo l_entity, CodeEntityInfo r_entity) { >> + printf("%c %p ", is_equal ? ' ' : '*', addr); >> + PrintCodeEntityInfo(l_entity); >> + PrintCodeEntityInfo(r_entity); >> + printf("\n"); >> +} >> + >> + >> +static inline int StrChrLen(const char* s, char c) { >> + return strchr(s, c) - s; >> +} >> + >> + >> +static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) >> { >> + int ref_len = StrChrLen(ref_s, ','); >> + int new_len = StrChrLen(new_s, ','); >> + return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; >> +} >> + >> + >> +static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) >> { >> + // Skip size. >> + ref_s = strchr(ref_s, ',') + 1; >> + new_s = strchr(new_s, ',') + 1; >> + int ref_len = StrChrLen(ref_s, '\n'); >> + int new_len = StrChrLen(new_s, '\n'); >> + // If reference is anonymous (""), it's OK to have anything in new. >> + if (ref_len == 2) return true; >> + // A special case for ErrorPrototype. Haven't yet figured out why they >> + // are different. >> + const char* error_prototype = "\"ErrorPrototype"; >> + if (IsStringEqualTo(error_prototype, ref_s) >> + && IsStringEqualTo(error_prototype, new_s)) { >> + return true; >> + } >> + return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; >> +} >> + >> + >> +static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) >> { >> + if (ref_e == NULL && new_e != NULL) return true; >> + if (ref_e != NULL && new_e != NULL) { >> + return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, >> new_e); >> + } >> + if (ref_e != NULL && new_e == NULL) { >> + // args_count entities (argument adapters) are not found by heap >> traversal, >> + // but they are not needed because they doesn't contain any code. >> + ref_e = strchr(ref_e, ',') + 1; >> + const char* args_count = "\"args_count:"; >> + return IsStringEqualTo(args_count, ref_e); >> + } >> + return false; >> +} >> + >> + >> +// Test that logging of code create / move / delete events >> +// is equivalent to traversal of a resulting heap. >> +TEST(EquivalenceOfLoggingAndTraversal) { >> + i::FLAG_logfile = "*"; >> + i::FLAG_log = true; >> + i::FLAG_log_code = true; >> + >> + // Make sure objects move. >> + bool saved_always_compact = i::FLAG_always_compact; >> + if (!i::FLAG_never_compact) { >> + i::FLAG_always_compact = true; >> + } >> + >> + v8::Persistent<v8::Context> env = v8::Context::New(); >> + v8::HandleScope scope; >> + env->Enter(); >> + >> + // Compile and run a function that creates other functions. >> + v8::Script::Compile(v8::String::New( >> + "(function f() {\n" >> + " var rets = [];\n" >> + " for (var i = 0; i < 100; ++i) {\n" >> + " rets.push((function inc(n) { return n + 1; })(i));\n" >> + " }\n" >> + "})();"))->Run(); >> + i::Heap::CollectAllGarbage(); >> + >> + i::EmbeddedVector<char,204800> buffer; >> + int log_size; >> + ParseLogResult ref_result; >> + >> + // Retrieve the log. >> + { >> + // Make sure that no GCs occur prior to LogCompiledFunctions call. >> + i::AssertNoAllocation no_alloc; >> + >> + log_size = Logger::GetLogLines(0, buffer.start(), buffer.length()); >> + CHECK_GT(log_size, 0); >> + CHECK_GT(buffer.length(), log_size); >> + >> + // Fill a map of compiled code objects. >> + ParseLog(buffer.start(), buffer.start() + log_size, &ref_result); >> + } >> + >> + // Iterate heap to find compiled functions, will write to log. >> + i::Logger::LogCompiledFunctions(); >> + char* new_log_start = buffer.start() + log_size; >> + const int new_log_size = Logger::GetLogLines( >> + log_size, new_log_start, buffer.length() - log_size); >> + CHECK_GT(new_log_size, 0); >> + CHECK_GT(buffer.length(), log_size + new_log_size); >> + >> + // Fill an equivalent map of compiled code objects. >> + ParseLogResult new_result; >> + ParseLog(new_log_start, new_log_start + new_log_size, &new_result); >> + >> + // Test their actual equivalence. >> + bool results_equal = true; >> + int ref_idx = -1, new_idx = -1, ref_inc = 1, new_inc = 1; >> + while (ref_inc > 0 || new_inc > 0) { >> + const Address ref_addr = ref_result.min_addr + ref_idx; >> + const Address new_addr = new_result.min_addr + new_idx; >> + ref_inc = ref_addr <= ref_result.max_addr && ref_addr <= new_addr ? >> 1 : 0; >> + new_inc = new_addr <= new_result.max_addr && new_addr <= ref_addr ? >> 1 : 0; >> + const int ref_item = ref_result.entities_map[ref_idx]; >> + const int new_item = new_result.entities_map[new_idx]; >> + if (ref_item != -1 || new_item != -1) { >> + CodeEntityInfo ref_entity = >> + ref_item != -1 ? ref_result.entities[ref_item] : NULL; >> + CodeEntityInfo new_entity = >> + new_item != -1 ? new_result.entities[new_item] : NULL; >> + const bool equal = AreEntitiesEqual(ref_entity, new_entity); >> + if (!equal) results_equal = false; >> + PrintCodeEntitiesInfo( >> + equal, ref_inc != 0 ? ref_addr : new_addr, >> + ref_entity, new_entity); >> + } >> + ref_idx += ref_inc; >> + new_idx += new_inc; >> + } >> + CHECK(results_equal); >> + >> + env->Exit(); >> + v8::V8::Dispose(); >> + i::FLAG_always_compact = saved_always_compact; >> } >> >> >> >> > > > > > --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---
