javascript调试原理

我们先看一下C/C++和java的调试原理

C/C++调试原理:目前比较流行的调试工具是 GDB 和微软的 Visual Studio 自带的 debugger,在这种 debugger 中,首先,需要编译一个“ debug ”模式的程序,将调试语句编译到该程序中。其次,在调试过程中,debugger 将会深层接入程序的运行,掌握和控制运行态的一些信息,并将这些信息及时返回。

Java调试原理:Java的调试使用Java 虚拟机工具接口(Java Virtual Machine Tool Interface,JVMTI)提供的调试接口。包括进入一个函数,进入一个语句的事件,得到当前上下文的API等等,你只要注册了这些事件,调用这些API即可。

从上面可以看出,调试的方式主要有两种,一种是自己写编辑,在目标文件中加入调试代码,由调试代码来做调试工作,另一种是运行该语言的容器本身提供了调试接口。

我们再来看javascript的调试。

目前的javascript调试工具也是用的第二种调试方式,IE和firefox分别提供了javascript的调试接口。

以firefox为例,它提供的调试接口是:jsdIDebuggerService,我们可以向它注入一些调试的钩子,来实现调试,它提供的调试钩子主要有:

钩子类说明
breakpointHookCalled when the engine encounters a breakpoint.
debuggerHookCalled when the engine encounters the debugger keyword.
debugHookCalled when the errorHook returns false.
errorHookCalled when an error or warning occurs.
functionHookCalled before and after a function is called.
interruptHookCalled before the next PC is executed.
scriptHookCalled when a jsdIScript is created or destroyed.
throwHookCalled when an exception is thrown (even if it will be caught.)
topLevelHookCalled before and after a toplevel script is evaluated.

包括firebug,venkman,ATF,aptana等调试工具都是实现这些钩子类来做调试的,譬如要做错误定位,就要实现errorHook,errorHook有一个方法

onError(message, fileName, lineNo, pos, flags, errnum, exc)就给出了错误信息,错误文件,行号,位置,等信息。只要向jsdIDebuggerService上注册errorHook,firefox发生js错误时就会调用该方法。

这种调试技术目前已经成熟,但是却都是只能在单个浏览器上调试,我们要在其它浏览器上调试还是比较困难的。

开源项目Javascript Debug Toolkit用另一种原理做了跨浏览器调试javascript的工具,和C/C++的调试一样,它先把javascript编译为带调试代码的目标文件,在浏览器中运行,只要这些调试代码能够做到跨浏览器,整个调试工具就能跨浏览器了。

我们看一下Javascript Debug Toolkit是如何工作的。来看一个例子。

源代码

function test(a,b){

    var c = a + b;

    return c;

}

目标调试代码

jsdebug("test.js",1);function test(a,b){

jsdebug("test.js",2);    var c = a + b;

jsdebug("test.js",3);    return c;

jsdebug("test.js",4);}

这样在每一行代码执行前都会调用jsdebug函数,该函数可以向调试服务器发调试通知,可以把当前的context传给调试服务器,调试服务器与调试代码进行交互(通过ajax技术),包括单步执行,跟进,跟出等都会可以实现。

这种技术的最大好处就是跨浏览器,目前已经能够在ie,firefox,safari,chrome,opera以及一些掌机浏览器上调试javascript。相信这种技术也会很快被使用起来,会有更多跨浏览器的javascript调试工具出现。

 

 

javascript调试原理(一)中讲了javascript的调试原理,本单给出一个javascript调试的客户端模拟实现: 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  
<HTML>  
<HEAD>  
<TITLE> New Document </TITLE>  
<META NAME="Generator" CONTENT="EditPlus">  
<META NAME="Author" CONTENT="">  
<META NAME="Keywords" CONTENT="">  
<META NAME="Description" CONTENT="">  
</HEAD>  
  
<BODY>  
<textarea id="jsstr" readonly style="width:400px;height:200px">  
function test(){  
    var a = test1();  
    var b = test2();  
    var c = "result is " + a + b;  
    alert(c);  
}  
function test1(){  
    return "test1-->abc";  
}  
function test2(){  
    return "test2-->abc";  
}  
test();  
</textarea>  
<div id="result">  
      
</div>  
<script>  
var debugStr = "";  
function jsdebug(resource,line,evalFunc){  
    var lines = debugStr.split("\n");  
    var jsLine = lines[line-1];  
    lines[line-1] = "当前行:--->" + jsLine;  
    var arr = [  
        "调试代码:",  
        lines.join("\n")  
    ];  
    var result = document.getElementById("result");  
    result.innerHTML = result.innerHTML + "<br>执行到第" + line + "行: -->" + jsLine;  
    alert(arr.join("\n"));  
  
}  
function debug(str){  
    var lines = str.split("\n");  
    debugStr = str;  
    var codes = [];  
    for(var i=0;i<lines.length;i++){  
        codes[i] = "jsdebug('test'," + (i+1) + ",function(text){return eval(text)});" + lines[i];  
    }  
    eval(codes.join('\n'));  
}  
debug(document.getElementById('jsstr').value);  
</script>  
</BODY>  
</HTML>  

 

 

 

只要把其中的alert换成同步的ajax请求,一可以实现每句执行时把当前的行号,context等 信息传给调试服务器。 

 

Javascript Debug Toolkit的客户端原理是在这个基础上的,下一篇将展开讲一下这种原理会遇到的问题及解决方法。

 

 

javascript调试原理(二)中给出一个模拟客户端调试的例子,在客户端有两个问题: 

