diff options
Diffstat (limited to 'files/zh-cn/spidermonkey/hacking_tips/index.html')
-rw-r--r-- | files/zh-cn/spidermonkey/hacking_tips/index.html | 159 |
1 files changed, 0 insertions, 159 deletions
diff --git a/files/zh-cn/spidermonkey/hacking_tips/index.html b/files/zh-cn/spidermonkey/hacking_tips/index.html deleted file mode 100644 index ab5a5d0e59..0000000000 --- a/files/zh-cn/spidermonkey/hacking_tips/index.html +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Hacking Tips -slug: SpiderMonkey/Hacking_Tips -translation_of: Mozilla/Projects/SpiderMonkey/Hacking_Tips ---- -<p>当你需要深入SpiderMonkey去分析或调试bug时,这里汇总的一些小技巧对你或许会有帮助。本文中所有技巧使用的SpiderMonkey都是按照 <a href="/en-US/docs/SpiderMonkey" title="/en-US/docs/SpiderMonkey">SpiderMonkey构建文档(build documentation of SpiderMonkey)</a> 里面的过程编译的。本文分成两个部分,第一部分介绍调试技巧,第二部分介绍如何构造一个优化。</p> -<h2 id="调试技巧">调试技巧</h2> -<h3 id="打印帮助信息(JS_shell中)">打印帮助信息(JS shell中)</h3> -<p>通过执行<strong>help</strong>函数可以打印出所有内置工具函数名以及各自的简单介绍.</p> -<h3 id="获取函数的字节码(JS_shell中)">获取函数的字节码(JS shell中)</h3> -<p>shell中有一个名为<strong>dis</strong>的函数,可以导出一个函数的字节码.如果不指定参数,它会打印出当前调用函数的字节码.</p> -<pre class="eval"><code>js> function f () { - return 1; -} -js> <strong>dis(</strong>f<strong>)</strong>; -flags: -loc op ------ -- -main: -00000: one -00001: return -00002: stop - -Source notes: - ofs line pc delta desc args ----- ---- ----- ------ -------- ------ - 0: 1 0 [ 0] newline - 1: 2 0 [ 0] colspan 2 - 3: 2 2 [ 2] colspan 9 - -</code></pre> -<h3 id="获取函数的字节码(gdb)">获取函数的字节码(gdb)</h3> -<p>In <em>jsopcode.cpp</em>, a function named <strong>js_DisassembleAtPC</strong> can print the bytecode of a script. Some variants of this function such as <strong>js_DumpPc</strong>, <strong>js_DumpScript</strong> and <strong>js_DumpScriptDepth</strong> are convenient for debugging.</p> -<h3 id="利用GDB输出JS调用栈">利用GDB输出JS调用栈</h3> -<h3 id="Printing_the_JS_stack._(from_gdb)">Printing the JS stack. (from gdb)</h3> -<p>In <em>jsobj.cpp</em>, a function named <strong>js_DumpBacktrace</strong> print a backtrace à la gdb for the JS stack. The backtrace contains in the following order, the stack depth, the interpreter frame pointer (see <em>js/src/vm/Stack.h</em>, <strong>StackFrame</strong> class) or (nil) if compiled with IonMonkey, the file and line number of the call location and under parentheses, the <strong>JSScript</strong> pointer and the <strong>jsbytecode</strong> pointer (pc) executed.</p> -<pre class="eval"><code>$ gdb --args js -[…] -(gdb) b js_ReportOverRecursed -(gdb) r -js> function f(i) { - if (i % 2) f(i + 1); - else f(i + 3); -} -js> f(0) - -Breakpoint 1, js_ReportOverRecursed (maybecx=0xfdca70) at /home/nicolas/mozilla/ionmonkey/js/src/jscntxt.cpp:495 -495 if (maybecx) -(gdb) call <strong>js_DumpBacktrace(</strong>maybecx<strong>)</strong> -#0 (nil) typein:2 (0x7fffef1231c0 @ 0) -#1 (nil) typein:2 (0x7fffef1231c0 @ 24) -#2 (nil) typein:3 (0x7fffef1231c0 @ 47) -#3 (nil) typein:2 (0x7fffef1231c0 @ 24) -#4 (nil) typein:3 (0x7fffef1231c0 @ 47) -[…] -#25157 0x7fffefbbc250 typein:2 (0x7fffef1231c0 @ 24) -#25158 0x7fffefbbc1c8 typein:3 (0x7fffef1231c0 @ 47) -#25159 0x7fffefbbc140 typein:2 (0x7fffef1231c0 @ 24) -#25160 0x7fffefbbc0b8 typein:3 (0x7fffef1231c0 @ 47) -#25161 0x7fffefbbc030 typein:5 (0x7fffef123280 @ 9) - -</code></pre> -<h3 id="利用GDB为执行代码生成断点(x86x86_64平台)">利用GDB为执行代码生成断点(x86/x86_64平台)</h3> -<h3 id="Setting_a_breakpoint_in_the_generated_code._(from_gdb_x86_x86-64)">Setting a breakpoint in the generated code. (from gdb, x86 / x86-64)</h3> -<p>To set a breakpoint the generated code of a specific JSScript compiled with IonMonkey (this will also work with JäegerMonkey, except that functions would be different). Set a breakpoint on the instruction you are interested in. If you have no precise idea which function you are looking at, you can set a breakpoint on the <strong>js::ion::CodeGenerator::visitStart</strong> function. Optionally, a condition on the <strong>ins->id()</strong> of the LIR instruction can be added to select precisely the instruction you are looking for. Once the breakpoint is on <strong>CodeGenerator</strong> function of the LIR instruction, add a command to generate a static breakpoint in the generated code.</p> -<pre class="eval"><code>$ gdb --args js -[…] -(gdb) b js::ion::CodeGenerator::visitStart -(gdb) command ->call masm.breakpoint() ->continue ->end -(gdb) r -js> function f(a, b) { return a + b; } -js> for (var i = 0; i < 100000; i++) f(i, i + 1); - -Breakpoint 1, js::ion::CodeGenerator::visitStart (this=0x101ed20, lir=0x10234e0) - at /home/nicolas/mozilla/ionmonkey/js/src/ion/CodeGenerator.cpp:609 -609 } - -Program received signal SIGTRAP, Trace/breakpoint trap. -0x00007ffff7fb165a in ?? () -(gdb) - -</code></pre> -<p>Once you hit the generated breakpoint, you can replace it by a gdb breakpoint to make it conditional, the procedure is to first replace the generated breakpoint by a nop instruction, and to set a breakpoint at the address of the nop.</p> -<pre class="eval"><code>(gdb) x /5i $pc - 1 - 0x7ffff7fb1659: int3 -=> 0x7ffff7fb165a: mov 0x28(%rsp),%rax - 0x7ffff7fb165f: mov %eax,%ecx - 0x7ffff7fb1661: mov 0x30(%rsp),%rdx - 0x7ffff7fb1666: mov %edx,%ebx - -(gdb) # replace the int3 by a nop -(gdb) set *(unsigned char *) ($pc - 1) = 0x90 -(gdb) x /1i $pc - 1 - 0x7ffff7fb1659: nop - -(gdb) # set a breakpoint at the previous location -(gdb) b *0x7ffff7fb1659 -Breakpoint 2 at 0x7ffff7fb1659 -</code> -</pre> -<h3 id="Finding_the_script_of_Ion_generated_assembly_(from_gdb)">Finding the script of Ion generated assembly (from gdb)</h3> -<p>When facing a bug in which you are in the middle of IonMonkey generated code, first thing to note, is that gdb's backtrace is not reliable, because the generated code does not keep a frame pointer. To figure it out you have to read the stack to infer the IonMonkey frame.</p> -<pre><code>(gdb) </code>x /64a $sp -[…] -0x7fffffff9838: 0x7ffff7fad2da 0x141 -0x7fffffff9848: 0x7fffef134d40 0x2 -[…] -(gdb) p (*(JSFunction**) 0x7fffffff9848)->u.i.script_->lineno -$1 = 1 -(gdb) p (*(JSFunction**) 0x7fffffff9848)->u.i.script_->filename -$2 = 0xff92d1 "typein" -</pre> -<p>The stack is order as defined in js/src/ion/IonFrames-x86-shared.h, it is composed of the return address, a descriptor (a small value), the JSFunction (if it is even) or a JSScript (if the it is odd, remove it to dereference the pointer) and the frame ends with the number of actual arguments (a small value too). If you want to know at which LIR the code is failing at, the <strong>js::ion::CodeGenerator::generateBody</strong> function can be intrumented to dump the LIR <strong>id</strong> before each instruction.</p> -<pre><code>for (; iter != current->end(); iter++) { - IonSpew(IonSpew_Codegen, "instruction %s", iter->opName()); - […] - - masm.store16(Imm32(iter->id(), Address(StackPointer, -8))); // added - if (!iter->accept(this)) - return false; -</code> -<code> […] -</code><code>}</code></pre> -<p><code>This modification will add an instruction which abuse the stack pointer </code>to store an immediate value (the LIR id) to a location which would never be generated by any sane compiler. Thus when dumping the assembly under gdb, this kind of instructions would be easily noticeable.</p> -<h3 id="Break_on_valgrind_errors">Break on valgrind errors</h3> -<p>Sometimes, a bug can be reproduced under valgrind but hardly under gdb. One way to investigate is to let valgrind start gdb for you, the other way documented here is to let valgrind act as a gdb server which can be manipulated from the gdb remote.</p> -<pre><code>$ valgrind --smc-check=all-non-file --vgdb-error=0 ./js …</code></pre> -<p>This command will tell you how to start gdb as a remote. Be aware that functions which are usually dumping some output will do it in the shell where valgrind is started and not in the shell where gdb is started. Thus functions such as <strong>js_DumpBacktrace</strong>, when called from gdb, will print their output in the shell containing valgrind.</p> -<h2 id="Hacking_tips">Hacking tips</h2> -<h3 id="Using_the_Gecko_Profiler_(browser_xpcshell)">Using the Gecko Profiler (browser / xpcshell)</h3> -<p>see the section dedicated to <a href="/en-US/docs/Performance/Profiling_with_the_Built-in_Profiler" title="/en-US/docs/Performance/Profiling_with_the_Built-in_Profiler">profiling with the gecko profiler</a>. This method of profiling has the advantage of mixing the JavaScript stack with the C++ stack, which is useful to analyze library function issues. One tip is to start looking at a script with an inverted JS stack to locate the most expensive JS function, then to focus on the frame of this JS function, and to remove the inverted stack and look at C++ part of this function to determine from where the cost is coming from.</p> -<h3 id="Using_the_JIT_Inspector_(browser)">Using the JIT Inspector (browser)</h3> -<p>Install the <a href="https://addons.mozilla.org/en-US/firefox/addon/jit-inspector/" title="https://addons.mozilla.org/en-US/firefox/addon/jit-inspector/">JIT Inspector</a> addon in your browser. This addon provides estimated cost of IonMonkey , JaëgerMonkey and the interpreter. In addition to provides a clean way to analyze if instructions are infered as being monomorphic or polymorphic in addition to the number of time each category of type has been observed.</p> -<h3 id="Using_callgrind_(JS_shell)">Using callgrind (JS shell)</h3> -<p>As SpiderMonkey just-in-time compiler are rewriting the executed program, valgrind should be informed from the command line by adding <strong>--smc-check=all-non-file</strong>.</p> -<pre class="eval"><code>$ valgrind --tool=callgrind --callgrind-out-file=bench.clg --smc-check=all-non-file ./js ./run.js -</code></pre> -<p>The output file can then be use with <strong>kcachegrind</strong> which provides a graphical view of the call graph.</p> -<h3 id="Using_IonMonkey_spew_(JS_shell)">Using IonMonkey spew (JS shell)</h3> -<p>IonMonkey spew is extremely verbose (not as much as the INFER spew), but you can filter it to focus on the list of compiled scripts or channels, IonMonkey spew channels can be selected with the IONFLAGS environment variable, and compilation spew can be filtered with IONFILTER.</p> -<p>IONFLAGS contains the names of each channel separated by commas. The <strong>logs</strong> channel produces 2 files in <em>/tmp/</em>, one (<em>/tmp/ion.json</em>) made to be used with <a class="external text" href="https://github.com/sstangl/iongraph" rel="nofollow">iongraph</a> (made by Sean Stangl) and another one (<em>/tmp/ion.cfg</em>) made to be used with <a class="external text" href="http://java.net/projects/c1visualizer/" rel="nofollow">c1visualizer</a>. These tools will show the MIR & LIR steps done by IonMonkey during the compilation.</p> -<p>Compilation logs and spew can be filtered with the IONFILTER environment variable which contains locations as output in other spew channels. Multiple locations can be separated with comma as a separator of locations.</p> -<pre class="eval"><code>$ IONFILTER=pdfjs.js:16934 IONFLAGS=logs,scripts,osi,bailouts ./js ./run.js 2>&1 | less -</code></pre> -<p>The <strong>bailouts</strong> channel is likely to be the first thing you should focus on, because this means that something does not stay in IonMonkey and fallback to the interpreter. This channel output locations (as returned by the<strong> id()</strong> function of both instructions) of the lastest MIR and the lastest LIR phases. These locations should correspond to phases of the <strong>logs</strong> and a filter can be used to remove uninteresting functions.</p> -<h3 id="Hack_Replacing_one_instruction.">[Hack] Replacing one instruction.</h3> -<p>To replace one specific instruction, you can use in visit function of each instruction the JSScript <strong>filename</strong> in <strong>lineno</strong> fields as well as the <strong>id()</strong> of the LIR / MIR instructions. The JSScript can be obtained from <strong>info().script()</strong>.</p> -<pre class="eval"><code>bool -CodeGeneratorX86Shared::visitGuardShape(LGuardShape *guard) -{ - if (info().script()->lineno == 16934 && guard->id() == 522) { - [… another impl only for this one …] - return true; - } - [… old impl …] -</code></pre> |