Есть ли способ вывести трассировку стека без выдачи исключения в Java?


159

Я думаю о создании инструмента отладки для моего Java-приложения.

Я задаюсь вопросом, возможно ли получить трассировку стека, точно так же, как и Exception.printStackTrace()без фактического исключения?

Моя цель состоит в том, чтобы в любом данном методе создать дамп стека, чтобы увидеть, кто является вызывающим методом.

Ответы:


84

Вы также можете попытаться Thread.getAllStackTraces()получить карту трассировок стека для всех потоков, которые являются живыми.


250

Да просто пользуйся

Thread.dumpStack()

44
... но не бросает это.
Роб

5
Это ответ, который фактически решает вопрос.
Бруно

7
Очень просто и элегантно. Это должен был быть принятый ответ.
DeLock

11
Fwiw, источник этого метода: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK

Это, безусловно, лучший ответ на вопрос, если вы довольны выводом на него stderr, что, по-видимому, является следствием вопроса.
Майк Грызун

46

Если вам нужна трассировка только для текущего потока (а не для всех потоков в системе, как рекомендует Рам), выполните:

Thread.currentThread (). getStackTrace ()

Чтобы найти абонента, выполните:

private String getCallingMethodName() {
    StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
    return callingFrame.getMethodName();
}

И вызовите этот метод из метода, который должен знать, кто его вызывает. Однако, предупреждающее слово: индекс вызывающего кадра в списке может варьироваться в зависимости от JVM! Все зависит от того, сколько уровней вызовов есть в getStackTrace, прежде чем вы достигнете точки, где генерируется трассировка. Более надежным решением было бы получить трассировку и перебрать ее, ища фрейм для getCallingMethodName, а затем сделать еще два шага вверх, чтобы найти истинного абонента.


2
Так что просто создайте новое исключение самостоятельно и используйте [1] в качестве индекса!
Даниэль

3
Название этого вопроса гласит «без исключения». Конечно, вы предлагаете создать исключение, но не выбрасывать его, но я все же думаю, что это не в духе вопроса.
Том Андерсон

1
Конечно, это является. Создание исключения - самый низкоуровневый способ получить трассировку стека. Даже ваш Thread.currentThread (). GetStackTrace () делает это, но мой способ делает это быстрее :).
Даниэль

@Daniel Есть еще одно соображение: во многих средах тестирования, например, в Spock, достаточно просто создать исключение (не выбрасывая его), чтобы платформа смогла подобрать: тогда тест будет считать, что исключение "произошло", и тест будет конец, с ошибкой.
Майк Грызун

@mikerodent: Ты уверен? Спок должен был бы использовать агентов, чтобы сделать это. Но в любом случае, поскольку мне просто нужно было вызвать класс, я использовал Reflection.getCallerClass (2) в служебной функции. «Мы не получим функцию и номер строки таким образом», - подумал.
Даниил

37

Вы можете получить трассировку стека следующим образом:

Throwable t = new Throwable();
t.printStackTrace();

Если вы хотите получить доступ к фрейму, вы можете использовать t.getStackTrace (), чтобы получить массив фреймов стека.

Имейте в виду, что в этой трассировке стека (как и в любом другом) могут отсутствовать некоторые кадры, если компилятор горячей точки был занят оптимизацией.


Мне нравится этот ответ, потому что он дает мне возможность направить вывод. Я могу заменить t.printStackTraceна t.printStackTrace(System.out).

4
В одной строке:new Throwable().printStackTrace(System.out);

1
Это должен быть принятый ответ. Это просто и прямо вперед! Спасибо за обмен
Сэм

2
и его легко отправить на java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead

13

Обратите внимание, что Thread.dumpStack () фактически генерирует исключение:

new Exception("Stack trace").printStackTrace();

12
Это создает исключение, но не выдает его.
MatrixFrog

6

Вы также можете отправить сигнал в JVM для выполнения Thread.getAllStackTraces () в работающем Java-процессе, отправив в него сигнал QUIT.

В Unix / Linux используйте:

kill -QUIT process_idгде process_id - это номер процесса вашей Java-программы.

В Windows вы можете нажать Ctrl-Break в приложении, хотя обычно вы этого не увидите, если не запускаете консольный процесс.

JDK6 представил другую опцию, команду jstack, которая будет отображать стек из любого запущенного процесса JDK6 на вашем компьютере:

jstack [-l] <pid>

Эти параметры очень полезны для приложений, которые работают в производственной среде и не могут быть легко изменены. Они особенно полезны для диагностики тупиков во время выполнения или проблем с производительностью.

http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/ http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html



6

Java 9 представила StackWalker и поддерживающие классы для обхода стека.

Вот несколько фрагментов из Javadoc:

walkМетод открывает последовательный поток StackFramesдля текущего потока , а затем применяет данную функцию , чтобы идти по StackFrameтечению. Поток сообщает элементы фрейма стека по порядку, от самого верхнего фрейма, который представляет точку выполнения, в которой был сгенерирован стек, до самого нижнего фрейма. StackFrameПоток закрывается , когда метод возвращает ходьбы. Если будет предпринята попытка повторно использовать закрытый поток, IllegalStateExceptionбудет сгенерировано.

...

  1. Чтобы сделать снимок 10 лучших кадров стека текущего потока,

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));

0

HPROF Java Profiler

Вам даже не нужно делать это в коде. Вы можете присоединить Java HPROF к вашему процессу и в любой момент нажать Control- \, чтобы вывести дамп кучи, запущенные потоки и т. Д. , , не портя код вашего приложения. Это немного устарело, Java 6 поставляется с jconsole с графическим интерфейсом, но я все еще нахожу HPROF очень полезным.


0

Ответ Роб Ди Марко , безусловно, лучше всего, если вы рады выходу в stderr.

Если вы хотите записать в String, с новыми строками в соответствии с обычной трассировкой, вы можете сделать это:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.