1.如何获得当前的context? 

2.如何做resume,stepinto,stepreturn,stepover? 

本章围绕着这两个问题展开讨论 

1.如何获得当前的context 

我们先看一段代码: 

function test(){  
    this.a = "a";  
    var b = "b";  
}  

 

 

那么在进入test之后,如何获得a和b的值呢? 

a的值比较简单,只要把this传过去,通过for...in语句就可以获得,但是b呢?它相当于一个私有变量,在外面是不能访问的,要访问b只能通过在b所在的作用域中获得,因此我们在插入前面的每一行中要加上一个eval函数,eval函数的作用域是当前行的,所以可以获得当前行的上下文。所以在第二章中才会在每一行加上 

function(text){try{return eval(text)}catch(e){}});  

 

 

我们把a,b,c加上看一下效果 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  
<HTML>  
<HEAD>  
<TITLE> New Document </TITLE>  
<META NAME="Generator" CONTENT="EditPlus">  
<META NAME="Author" CONTENT="">  
<META NAME="Keywords" CONTENT="">  
<META NAME="Description" CONTENT="">  
</HEAD>  
  
<BODY>  
<textarea id="jsstr" readonly style="width:400px;height:200px">function test(){  
    var a = test1();  
    var b = test2();  
    var c = "result is " + a + b;  
    alert(c);  
}  
function test1(){  
    return "test1-->abc";  
}  
function test2(){  
    return "test2-->abc";  
}  
test();</textarea>  
<div id="result">  
      
</div>  
<script>  
var debugStr = "";  
function jsdebug(resource,line,evalFunc){  
    var lines = debugStr.split("\n");  
    var jsLine = lines[line-1];  
    lines[line-1] = "当前行:--->" + jsLine;  
    var arr = [  
        "调试代码:",  
        lines.join("\n"),  
        "==========context===========",  
        "a = " + evalFunc("a"),  
        "b = " + evalFunc("b"),  
        "c = " + evalFunc("c"),  
    ];  
    var result = document.getElementById("result");  
    result.innerHTML = result.innerHTML + "<br>执行到第" + line + "行: -->" + jsLine;  
    alert(arr.join("\n"));  
  
}  
function debug(str){  
    var lines = str.split("\n");  
    debugStr = str;  
    var codes = [];  
    for(var i=0;i<lines.length;i++){  
        codes[i] = "jsdebug('test'," + (i+1) + ",function(text){try{return eval(text)}catch(e){}});" + lines[i];  
    }  
    eval(codes.join('\n'));  
}  
debug(document.getElementById('jsstr').value);  
</script>  
</BODY>  
</HTML>  

 

 

在执行到第3,4,5行的时候可以看到a,b,c的值 

这里又有一个问题:我怎么知道函数中有哪些变量? 

这就要用到arguments.callee.caller了,把函数当成一个字条串解析,解析出函数的输入参数及定义的变量,就能拿到一个变量名的集合,然后再一个一个地取值。具体的算法这里就不给出了。 

 

再说第二个问题,如何控制stepinto,stepreturn,resume 

我们需要在客户端维护一个函数的调用栈,这个栈中记录着每一步的调用,在执行的时候根据栈中存的信息和当前的上下文比较,就能拿到当前语句究竟是stepover还是stepinto还是stepreturn。 

 

那么如何做resume呢,客户端维护一份断点列表,执行每一句的时候判断是否到了断点,不在断点就继续执行,到了断点就停止。 

 

这部分内容也不再给出详细代码,请参照Javascript Debug Toolkit的源代码

 

 

开源项目--跨浏览器的javascript调试工具Javascript Debug Toolkit(jsdt) 

http://code.google.com/p/jsdt/ 

jsdt是一个eclipse的插件,要求在eclipse3.2,jre1.5以上运行,可以进行跨浏览器的javascript调试。 

jsdt在eclipse中设置断点,在浏览器中运行,执行到断点时自行中断到调试器 

jsdt可以单步调试,可以查看当前函数的context 

jsdt可跨浏览器调试,目前的浏览器ie,firefox,safari,chrome都支持,其中,因为chrome的bug,css和image不能正常显示 

调试的效果图如下: 

 

 

 

设置断点 

 

 

 

中断 

 

stepOver 

 

stepInto 

 

stepReturn 

 

 

jsdt包含以下几个部分 

1.js引擎,基于rhino引擎,解析js,确定哪一行可以加断点,哪一行不能加断点 

2.jsdt server,是一个http服务器,接管客户端的所有请求 

3.jsdt client,客户端引擎 

4.jsdt debug implement,实现了一套eclipse插件的接口和ui,譬如设置断点等。 

 

jsdt的基本原理如下: 

1.jsdt提供一个http代理,把要访问的文件或url的地址转换成jsdt的server地址 

2.jsdt的server在送给浏览器的每一行javascript代码前加上调试的代码,该调试代码发送ajax请求到jsdt的server,将当前的上下文送给jsdt 

3.jsdt根据上下文在eclipse中中断,显示上下文等。 

4.jsdt根据用户的指令(resume,stepinto,stepover,stepreturn)等回答ajax请求 

5.jsdt的客户端引擎根据server发的指令进行下一步操作

 

  • 大小: 41.3 KB
  • 大小: 43.8 KB
  • 大小: 46.7 KB
  • 大小: 47.2 KB
  • 大小: 46.5 KB

 

 

原文转载自:http://www.iteye.com/topic/299025

 

转载于:https://www.cnblogs.com/mjian/p/9250086.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值