Author: [email protected]
Date: Tue Feb 17 06:45:45 2009
New Revision: 1292
Modified:
branches/bleeding_edge/src/log.cc
branches/bleeding_edge/src/platform-freebsd.cc
branches/bleeding_edge/src/platform-linux.cc
branches/bleeding_edge/src/platform-macos.cc
branches/bleeding_edge/src/platform-win32.cc
branches/bleeding_edge/src/platform.h
branches/bleeding_edge/tools/tickprocessor.py
Log:
Added the simplest call stack sampling and call profile in tick processor
output.
Currently only two stack frames are sampled (current function and its
caller).
Output of tick processor looks like this:
[Call profile]:
total call path
15.2% LazyCompile: am3 crypto.js:108 <- LazyCompile: montReduce
crypto.js:583
6.5% LazyCompile: am3 crypto.js:108 <- LazyCompile: bnpSquareTo
crypto.js:431
2.9% Builtin: KeyedStoreIC_Generic <- LazyCompile: montReduce
crypto.js:583
2.3% LazyCompile: am3 crypto.js:108 <- LazyCompile: bnpMultiplyTo
crypto.js:415
Tested under Windows, Linux and OS X.
Review URL: http://codereview.chromium.org/21403
Modified: branches/bleeding_edge/src/log.cc
==============================================================================
--- branches/bleeding_edge/src/log.cc (original)
+++ branches/bleeding_edge/src/log.cc Tue Feb 17 06:45:45 2009
@@ -139,12 +139,14 @@
//
class Ticker: public Sampler {
public:
- explicit Ticker(int interval):
- Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL) {}
+ explicit Ticker(int interval, unsigned int low_stack_bound):
+ Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL),
+ low_stack_bound_(low_stack_bound) {}
~Ticker() { if (IsActive()) Stop(); }
void Tick(TickSample* sample) {
+ if (IsProfiling()) SampleStack(sample);
if (profiler_) profiler_->Insert(sample);
if (window_) window_->AddState(sample->state);
}
@@ -170,8 +172,21 @@
}
private:
+ void SampleStack(TickSample* sample) {
+ // Assuming that stack grows from lower addresses
+ if (sample->sp < sample->fp && sample->fp < low_stack_bound_) {
+ sample->InitStack(1);
+ sample->stack[0] = Memory::Address_at(
+ (Address)(sample->fp + StandardFrameConstants::kCallerPCOffset));
+ } else {
+ // FP seems to be in some intermediate state, better discard this
sample
+ sample->InitStack(0);
+ }
+ }
+
SlidingStateWindow* window_;
Profiler* profiler_;
+ unsigned int low_stack_bound_;
};
@@ -239,9 +254,6 @@
// the thread to terminate.
running_ = false;
TickSample sample;
- sample.pc = 0;
- sample.sp = 0;
- sample.state = OTHER;
Insert(&sample);
Join();
@@ -900,6 +912,11 @@
if (overflow) {
msg.Append(",overflow");
}
+ if (*(sample->stack)) {
+ for (size_t i = 0; sample->stack[i]; ++i) {
+ msg.Append(",0x%x", reinterpret_cast<unsigned
int>(sample->stack[i]));
+ }
+ }
msg.Append('\n');
msg.WriteToLogFile();
}
@@ -990,7 +1007,10 @@
current_state_ = new VMState(OTHER);
- ticker_ = new Ticker(10);
+ // as log is initialized early with V8, we can assume that JS execution
+ // frames can never reach this point on stack
+ int stack_var;
+ ticker_ = new Ticker(10, reinterpret_cast<unsigned int>(&stack_var));
if (FLAG_sliding_state_window && sliding_state_window_ == NULL) {
sliding_state_window_ = new SlidingStateWindow();
Modified: branches/bleeding_edge/src/platform-freebsd.cc
==============================================================================
--- branches/bleeding_edge/src/platform-freebsd.cc (original)
+++ branches/bleeding_edge/src/platform-freebsd.cc Tue Feb 17 06:45:45 2009
@@ -634,9 +634,11 @@
#if defined (__arm__) || defined(__thumb__)
sample.pc = mcontext.mc_r15;
sample.sp = mcontext.mc_r13;
+ sample.fp = mcontext.mc_r11;
#else
sample.pc = mcontext.mc_eip;
sample.sp = mcontext.mc_esp;
+ sample.fp = mcontext.mc_ebp;
#endif
}
Modified: branches/bleeding_edge/src/platform-linux.cc
==============================================================================
--- branches/bleeding_edge/src/platform-linux.cc (original)
+++ branches/bleeding_edge/src/platform-linux.cc Tue Feb 17 06:45:45 2009
@@ -617,9 +617,11 @@
#if defined (__arm__) || defined(__thumb__)
sample.pc = mcontext.gregs[R15];
sample.sp = mcontext.gregs[R13];
+ sample.fp = mcontext.gregs[R11];
#else
sample.pc = mcontext.gregs[REG_EIP];
sample.sp = mcontext.gregs[REG_ESP];
+ sample.fp = mcontext.gregs[REG_EBP];
#endif
}
Modified: branches/bleeding_edge/src/platform-macos.cc
==============================================================================
--- branches/bleeding_edge/src/platform-macos.cc (original)
+++ branches/bleeding_edge/src/platform-macos.cc Tue Feb 17 06:45:45 2009
@@ -583,9 +583,11 @@
#if __DARWIN_UNIX03
sample.pc = mcontext->__ss.__eip;
sample.sp = mcontext->__ss.__esp;
+ sample.fp = mcontext->__ss.__ebp;
#else // !__DARWIN_UNIX03
sample.pc = mcontext->ss.eip;
sample.sp = mcontext->ss.esp;
+ sample.fp = mcontext->ss.ebp;
#endif // __DARWIN_UNIX03
}
Modified: branches/bleeding_edge/src/platform-win32.cc
==============================================================================
--- branches/bleeding_edge/src/platform-win32.cc (original)
+++ branches/bleeding_edge/src/platform-win32.cc Tue Feb 17 06:45:45 2009
@@ -1585,6 +1585,7 @@
// Invoke tick handler with program counter and stack pointer.
sample.pc = context.Eip;
sample.sp = context.Esp;
+ sample.fp = context.Ebp;
}
// We always sample the VM state.
Modified: branches/bleeding_edge/src/platform.h
==============================================================================
--- branches/bleeding_edge/src/platform.h (original)
+++ branches/bleeding_edge/src/platform.h Tue Feb 17 06:45:45 2009
@@ -422,10 +422,29 @@
// TickSample captures the information collected for each sample.
class TickSample {
public:
- TickSample() : pc(0), sp(0), state(OTHER) {}
+ TickSample() : pc(0), sp(0), fp(0), state(OTHER) {}
unsigned int pc; // Instruction pointer.
unsigned int sp; // Stack pointer.
+ unsigned int fp; // Frame pointer.
StateTag state; // The state of the VM.
+ SmartPointer<Address> stack; // Call stack, null-terminated.
+
+ inline TickSample& operator=(const TickSample& rhs) {
+ if (this == &rhs) return *this;
+ pc = rhs.pc;
+ sp = rhs.sp;
+ fp = rhs.fp;
+ state = rhs.state;
+ DeleteArray(stack.Detach());
+ stack = rhs.stack;
+ return *this;
+ }
+
+ inline void InitStack(int depth) {
+ stack = SmartPointer<Address>(NewArray<Address>(depth + 1));
+ // null-terminate
+ stack[depth] = 0;
+ }
};
class Sampler {
Modified: branches/bleeding_edge/tools/tickprocessor.py
==============================================================================
--- branches/bleeding_edge/tools/tickprocessor.py (original)
+++ branches/bleeding_edge/tools/tickprocessor.py Tue Feb 17 06:45:45 2009
@@ -26,6 +26,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import csv, splaytree, sys
+from operator import itemgetter
class CodeEntry(object):
@@ -34,9 +35,14 @@
self.start_addr = start_addr
self.tick_count = 0
self.name = name
+ self.stacks = {}
- def Tick(self, pc):
+ def Tick(self, pc, stack):
self.tick_count += 1
+ if len(stack) > 0:
+ stack.insert(0, self.ToString())
+ stack_key = tuple(stack)
+ self.stacks[stack_key] = self.stacks.setdefault(stack_key, 0) + 1
def RegionTicks(self):
return None
@@ -69,8 +75,8 @@
self.assembler = assembler
self.region_ticks = None
- def Tick(self, pc):
- super(JSCodeEntry, self).Tick(pc)
+ def Tick(self, pc, stack):
+ super(JSCodeEntry, self).Tick(pc, stack)
if not pc is None:
offset = pc - self.start_addr
seen = []
@@ -162,7 +168,7 @@
logreader = csv.reader(logfile)
for row in logreader:
if row[0] == 'tick':
- self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]))
+ self.ProcessTick(int(row[1], 16), int(row[2], 16), int(row[3]),
row[4:])
elif row[0] == 'code-creation':
self.ProcessCodeCreation(row[1], int(row[2], 16), int(row[3]),
row[4])
elif row[0] == 'code-move':
@@ -237,25 +243,41 @@
def IncludeTick(self, pc, sp, state):
return (self.included_state is None) or (self.included_state == state)
- def ProcessTick(self, pc, sp, state):
- if not self.IncludeTick(pc, sp, state):
- self.excluded_number_of_ticks += 1;
- return
- self.total_number_of_ticks += 1
+ def FindEntry(self, pc):
page = pc >> 12
if page in self.vm_extent:
- entry = self.cpp_entries.FindGreatestsLessThan(pc).value
- if entry.IsSharedLibraryEntry():
- self.number_of_library_ticks += 1
- entry.Tick(None)
- return
+ entry = self.cpp_entries.FindGreatestsLessThan(pc)
+ if entry != None:
+ return entry.value
+ else:
+ return entry
max = self.js_entries.FindMax()
min = self.js_entries.FindMin()
if max != None and pc < max.key and pc > min.key:
- code_obj = self.js_entries.FindGreatestsLessThan(pc).value
- code_obj.Tick(pc)
+ return self.js_entries.FindGreatestsLessThan(pc).value
+ return None
+
+ def ProcessStack(self, stack):
+ result = []
+ for frame in stack:
+ if frame.startswith('0x'):
+ entry = self.FindEntry(int(frame, 16))
+ if entry != None:
+ result.append(entry.ToString())
+ return result
+
+ def ProcessTick(self, pc, sp, state, stack):
+ if not self.IncludeTick(pc, sp, state):
+ self.excluded_number_of_ticks += 1;
return
- self.unaccounted_number_of_ticks += 1
+ self.total_number_of_ticks += 1
+ entry = self.FindEntry(pc)
+ if entry == None:
+ self.unaccounted_number_of_ticks += 1
+ return
+ if entry.IsSharedLibraryEntry():
+ self.number_of_library_ticks += 1
+ entry.Tick(pc, self.ProcessStack(stack))
def PrintResults(self):
print('Statistical profiling result from %s, (%d ticks, %d
unaccounted, %d excluded).' %
@@ -276,6 +298,11 @@
# Print the C++ ticks.
self.PrintHeader('C++')
self.PrintEntries(cpp_entries, lambda e:not e.IsSharedLibraryEntry())
+ # Print call profile.
+ print('\n [Call profile]:')
+ print(' total call path')
+ js_entries.extend(cpp_entries)
+ self.PrintCallProfile(js_entries)
def PrintHeader(self, header_title):
print('\n [%s]:' % header_title)
@@ -308,6 +335,22 @@
'accum' : ticks[0] * 100.0 / entry.tick_count,
'name': name
})
+
+ def PrintCallProfile(self, entries):
+ all_stacks = {}
+ total_stacks = 0
+ for entry in entries:
+ all_stacks.update(entry.stacks)
+ for count in entry.stacks.itervalues():
+ total_stacks += count
+ all_stacks_items = all_stacks.items();
+ all_stacks_items.sort(key = itemgetter(1), reverse=True)
+ for stack, count in all_stacks_items:
+ total_percentage = count * 100.0 / total_stacks
+ print(' %(total)5.1f%% %(call_path)s' % {
+ 'total' : total_percentage,
+ 'call_path' : stack[0] + ' <- ' + stack[1]
+ })
if __name__ == '__main__':
sys.exit('You probably want to run windows-tick-processor.py or
linux-tick-processor.py.')
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---