java - 在 Java 中伪造堆栈跟踪

标签 java exception-handling rmi rpc stack-trace

当您在 Java 中使用 RMI 时,异常的远程堆栈跟踪将在您收到它时添加到前面,有点像这样:

ERROR Client received error when doing stuff:
myapp.FooBarException: bla
 at server.myMethod()
 at rmi.callHandler() // and now, on the next line comes the client
 at rmi.sendCall();
 at client.doServerMethod()
 at Thread.run()

这种堆栈跟踪“伪造”是如何完成的?


我要它做什么(除了被迭代)?好吧,如果我能做到这一点,它会对我有帮助:

outer() {

  thread = new Thread(...
      inner();
      // inner() throws
      // RuntimeException
      //  at inner();
      //  at Runnable.run();
      //  at Thread.run();
      //  at outer();
      //  at lalalala();
      //  ...

  ).start();

  thread.join();

}

并使 inner() 中抛出的异常在堆栈跟踪中也有 outer()(和链下的方法),用于日志记录目的。

最佳答案

这有点简单:

Throwable 有方法 getStackTrace()setStackTrace()

来自 one of my projects (非开源,但也许有一天我会打开远程调用引擎):

    /**
     * Setzt den Stack-Trace zusammen. Das untere Ende (tiefer in der
     * Aufrufhierarchie, am Anfang des Arrays/der Ausgabe) ist das,
     * welches im Throwable schon drin ist, das obere Ende wird aus
     * dem aktuellen Stack genommen. Dazwischen
     * kommt ein "Remote-Aufruf-Markierer".
     */

为方便起见翻译:

Merges the stack trace. The lower end (deeper in the call hierarchy, at the end of the array/the output) is what already is in the stack, the upper end will be taken from the current stack. Between them we will put an Remote call marker.

    private void mergeStackTraces(Throwable error)
    {
        StackTraceElement[] currentStack =
            new Throwable().getStackTrace();
        int currentStackLimit = 5; // TODO: raussuchen
        StackTraceElement[] oldStack =
            error.getStackTrace();
        StackTraceElement[] zusammen =
            new StackTraceElement[currentStack.length - currentStackLimit +
                                  oldStack.length + 1];
        System.arraycopy(oldStack, 0, zusammen, 0, oldStack.length);
        zusammen[oldStack.length] =
            new StackTraceElement("══════════════════════════",
                                  "<remote call %" +callID+ ">",
                                  "", -3);
        System.arraycopy(currentStack, currentStackLimit,
                         zusammen, oldStack.length+1,
                         currentStack.length - currentStackLimit);
        error.setStackTrace(zusammen);
    }

(在服务器端,我已经切断了与方法调用本身无关的堆栈跟踪部分,即与消息处理相关的所有内容。)

这导致组合堆栈跟踪如下:

java.lang.SecurityException: Das Passwort für Nutzer »Paul« ist falsch.
        at de.fencing_game.db.userdb.Db4oUserDB.login(Db4oUserDB.java:304)
        at de.fencing_game.server.impl.StandardServers$SSServer$1.run(StandardServers.java:316)
        at de.fencing_game.server.impl.StandardServers$SSServer$1.run(StandardServers.java:314)
        at java.security.AccessController.doPrivileged(Native Method)
        at de.fencing_game.server.impl.StandardServers$SSServer.login(StandardServers.java:313)
        at de.fencing_game.transport.server.ServerTransport$ConnectionInfo$4.login(ServerTransport.java:460)
        at ══════════════════════════.<remote call %2>()
        at $Proxy1.login(Unknown Source)
        at de.fencing_game.gui.basics.LoginUtils.login(LoginUtils.java:80)
        at de.fencing_game.gui.Lobby.connectTo(Lobby.java:302)
        at de.fencing_game.gui.Lobby$20.run(Lobby.java:849)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:226)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:647)
        at java.awt.EventQueue.access$000(EventQueue.java:96)
        at java.awt.EventQueue$1.run(EventQueue.java:608)
        at java.awt.EventQueue$1.run(EventQueue.java:606)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:617)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:138)

我想 RMI 系统做的事情非常相似(只是没有 ══════════════════════════) .


编辑: 对于您的用例,您必须在启动内部线程时保存外部线程的堆栈跟踪,然后在 run 方法中捕获异常并将外部堆栈跟踪附加到内部异常的堆栈跟踪。不过,我真的建议放置某种类型的分隔符。

关于java - 在 Java 中伪造堆栈跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6270256/

相关文章:

java - Lombok SuperBuilder继承

java - 如何访问位于jar的 'META-INF/resources'文件夹中的jsp页面中的Exception对象?

java - JDK 中的哪个类创建 JRMI ProtocolAck?

java.rmi.ConnectException : Connection refused to host when RMI server is child process of RMI client

Java 和非接触式智能卡

java - 具有接收者参数覆盖的方法是否等效于没有接收者参数的相同方法声明?

hibernate - Quartz 作业因 StaleObjectStateException 异常而停止执行

python - 无法捕获 SQLAlchemy IntegrityError

c# - 如何处理由 C++ 加载的 C# DLL 中的异常

Java RMI : Rejecting requests when saturation is reached