JPDA(一):使用JDI写一个调试器

Debugging规范

简单来说,Java Platform Debugger Architecture(JPDA)就是Java提供的一套用于开发Java调试工具的规范,任何的JDK实现都需要实现这个规范。JPDA是一个Architecture,它包括了三个不同层次的规范,如下图,

                 /    |--------------|
                /     |     VM       |
    debuggee - (      |--------------|  <------- JVMTI - Java VM Tool Interface
                \     |   back-end   |
                 \    |--------------|
                 /           |
 comm channel - (            |  <--------------- JDWP - Java Debug Wire Protocol
                 \           |
                 /    |--------------|
                /     | front-end    |
    debugger - (      |--------------|  <------- JDI - Java Debug Interface
                \     |      UI      |
                 \    |--------------|

具体的就不再赘述,可参考下面的参考资料。

JDK&SA的JNI实现

假设我们要开发一个调试工具,那我们只需要使用front-end的JDI的API就可以完成。JDI的API在com.sun.jdi包下,相当于是JDI的接口规范了。除了JDK自带的实现外,我在HotSpot的SA中也发现了一个实现。他俩的实现分别是在com.sun.tools.jdi包下和sun.jvm.hotspot.jdi包下。

我们来稍微看下在代码结构上这两个实现的差异,diff -u

[email protected]:~/Projects/jdk$ tree src/share/classes/com/sun/tools/jdi/ > ~/jdi_jdk
[email protected]:~/Projects/hotspot/agent$ tree src/share/classes/sun/jvm/hotspot/jdi/ > ~/jdi_sa
[email protected]:~$ diff -u jdi_jdk jdi_sa
--- jdi_jdk	2015-01-31 01:29:49.995433328 +0800
+++ jdi_sa	2015-01-31 01:32:21.231773459 +0800
@@ -1,5 +1,4 @@
-src/share/classes/com/sun/tools/jdi/
-├── AbstractLauncher.java
+src/share/classes/sun/jvm/hotspot/jdi
 ├── ArrayReferenceImpl.java
 ├── ArrayTypeImpl.java
 ├── BaseLineInfo.java
@@ -12,80 +11,51 @@
 ├── ClassLoaderReferenceImpl.java
 ├── ClassObjectReferenceImpl.java
 ├── ClassTypeImpl.java
-├── CommandSender.java
 ├── ConcreteMethodImpl.java
 ├── ConnectorImpl.java
 ├── DoubleTypeImpl.java
 ├── DoubleValueImpl.java
-├── EventQueueImpl.java
-├── EventRequestManagerImpl.java
-├── EventSetImpl.java
 ├── FieldImpl.java
 ├── FloatTypeImpl.java
 ├── FloatValueImpl.java
-├── GenericAttachingConnector.java
-├── GenericListeningConnector.java
 ├── IntegerTypeImpl.java
 ├── IntegerValueImpl.java
 ├── InterfaceTypeImpl.java
-├── InternalEventHandler.java
-├── JDWPException.java
 ├── JNITypeParser.java
+├── JVMTIThreadState.java
 ├── LineInfo.java
-├── LinkedHashMap.java
 ├── LocalVariableImpl.java
 ├── LocationImpl.java
-├── LockObject.java
 ├── LongTypeImpl.java
 ├── LongValueImpl.java
-├── META-INF
-│   └── services
-│       ├── com.sun.jdi.connect.Connector
-│       └── com.sun.jdi.connect.spi.TransportService
 ├── MethodImpl.java
 ├── MirrorImpl.java
 ├── MonitorInfoImpl.java
 ├── NonConcreteMethodImpl.java
 ├── ObjectReferenceImpl.java
-├── ObsoleteMethodImpl.java
-├── Packet.java
-├── PacketStream.java
 ├── PrimitiveTypeImpl.java
 ├── PrimitiveValueImpl.java
-├── ProcessAttachingConnector.java
-├── RawCommandLineLauncher.java
 ├── ReferenceTypeImpl.java
-├── resources
-│   ├── jdi_ja.properties
-│   ├── jdi.properties
-│   └── jdi_zh_CN.properties
+├── SACoreAttachingConnector.java
+├── SADebugServerAttachingConnector.java
+├── SADebugServer.java
+├── SAJDIClassLoader.java
+├── SAPIDAttachingConnector.java
 ├── SDE.java
 ├── ShortTypeImpl.java
 ├── ShortValueImpl.java
-├── SocketAttachingConnector.java
-├── SocketListeningConnector.java
-├── SocketTransportService.java
 ├── StackFrameImpl.java
 ├── StratumLineInfo.java
 ├── StringReferenceImpl.java
-├── SunCommandLineLauncher.java
-├── TargetVM.java
-├── ThreadAction.java
 ├── ThreadGroupReferenceImpl.java
-├── ThreadListener.java
 ├── ThreadReferenceImpl.java
 ├── TypeComponentImpl.java
 ├── TypeImpl.java
 ├── ValueContainer.java
 ├── ValueImpl.java
 ├── VirtualMachineImpl.java
-├── VirtualMachineManagerImpl.java
-├── VirtualMachineManagerService.java
-├── VMAction.java
-├── VMListener.java
 ├── VMModifiers.java
-├── VMState.java
 ├── VoidTypeImpl.java
 └── VoidValueImpl.java

-3 directories, 85 files
+0 directories, 58 files

JDI实例

下面直接通过一个例子来对JDI有一个更直观的认识。写一个简单的调试器,对目标Java进程打断点,并且输出一个目标进程的变量(这其实就已经是我们平常调试程序要做的事情了不是吗^_^)。

目标进程(debugee)的代码,我们调试的目标就是输出满足条件的变量i,

package me.kisimple.just4fun;

import java.util.Random;

/**
 * Created by blues on 31/01/15.
 */
public class Main {

    public static void main(String[] args) throws Exception{
        Random random = new Random();
        while(true) {
            int i = random.nextInt(1000);
            if(i % 10 == 0) {
//                System.out.println(i);
                doNothing();
                Thread.sleep(5000);
            }
        }
    }

    private static void doNothing() {

    }

}

调试器(debuger)的代码,

package me.kisimple.just4fun;

import com.sun.jdi.*;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.event.*;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.tools.jdi.SocketAttachingConnector;
import sun.jvm.hotspot.jdi.SAPIDAttachingConnector;

import java.util.List;
import java.util.Map;

/**
 * Created by blues on 31/01/15.
 */
public class SimpleDebugger {

    private static final String CLASS_NAME = "me.kisimple.just4fun.Main";
    private static final int LINE = 16;
    private static final String VAR_NAME = "i";

    public static void main(String[] args) {
        List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();
        SocketAttachingConnector sac = null;
        SAPIDAttachingConnector sapac = null;
        for (Connector connector : connectors) {
            if(connector instanceof SocketAttachingConnector) {
                sac = (SocketAttachingConnector)connector;
            } else if(connector instanceof SAPIDAttachingConnector) {
                sapac = (SAPIDAttachingConnector)connector;
            }
        }
        boolean sa = System.getProperty("kis.jdi.sa") != null;
        try {

            // 1. 使用不同SP提供的connector attach到目标VM上面
            VirtualMachine vm = null;
            if(sa) {
                if(args.length != 1) {
                    // usage();
                    return;
                }
                if(sapac != null) {
                    Map<String, Connector.Argument> defaultArguments = sapac.defaultArguments();
                    Connector.Argument pidArg = defaultArguments.get("pid"); // SAPIDAttachingConnector#ARG_PID
                    pidArg.setValue(args[0]);
                    vm = sapac.attach(defaultArguments);
                    System.out.println("using sa...");
                    System.out.println(vm.allThreads());
                }
            } else {
                if(sac != null) {
                    Map<String, Connector.Argument> defaultArguments = sac.defaultArguments();
                    Connector.Argument hostArg = defaultArguments.get("hostname"); // SocketAttachingConnector#ARG_HOST
                    Connector.Argument portArg = defaultArguments.get("port"); // SocketAttachingConnector#ARG_PORT
                    hostArg.setValue("localhost");
                    portArg.setValue("8787");
                    vm = sac.attach(defaultArguments);
                }
            }
            // process = vm.process();
            if(vm == null) {
                return;
            }

            // 2. 发送请求告诉目标VM我们需要关心哪些事件
            EventRequestManager requestManager = vm.eventRequestManager();
            List<ReferenceType> referenceTypes = vm.classesByName(CLASS_NAME);
            List<Location> locations = referenceTypes.get(0).locationsOfLine(LINE);
            BreakpointRequest breakpointRequest =
                    requestManager.createBreakpointRequest(locations.get(0));
            breakpointRequest.enable();

            // 3. 事件监听以及处理
            EventQueue eventQueue = vm.eventQueue();
            boolean disconnected = false;
            while(true) {
                if(disconnected) break;
                EventSet eventSet = eventQueue.remove();
                EventIterator eventIterator = eventSet.eventIterator();
                while(eventIterator.hasNext()) {
                    Event event = eventIterator.nextEvent();
                    if(event instanceof BreakpointEvent) {
                        System.out.println("Reach line " + LINE + " of " + CLASS_NAME);
                        BreakpointEvent breakpointEvent = (BreakpointEvent) event;
                        ThreadReference threadReference = breakpointEvent.thread();
                        StackFrame stackFrame = threadReference.frame(0);
                        LocalVariable localVariable = stackFrame
                                .visibleVariableByName(VAR_NAME);
                        Value value = stackFrame.getValue(localVariable);
                        int i = ((IntegerValue) value).value();
                        System.out.println("The local variable " + VAR_NAME + " is " + i);
                        eventSet.resume();
                    } else if (event instanceof VMDisconnectEvent) {
                        System.out.println("VM disconnected.");
                        disconnected = true;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

上面注释已经给出了一般使用JDI API的三个步骤,从中也可以看出其实不同的JDI实现,差异主要在于Connector的实现。Connector是通过ServiceLoader机制来加载的,加载的代码在VirtualMachineManagerImpl中,

    protected VirtualMachineManagerImpl() {

        /*
         * Create a top-level thread group
         */
        ThreadGroup top = Thread.currentThread().getThreadGroup();
        ThreadGroup parent = null;
        while ((parent = top.getParent()) != null) {
            top = parent;
        }
        mainGroupForJDI = new ThreadGroup(top, "JDI main");

        /*
         * Load the connectors
         */
        ServiceLoader<Connector> connectorLoader =
            ServiceLoader.load(Connector.class, Connector.class.getClassLoader());

        Iterator<Connector> connectors = connectorLoader.iterator();

        while (connectors.hasNext()) {
            Connector connector;

            try {
                connector = connectors.next();
            } catch (ThreadDeath x) {
                throw x;
            } catch (Exception x) {
                System.err.println(x);
                continue;
            } catch (Error x) {
                System.err.println(x);
                continue;
            }

            addConnector(connector);
        }

        /*
         * Load any transport services and encapsulate them with
         * an attaching and listening connector.
         */
        ServiceLoader<TransportService> transportLoader =
            ServiceLoader.load(TransportService.class,
                               TransportService.class.getClassLoader());

        Iterator<TransportService> transportServices =
            transportLoader.iterator();

        while (transportServices.hasNext()) {
            TransportService transportService;

            try {
                transportService = transportServices.next();
            } catch (ThreadDeath x) {
                throw x;
            } catch (Exception x) {
                System.err.println(x);
                continue;
            } catch (Error x) {
                System.err.println(x);
                continue;
            }

            addConnector(GenericAttachingConnector.create(transportService));
            addConnector(GenericListeningConnector.create(transportService));
        }

        // no connectors found
        if (allConnectors().size() == 0) {
            throw new Error("no Connectors loaded");
        }

        // Set the default launcher. In order to be compatible
        // 1.2/1.3/1.4 we try to make the default launcher
        // "com.sun.jdi.CommandLineLaunch". If this connector
        // isn't found then we arbitarly pick the first connector.
        //
        boolean found = false;
        List<LaunchingConnector> launchers = launchingConnectors();
        for (LaunchingConnector lc: launchers) {
            if (lc.name().equals("com.sun.jdi.CommandLineLaunch")) {
                setDefaultConnector(lc);
                found = true;
                break;
            }
        }
        if (!found && launchers.size() > 0) {
            setDefaultConnector(launchers.get(0));
        }

    }

那么这两个实现又分别提供了什么Connector呢?看下各自的jar包就知道了,如下图,

tools.jar与sa-jdi.jar中的com.sun.jdi.connect.Connector文件的内容分别是,

# List all Sun provided connector providers here. If there
# are providers that are only available on a particular OS
# then prefix the line with #[OS] and they will automatically
# uncommented by the build process - see make/jpda/front/Makefile.
#
com.sun.tools.jdi.SunCommandLineLauncher
com.sun.tools.jdi.RawCommandLineLauncher
com.sun.tools.jdi.SocketAttachingConnector
com.sun.tools.jdi.SocketListeningConnector
com.sun.tools.jdi.SharedMemoryAttachingConnector
com.sun.tools.jdi.SharedMemoryListeningConnector
com.sun.tools.jdi.ProcessAttachingConnector
# SA JDI Connectors

sun.jvm.hotspot.jdi.SACoreAttachingConnector
sun.jvm.hotspot.jdi.SADebugServerAttachingConnector
sun.jvm.hotspot.jdi.SAPIDAttachingConnector

在我们的SimpleDebugger中分别使用了JDK的SocketAttachingConnector与SA的SAPIDAttachingConnector,看类名很容易就知道,一个是通过socket来连接,一个则是通过pid。接下来就跑一跑程序看下。

作为debugee,运行Main需要加上-agentlib:jdwp=transport=dt_socket,server=y,address=8787这个VM option,这样才能通过socket的方式来连接。

$ java7 -agentlib:jdwp=transport=dt_socket,server=y,address=8787 me.kisimple.just4fun.Main
Listening for transport dt_socket at address: 8787

java7是个alias,alias java7=‘/usr/lib/jvm/java-7-openjdk-i386/bin/java‘。jps看了下pid是5156。

$ java7 me.kisimple.just4fun.SimpleDebugger
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:635)
	at java.util.ArrayList.get(ArrayList.java:411)
	at java.util.Collections$UnmodifiableList.get(Collections.java:1211)
	at me.kisimple.just4fun.SimpleDebugger.main(SimpleDebugger.java:71)
$ java7 me.kisimple.just4fun.SimpleDebugger
Reach line 16 of me.kisimple.just4fun.Main
The local variable i is 930
Reach line 16 of me.kisimple.just4fun.Main
The local variable i is 350
Reach line 16 of me.kisimple.just4fun.Main
The local variable i is 70

SimpleDebugger的运行很奇怪,第一次跑总会报错,重跑一下就好了,是个bug?暂时没去管它。

上面的输出是使用了JDK的SocketAttachingConnector的结果。是的我们已经写出了一个很简单的调试器^_^ 下面看下SA的SAPIDAttachingConnector,通过传入了一个system property来选择使用它,当然还要把debugee的pid传过去,

$ java7 -Dkis.jdi.sa=true me.kisimple.just4fun.SimpleDebugger 5156
java.io.IOException
	at sun.jvm.hotspot.jdi.SAPIDAttachingConnector.attach(SAPIDAttachingConnector.java:126)
	at me.kisimple.just4fun.SimpleDebugger.main(SimpleDebugger.java:47)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at sun.jvm.hotspot.jdi.SAPIDAttachingConnector.createVirtualMachine(SAPIDAttachingConnector.java:87)
	at sun.jvm.hotspot.jdi.SAPIDAttachingConnector.attach(SAPIDAttachingConnector.java:111)
	... 1 more
Caused by: sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process
	at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal$LinuxDebuggerLocalWorkerThread.execute(LinuxDebuggerLocal.java:152)
	at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal.attach(LinuxDebuggerLocal.java:268)
	at sun.jvm.hotspot.HotSpotAgent.attachDebugger(HotSpotAgent.java:602)
	at sun.jvm.hotspot.HotSpotAgent.setupDebuggerLinux(HotSpotAgent.java:565)
	at sun.jvm.hotspot.HotSpotAgent.setupDebugger(HotSpotAgent.java:338)
	at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:313)
	at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:157)
	at sun.jvm.hotspot.jdi.VirtualMachineImpl.createVirtualMachineForPID(VirtualMachineImpl.java:222)
	... 7 more
Caused by: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process
	at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal.attach0(Native Method)
	at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal.access$100(LinuxDebuggerLocal.java:51)
	at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal$1AttachTask.doit(LinuxDebuggerLocal.java:259)
	at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal$LinuxDebuggerLocalWorkerThread.run(LinuxDebuggerLocal.java:127)

报错了,猜测是权限问题(对比下JDK的JDI实现则没有权限问题),sudo看看,

$ sudo java7 -Dkis.jdi.sa=true me.kisimple.just4fun.SimpleDebugger 5156
[sudo] password for blues:
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jdi/Bootstrap
	at me.kisimple.just4fun.SimpleDebugger.main(SimpleDebugger.java:24)
Caused by: java.lang.ClassNotFoundException: com.sun.jdi.Bootstrap
	at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
	... 1 more

还是不行,环境变量的问题,我是在.bashrc中export CLASSPATH的,sudo之后,应该是一个新的bash。那再换个笨点的方式执行,

$ sudo su root
# export CLASSPATH=.:/usr/lib/jvm/java-7-openjdk-i386/jre/lib/rt.jar:/usr/lib/jvm/java-7-openjdk-i386/lib/tools.jar:/usr/lib/jvm/java-7-openjdk-i386/lib/sa-jdi.jar:
# /usr/lib/jvm/java-7-openjdk-i386/bin/java -Dkis.jdi.sa=true me.kisimple.just4fun.SimpleDebugger 5156
using sa...
[instance of java.lang.Thread(name='JDWP Transport Listener: dt_socket', id=0), instance of java.lang.Thread(name='JDWP Event Helper Thread', id=1), instance of java.lang.Thread(name='Signal Dispatcher', id=2), instance of java.lang.ref.Finalizer$FinalizerThread(name='Finalizer', id=3), instance of java.lang.ref.Reference$ReferenceHandler(name='Reference Handler', id=4), instance of java.lang.Thread(name='main', id=5)]
com.sun.jdi.VMCannotBeModifiedException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
	at java.lang.Class.newInstance(Class.java:379)
	at sun.jvm.hotspot.jdi.VirtualMachineImpl.throwNotReadOnlyException(VirtualMachineImpl.java:281)
	at sun.jvm.hotspot.jdi.VirtualMachineImpl.eventRequestManager(VirtualMachineImpl.java:524)
	at me.kisimple.just4fun.SimpleDebugger.main(SimpleDebugger.java:67)

前面已经成功输出了debugee当前的所有线程(有点像jstack的功能了)。但是后面报错又是咋回事?看了看官网文档源码

The VirtualMachine object returned by this connector‘s attach() method is ‘read-only‘. This means that the method:vm.canBeModified() will return false, and that the JDI client should not call any JDI
methods that are defined to throw a VMCannotBeModifiedException in this case.

    public EventQueue eventQueue() {
        throwNotReadOnlyException("VirtualMachine.eventQueue()");
        return null;
    }

    public EventRequestManager eventRequestManager() {
        throwNotReadOnlyException("VirtualMachineImpl.eventRequestManager()");
        return null;
    }

是的,也就是说SA的JDI实现,对debugee的操作是read only的,没有办法去断点,所以如果要实现debuger还是用JDK的JDI实现吧。

最后,列出上述的两点差异,

  • SA的实现有安全校验,需要sudo,而JDK的实现则没有;
  • SA的实现对debugee都是只读的,像断点这样的更改操作是不支持的,而JDK的实现则都可以;

参考资料

时间: 2024-10-13 06:55:16

JPDA(一):使用JDI写一个调试器的相关文章

[Win32]一个调试器的实现(一)调试事件与调试循环

[Win32]一个调试器的实现(一)调试事件与调试循环 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 前言 程序员离不开调试器,它可以动态显示程序的执行过程,对于解决程序问题有极大的帮助.如果你和我一样对调试器的工作原理很感兴趣,那么这一系列文章很适合你,这些文章记录了我开发一个调试器雏形的过程,希望对你有帮助.或许我

[Win32]一个调试器的实现(八)单步执行

[Win32]一个调试器的实现(八)单步执行 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 上回讲解了如何实现断点功能,这回讲解如何实现与断点紧密相关的单步执行功能.单步执行有三种类型:StepIn,StepOver和StepOut,它们的实现方式比较多样化,单独实现它们的话并不困难,但是将它们整合到一起就比较困难了,

[Win32]一个调试器的实现(九)符号模型

[Win32]一个调试器的实现(九)符号模型 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 在接下来的文章中会讲解如何在调试器中显示局部变量和全局变量的类型和值.实现这个功能一定要有调试符号的支持,因为调试符号记录了每个变量的名称,类型,地址,长度等信息.这不是一件轻松的事情,因为你首先要对符号模型有一定的了解.所以本

使用IntentService给自己的Android应用写一个文件下载器。

接着上一篇的http://www.cnblogs.com/zhengxt/p/3657833.html,当我们想给自己的APP写一个文件下载器时,可以用重写IntentService来实现. 使用IntentService有几个好处,IntentService继承于Service,适合拿来处理一些耗时又不需要去管它的任务.把要执行的任务用Intent加入到队列中,IntentService会有一个工作线程来取出队列中的Intent来处理.需要实现抽象方法onHandleIntent方法来执行这些

用JAVA写一个视频播放器

前言 跳过废话,直接看正文 当年入坑java是因为它的跨平台优势.那时我认为,"编写一次,处处运行."这听上去多么牛逼,应该是所有语言发展的终极之道,java势必会一统天下. 然而事实证明,那时的我还是太年轻. 正所谓鱼和熊掌不可兼得,若要享受跨平台带来的方便,便不可避免地要接受性能上的不足.事实上,java一直在致力于提高虚拟机的性能(JIT等技术),但面对对实时计算性能要求很高或涉及到用硬件优化的任务(视频的硬件编码.解码)时,仍远远比不上c或c++.因此,很少能够看到有人用jav

[Win32]一个调试器的实现(十)显示变量

[Win32]一个调试器的实现(十)显示变量 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 上回介绍了微软的符号模型,有了这个基础知识,这回我们向MiniDebugger中添加两个新功能,分别是显示变量列表和以指定类型显示内存内容.显示变量列表用于列出当前函数内的局部变量或者全局变量:以指定类型显示内存内容用于读取指定

[Win32]一个调试器的实现(十一)显示函数调用栈

[Win32]一个调试器的实现(十一)显示函数调用栈 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 本文讲解如何在调试器中显示函数调用栈,如下图所示: 原理 首先我们来看一下显示调用栈所依据的原理.每个线程都有一个栈结构,用来记录函数的调用过程,这个栈是由高地址向低地址增长的,即栈底的地址比栈顶的地址大.ESP寄存器的

无法在Web服务器上启动调试,已附加了一个调试器

运行环境:开发环境:Windows7旗舰版64bit.VisualStudio2008 With SP1.ArcEngine10.0.NetFrameWork4.0.IIS7和C#开发语言. 问题描述:ASP.NET调试时遇到的错误"无法在Web服务器上启动调试.已附加了一个调试器. 问题原因:我自己正在调试一个WebService,再次启动另外一个WebService调试则会报出这个错误,VS.NET不允许同时调试两个Web应用程序的. 无法在Web服务器上启动调试,已附加了一个调试器

[Win32]一个调试器的实现(二)调试事件的处理

[Win32]一个调试器的实现(二)调试事件的处理 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 上一篇文章说到了调试循环的写法,这回讲一下调试器应该如何处理各种调试事件. RIP_EVENT 关于这种调试事件的文档资料非常少,即使提到也只是用“系统错误”或者“内部错误”一笔带过.既然如此,我们也不需要对其进行什么处理