Initial implementation of x86 stackwalker (#9). r=bryner

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@12 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
mmentovai 2006-09-06 19:28:46 +00:00
parent 3261e8b6ea
commit 213800d30c
9 changed files with 543 additions and 7 deletions

View file

@ -44,12 +44,17 @@ src_libairbag_la_SOURCES = \
src/processor/minidump_format.h \
src/processor/range_map.h \
src/processor/source_line_resolver.cc \
src/processor/source_line_resolver.h
src/processor/source_line_resolver.h \
src/processor/stackwalker.cc \
src/processor/stackwalker.h \
src/processor/stackwalker_x86.cc \
src/processor/stackwalker_x86.h
## Programs
bin_PROGRAMS = \
src/processor/minidump_dump
src/processor/minidump_dump \
src/processor/minidump_stackwalk
## Tests
@ -57,7 +62,8 @@ check_PROGRAMS = \
src/processor/range_map_unittest \
src/processor/source_line_resolver_unittest
check_SCRIPTS = \
src/processor/minidump_dump_test
src/processor/minidump_dump_test \
src/processor/minidump_stackwalk_test
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
@ -79,12 +85,20 @@ src_processor_minidump_dump_SOURCES = \
src_processor_minidump_dump_LDADD = \
src/processor/minidump.lo
src_processor_minidump_stackwalk_SOURCES = \
src/processor/minidump_stackwalk.cc
src_processor_minidump_stackwalk_LDADD = \
src/processor/minidump.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_x86.lo
## Additional files to be included in a source distribution
EXTRA_DIST = \
$(SCRIPTS) \
src/processor/testdata/minidump1.dmp \
src/processor/testdata/minidump1.out \
src/processor/testdata/minidump1.stack.out \
src/processor/testdata/module1.out \
src/processor/testdata/module2.out \
src/processor/testdata/module3_bad.out

View file

@ -53,7 +53,8 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
bin_PROGRAMS = src/processor/minidump_dump$(EXEEXT)
bin_PROGRAMS = src/processor/minidump_dump$(EXEEXT) \
src/processor/minidump_stackwalk$(EXEEXT)
check_PROGRAMS = src/processor/range_map_unittest$(EXEEXT) \
src/processor/source_line_resolver_unittest$(EXEEXT)
noinst_PROGRAMS =
@ -86,7 +87,8 @@ LTLIBRARIES = $(lib_LTLIBRARIES)
src_libairbag_la_LIBADD =
am__dirstamp = $(am__leading_dot)dirstamp
am_src_libairbag_la_OBJECTS = src/processor/minidump.lo \
src/processor/source_line_resolver.lo
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo src/processor/stackwalker_x86.lo
src_libairbag_la_OBJECTS = $(am_src_libairbag_la_OBJECTS)
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
@ -95,6 +97,13 @@ am_src_processor_minidump_dump_OBJECTS = \
src_processor_minidump_dump_OBJECTS = \
$(am_src_processor_minidump_dump_OBJECTS)
src_processor_minidump_dump_DEPENDENCIES = src/processor/minidump.lo
am_src_processor_minidump_stackwalk_OBJECTS = \
src/processor/minidump_stackwalk.$(OBJEXT)
src_processor_minidump_stackwalk_OBJECTS = \
$(am_src_processor_minidump_stackwalk_OBJECTS)
src_processor_minidump_stackwalk_DEPENDENCIES = \
src/processor/minidump.lo src/processor/stackwalker.lo \
src/processor/stackwalker_x86.lo
am_src_processor_range_map_unittest_OBJECTS = \
src/processor/range_map_unittest.$(OBJEXT)
src_processor_range_map_unittest_OBJECTS = \
@ -128,10 +137,12 @@ LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_minidump_dump_SOURCES) \
$(src_processor_minidump_stackwalk_SOURCES) \
$(src_processor_range_map_unittest_SOURCES) \
$(src_processor_source_line_resolver_unittest_SOURCES)
DIST_SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_minidump_dump_SOURCES) \
$(src_processor_minidump_stackwalk_SOURCES) \
$(src_processor_range_map_unittest_SOURCES) \
$(src_processor_source_line_resolver_unittest_SOURCES)
dist_docDATA_INSTALL = $(INSTALL_DATA)
@ -269,10 +280,15 @@ src_libairbag_la_SOURCES = \
src/processor/minidump_format.h \
src/processor/range_map.h \
src/processor/source_line_resolver.cc \
src/processor/source_line_resolver.h
src/processor/source_line_resolver.h \
src/processor/stackwalker.cc \
src/processor/stackwalker.h \
src/processor/stackwalker_x86.cc \
src/processor/stackwalker_x86.h
check_SCRIPTS = \
src/processor/minidump_dump_test
src/processor/minidump_dump_test \
src/processor/minidump_stackwalk_test
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
@ -292,10 +308,19 @@ src_processor_minidump_dump_SOURCES = \
src_processor_minidump_dump_LDADD = \
src/processor/minidump.lo
src_processor_minidump_stackwalk_SOURCES = \
src/processor/minidump_stackwalk.cc
src_processor_minidump_stackwalk_LDADD = \
src/processor/minidump.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_x86.lo
EXTRA_DIST = \
$(SCRIPTS) \
src/processor/testdata/minidump1.dmp \
src/processor/testdata/minidump1.out \
src/processor/testdata/minidump1.stack.out \
src/processor/testdata/module1.out \
src/processor/testdata/module2.out \
src/processor/testdata/module3_bad.out
@ -391,6 +416,10 @@ src/processor/minidump.lo: src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/source_line_resolver.lo: src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/stackwalker.lo: src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/stackwalker_x86.lo: src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/$(am__dirstamp):
@$(mkdir_p) src
@: > src/$(am__dirstamp)
@ -443,6 +472,12 @@ src/processor/minidump_dump.$(OBJEXT): src/processor/$(am__dirstamp) \
src/processor/minidump_dump$(EXEEXT): $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_DEPENDENCIES) src/processor/$(am__dirstamp)
@rm -f src/processor/minidump_dump$(EXEEXT)
$(CXXLINK) $(src_processor_minidump_dump_LDFLAGS) $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_LDADD) $(LIBS)
src/processor/minidump_stackwalk.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/minidump_stackwalk$(EXEEXT): $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_DEPENDENCIES) src/processor/$(am__dirstamp)
@rm -f src/processor/minidump_stackwalk$(EXEEXT)
$(CXXLINK) $(src_processor_minidump_stackwalk_LDFLAGS) $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_LDADD) $(LIBS)
src/processor/range_map_unittest.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
@ -461,19 +496,27 @@ mostlyclean-compile:
-rm -f src/processor/minidump.$(OBJEXT)
-rm -f src/processor/minidump.lo
-rm -f src/processor/minidump_dump.$(OBJEXT)
-rm -f src/processor/minidump_stackwalk.$(OBJEXT)
-rm -f src/processor/range_map_unittest.$(OBJEXT)
-rm -f src/processor/source_line_resolver.$(OBJEXT)
-rm -f src/processor/source_line_resolver.lo
-rm -f src/processor/source_line_resolver_unittest.$(OBJEXT)
-rm -f src/processor/stackwalker.$(OBJEXT)
-rm -f src/processor/stackwalker.lo
-rm -f src/processor/stackwalker_x86.$(OBJEXT)
-rm -f src/processor/stackwalker_x86.lo
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_dump.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_stackwalk.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86.Plo@am__quote@
.cc.o:
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`; \

View file

@ -0,0 +1,118 @@
// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// minidump_stackwalk.cc: Print the stack of the exception thread from a
// minidump.
//
// Author: Mark Mentovai
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#define O_BINARY 0
#else // !_WIN32
#include <io.h>
#define open _open
#endif // !_WIN32
#include <memory>
#include "processor/minidump.h"
#include "processor/stackwalker_x86.h"
using std::auto_ptr;
using namespace google_airbag;
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <file>\n", argv[0]);
exit(1);
}
int fd = open(argv[1], O_RDONLY | O_BINARY);
if (fd == -1) {
fprintf(stderr, "open failed\n");
exit(1);
}
Minidump minidump(fd);
if (!minidump.Read()) {
fprintf(stderr, "minidump.Read() failed\n");
exit(1);
}
MinidumpException* exception = minidump.GetException();
if (!exception) {
fprintf(stderr, "minidump.GetException() failed\n");
exit(1);
}
MinidumpThreadList* thread_list = minidump.GetThreadList();
if (!thread_list) {
fprintf(stderr, "minidump.GetThreadList() failed\n");
exit(1);
}
MinidumpThread* exception_thread =
thread_list->GetThreadByID(exception->GetThreadID());
if (!exception_thread) {
fprintf(stderr, "thread_list->GetThreadByID() failed\n");
exit(1);
}
MemoryRegion* stack_memory = exception_thread->GetMemory();
if (!stack_memory) {
fprintf(stderr, "exception_thread->GetStackMemory() failed\n");
exit(1);
}
MinidumpContext* context = exception->GetContext();
if (!context) {
fprintf(stderr, "exception->GetContext() failed\n");
exit(1);
}
MinidumpModuleList* modules = minidump.GetModuleList();
if (!modules) {
fprintf(stderr, "minidump.GetModuleList() failed\n");
exit(1);
}
StackwalkerX86 stackwalker = StackwalkerX86(context, stack_memory, modules);
auto_ptr<StackFrames> stack(stackwalker.Walk());
if (!stack.get()) {
fprintf(stderr, "stackwalker->Walk() failed\n");
exit(1);
}
unsigned int index;
for (index = 0 ; index < stack->size() ; index++) {
StackFrame frame = stack->at(index);
printf("[%2d] ebp = 0x%08llx eip = 0x%08llx \"%s\" + 0x%08llx\n",
index,
frame.frame_pointer,
frame.instruction,
frame.module_base ? frame.module_name.c_str() : "0x0",
frame.instruction - frame.module_base);
}
return 0;
}

View file

@ -0,0 +1,6 @@
#!/bin/sh
testdata_dir=$srcdir/src/processor/testdata
./src/processor/minidump_stackwalk $testdata_dir/minidump1.dmp | \
tr -s '\015' '\012' | \
diff -u $testdata_dir/minidump1.stack.out -
exit $?

View file

@ -0,0 +1,78 @@
// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// stackwalker.cc: Generic stackwalker.
//
// See stackwalker.h for documentation.
//
// Author: Mark Mentovai
#include <memory>
#include "processor/stackwalker.h"
#include "processor/minidump.h"
namespace google_airbag {
using std::auto_ptr;
Stackwalker::Stackwalker(MemoryRegion* memory, MinidumpModuleList* modules)
: memory_(memory), modules_(modules) {
}
StackFrames* Stackwalker::Walk() {
auto_ptr<StackFrames> frames(new StackFrames());
// Begin with the context frame, and keep getting callers until there are
// no more.
auto_ptr<StackFrame> frame(new StackFrame());
bool valid = GetContextFrame(frame.get());
while (valid) {
// frame already contains a good frame with properly set instruction and
// frame_pointer fields. The frame structure comes from either the
// context frame (above) or a caller frame (below).
// Resolve the module information, if a module map was provided.
if (modules_) {
MinidumpModule* module =
modules_->GetModuleForAddress(frame->instruction);
if (module) {
frame->module_name = *(module->GetName());
frame->module_base = module->base_address();
}
}
// Copy the frame into the frames vector.
frames->push_back(*frame);
// Use a new object for the next frame, even though the old object was
// copied. If StackFrame provided some sort of Clear() method, then
// the same frame could be reused.
frame.reset(new StackFrame());
// Get the next frame.
valid = GetCallerFrame(frame.get());
}
return frames.release();
}
} // namespace google_airbag

View file

@ -0,0 +1,83 @@
// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// stackwalker.cc: Generic stackwalker.
//
// The Stackwalker class is an abstract base class providing common generic
// methods that apply to stacks from all systems. Specific implementations
// will extend this class by providing GetContextFrame and GetCallerFrame
// methods to fill in system-specific data in a StackFrame structure.
// Stackwalker assembles these StackFrame strucutres into a vector of
// StackFrames.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_STACKWALKER_H__
#define PROCESSOR_STACKWALKER_H__
#include "google/stack_frame.h"
#include "processor/memory_region.h"
namespace google_airbag {
class MinidumpModuleList;
class Stackwalker {
public:
virtual ~Stackwalker() {}
// Produces a vector of StackFrames by calling GetContextFrame and
// GetCallerFrame, and populating the returned frames with module
// offset and name information if possible. The caller takes ownership
// of the StackFrames object and is responsible for freeing it.
StackFrames* Walk();
protected:
// memory identifies a MemoryRegion that provides the stack memory
// for the stack to walk. modules, if non-NULL, is a MinidumpModuleList
// that is used to look up which code module each stack frame is
// associated with.
Stackwalker(MemoryRegion* memory, MinidumpModuleList* modules);
// The stack memory to walk. Subclasses will require this region to
// get information from the stack.
MemoryRegion* memory_;
private:
// Obtains the context frame, the innermost called procedure in a stack
// trace. Returns false on failure.
virtual bool GetContextFrame(StackFrame* frame) = 0;
// Obtains a caller frame. Each call to GetCallerFrame should return the
// frame that called the last frame returned by GetContextFrame or
// GetCallerFrame. GetCallerFrame should return false on failure or
// when there are no more caller frames (when the end of the stack has
// been reached).
virtual bool GetCallerFrame(StackFrame* frame) = 0;
// A list of modules, for populating each StackFrame's module information.
// This field is optional and may be NULL.
MinidumpModuleList* modules_;
};
} // namespace google_airbag
#endif // PROCESSOR_STACKWALKER_H__

View file

@ -0,0 +1,103 @@
// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// stackwalker_x86.cc: x86-specific stackwalker.
//
// See stackwalker_x86.h for documentation.
//
// Author: Mark Mentovai
#include "processor/stackwalker_x86.h"
#include "processor/minidump.h"
namespace google_airbag {
StackwalkerX86::StackwalkerX86(MinidumpContext* context,
MemoryRegion* memory,
MinidumpModuleList* modules)
: Stackwalker(memory, modules)
, last_frame_pointer_(0) {
if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
// The x86 is a 32-bit CPU, the limits of the supplied stack are invalid.
// Mark memory_ = NULL, which will cause stackwalking to fail.
memory_ = NULL;
}
// TODO(mmentovai): verify that |context| is x86 when Minidump supports
// other CPU types.
context_ = context->context();
}
bool StackwalkerX86::GetContextFrame(StackFrame* frame) {
if (!context_ || !memory_ || !frame)
return false;
// The frame and instruction pointers are stored directly in registers,
// so pull them straight out of the CPU context structure.
frame->frame_pointer = last_frame_pointer_ = context_->ebp;
frame->instruction = context_->eip;
return true;
}
bool StackwalkerX86::GetCallerFrame(StackFrame* frame) {
if (!memory_ || !frame)
return false;
// The frame and instruction pointers for previous frames are saved on the
// stack. The typical x86 calling convention, when frame pointers are
// present, is for the calling procedure to use CALL, which pushes the
// return address onto the stack and sets the instruction pointer (%eip)
// to the entry point of the called routine. The called routine's then
// PUSHes the calling routine's frame pointer (%ebp) onto the stack before
// copying the stack pointer (%esp) to the frame pointer (%ebp). Therefore,
// the calling procedure's frame pointer is always available by
// dereferencing the called procedure's frame pointer, and the return
// address is always available at the memory location immediately above
// the address pointed to by the called procedure's frame pointer.
// If there is no frame pointer, determining the layout of the stack is
// considerably more difficult, requiring debugging information. This
// stackwalker doesn't attempt to solve that problem (at this point).
// Don't pass frame.frame_pointer or frame.instruction directly
// ReadMemory, because their types are too wide (64-bit), and we
// specifically want to read 32-bit quantities for both.
u_int32_t frame_pointer;
if (!memory_->GetMemoryAtAddress(last_frame_pointer_, &frame_pointer))
return false;
// A caller frame must reside higher in memory than its callee frames.
// Anything else is an error, or an indication that we've reached the
// end of the stack.
if (frame_pointer <= last_frame_pointer_)
return false;
u_int32_t instruction;
if (!memory_->GetMemoryAtAddress(last_frame_pointer_ + 4, &instruction))
return false;
frame->frame_pointer = last_frame_pointer_ = frame_pointer;
frame->instruction = instruction;
return true;
}
} // namespace google_airbag

View file

@ -0,0 +1,68 @@
// Copyright (C) 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// stackwalker_x86.h: x86-specific stackwalker.
//
// Provides stack frames given x86 register context and a memory region
// corresponding to an x86 stack.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_STACKWALKER_X86_H__
#define PROCESSOR_STACKWALKER_X86_H__
#include "google/airbag_types.h"
#include "processor/stackwalker.h"
#include "processor/minidump_format.h"
namespace google_airbag {
class MinidumpContext;
class MinidumpModuleList;
class StackwalkerX86 : public Stackwalker {
public:
// context is a MinidumpContext object that gives access to x86-specific
// register state corresponding to the innermost called frame to be
// included in the stack. memory and modules are passed directly through
// to the base Stackwalker constructor.
StackwalkerX86(MinidumpContext* context,
MemoryRegion* memory,
MinidumpModuleList* modules);
private:
// Implementation of Stackwalker, using x86 context (%ebp, %eip) and
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp]).
bool GetContextFrame(StackFrame* frame);
bool GetCallerFrame(StackFrame* frame);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.
const MDRawContextX86* context_;
// Stores the frame pointer returned in the last stack frame returned by
// GetContextFrame or GetCallerFrame.
u_int32_t last_frame_pointer_;
};
} // namespace google_airbag
#endif // PROCESSOR_STACKWALKER_X86_H__

View file

@ -0,0 +1,23 @@
[ 0] ebp = 0x0012ecb8 eip = 0x020a1515 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00221515
[ 1] ebp = 0x0012ecd8 eip = 0x020a03e3 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x002203e3
[ 2] ebp = 0x0012ecf0 eip = 0x023c8a28 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00548a28
[ 3] ebp = 0x0012ed30 eip = 0x023ccfd9 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x0054cfd9
[ 4] ebp = 0x0012ed64 eip = 0x0222fd12 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003afd12
[ 5] ebp = 0x0012ed94 eip = 0x022311dd "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003b11dd
[ 6] ebp = 0x0012edc8 eip = 0x034eb0f1 "c:\lizard\trunk\mozilla\dist\bin\components\xpc3250.dll" + 0x0005b0f1
[ 7] ebp = 0x0012eeb0 eip = 0x0049bda4 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0007bda4
[ 8] ebp = 0x0012f834 eip = 0x0047b92f "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0005b92f
[ 9] ebp = 0x0012f93c eip = 0x0046c945 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0004c945
[10] ebp = 0x0012f9c8 eip = 0x0046d345 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0004d345
[11] ebp = 0x0012f9f0 eip = 0x00430ec3 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x00010ec3
[12] ebp = 0x0012fa4c eip = 0x02213b7f "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00393b7f
[13] ebp = 0x0012fb60 eip = 0x02249ced "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003c9ced
[14] ebp = 0x0012fb70 eip = 0x0224a810 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003ca810
[15] ebp = 0x0012fbbc eip = 0x002ebff8 "c:\lizard\trunk\mozilla\dist\bin\xpcom_core.dll" + 0x0007bff8
[16] ebp = 0x0012fbe4 eip = 0x002ec8ec "c:\lizard\trunk\mozilla\dist\bin\xpcom_core.dll" + 0x0007c8ec
[17] ebp = 0x0012fc48 eip = 0x029193b5 "c:\lizard\trunk\mozilla\dist\bin\components\gkwidget.dll" + 0x000293b5
[18] ebp = 0x0012fc5c eip = 0x03174b19 "c:\lizard\trunk\mozilla\dist\bin\components\tkitcmps.dll" + 0x00004b19
[19] ebp = 0x0012ff54 eip = 0x10008e60 "c:\lizard\trunk\mozilla\dist\bin\xul.dll" + 0x00008e60
[20] ebp = 0x0012ff68 eip = 0x00401036 "c:\lizard\trunk\mozilla\dist\bin\firefox.exe" + 0x00001036
[21] ebp = 0x0012ffc0 eip = 0x004011bc "c:\lizard\trunk\mozilla\dist\bin\firefox.exe" + 0x000011bc
[22] ebp = 0x0012fff0 eip = 0x7c816d4f "C:\WINDOWS\system32\kernel32.dll" + 0x00016d4f