Czy istnieje sposób na zrzucenie śladu stosu bez zgłaszania wyjątku w Javie?

159

Myślę o stworzeniu narzędzia do debugowania dla mojej aplikacji Java.

Zastanawiam się, czy można uzyskać ślad stosu, tak jak Exception.printStackTrace() ale bez rzucania wyjątku?

Moim celem jest zrzucenie stosu w dowolnej metodzie, aby zobaczyć, kto jest obiektem wywołującym metodę.

corgrath
źródło

Odpowiedzi:

84

Możesz także spróbować Thread.getAllStackTraces()uzyskać mapę śladów stosu dla wszystkich żyjących wątków.

Prabhu R
źródło
250

Tak, po prostu użyj

Thread.dumpStack()
Rob Di Marco
źródło
44
... ale nie rzuca.
Rob
5
To jest odpowiedź, która faktycznie odpowiada na pytanie.
bruno
7
Bardzo proste i eleganckie. To powinna być zaakceptowana odpowiedź.
deLock
11
Fwiw, źródło tej metody: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK
Jest to zdecydowanie najlepsza odpowiedź na pytanie, jeśli jesteś zadowolony z wyjścia stderr, co wydaje się być implikacją pytania.
mike gryzoń
46

Jeśli chcesz śledzić tylko bieżący wątek (a nie wszystkie wątki w systemie, jak sugeruje to Ram), wykonaj:

Thread.currentThread (). getStackTrace ()

Aby znaleźć dzwoniącego, wykonaj:

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

I wywołaj tę metodę z poziomu metody, która musi wiedzieć, kto jest jej wywołującym. Jednak słowo ostrzeżenia: indeks ramki wywołującej na liście może się różnić w zależności od maszyny JVM! Wszystko zależy od tego, ile warstw wywołań znajduje się w getStackTrace, zanim dotrzesz do punktu, w którym generowany jest ślad. Bardziej niezawodnym rozwiązaniem byłoby pobranie śladu i iteracja po nim w poszukiwaniu ramki dla getCallingMethodName, a następnie wykonanie dwóch kroków dalej, aby znaleźć prawdziwego wywołującego.

Tom Anderson
źródło
2
Dlatego po prostu utwórz samodzielnie nowy wyjątek i użyj [1] jako indeksu!
Daniel,
3
Tytuł tego pytania brzmi „bez rzucania wyjątku”. To prawda, sugerujesz utworzenie wyjątku, ale go nie wyrzucasz, ale nadal uważam, że to nie jest w duchu pytania.
Tom Anderson,
1
Oczywiście, że jest. Tworzenie wyjątku to najniższy poziom uzyskiwania śladu stosu. Nawet twój Thread.currentThread (). GetStackTrace () robi to, ale na mój sposób robi to szybciej :).
Daniel,
@Daniel Jest jeszcze jedna kwestia: w przypadku wielu frameworków testowych, np. Spock, samo utworzenie wyjątku (bez jego wyrzucania) wystarczy, aby framework mógł go wychwycić: test uzna, że ​​wystąpił wyjątek i test koniec z porażką.
mike gryzoń
@mikerodent: Czy na pewno? Spock musiałby do tego użyć agentów. W każdym razie, ponieważ potrzebowałem tylko wywołać klasę, użyłem Reflection.getCallerClass (2) w funkcji narzędzia. Nie dostaniesz w ten sposób funkcji i numeru wiersza, pomyślał.
Daniel,
37

Możesz uzyskać ślad stosu w następujący sposób:

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

Jeśli chcesz uzyskać dostęp do ramki, możesz użyć t.getStackTrace (), aby uzyskać tablicę ramek stosu.

Należy pamiętać, że w tej ścieżce stosu (podobnie jak w każdym innym) może brakować niektórych ramek, jeśli kompilator punktu aktywnego był zajęty optymalizacją.

Gerco Dries
źródło
Podoba mi się ta odpowiedź, ponieważ daje mi możliwość kierowania produkcją. Mogę zamienić t.printStackTracez t.printStackTrace(System.out).
4
W jednej linii:new Throwable().printStackTrace(System.out);
1
To powinna być akceptowana odpowiedź. To jest proste i proste! Dzięki za udostępnienie
Sam
2
i łatwo jest wysłać do java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead
13

Zauważ, że Thread.dumpStack () faktycznie zgłasza wyjątek:

new Exception("Stack trace").printStackTrace();
Ariel T.
źródło
12
Tworzy wyjątek, ale go nie zgłasza.
MatrixFrog
6

Możesz również wysłać sygnał do maszyny JVM, aby wykonać Thread.getAllStackTraces () w działającym procesie Java, wysyłając do procesu sygnał QUIT.

W systemie Unix / Linux użyj:

kill -QUIT process_id, gdzie id_procesu to numer procesu programu Java.

W systemie Windows możesz nacisnąć Ctrl-Break w aplikacji, chociaż zwykle tego nie zobaczysz, chyba że uruchomisz proces konsoli.

JDK6 wprowadził kolejną opcję, komendę jstack, która wyświetli stos z dowolnego uruchomionego procesu JDK6 na twoim komputerze:

jstack [-l] <pid>

Te opcje są bardzo przydatne w przypadku aplikacji działających w środowisku produkcyjnym i nie można ich łatwo modyfikować. Są szczególnie przydatne do diagnozowania zakleszczeń w czasie wykonywania lub problemów z wydajnością.

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

Andrew Newdigate
źródło
6

Jeśli potrzebujesz wyjścia przechwytywania

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
Ricardo Jl Rufino
źródło
6

Java 9 wprowadziła StackWalker i obsługujące klasy do chodzenia po stosie.

Oto kilka fragmentów z Javadoc:

walkMetoda otwiera sekwencyjny strumień StackFramesdla bieżącego wątku, a następnie stosuje się daną funkcję chodzić StackFramestrumień. Strumień raportuje elementy ramek stosu w kolejności, od najwyższej ramki, która reprezentuje punkt wykonania, w którym stos został wygenerowany, do najniższej ramki. StackFrameStrumień jest zamknięty, gdy powraca metoda spacer. Jeśli zostanie podjęta próba ponownego wykorzystania zamkniętego strumienia, IllegalStateExceptionzostanie wyrzucona.

...

  1. Aby zrobić migawkę 10 najlepszych ramek stosu bieżącego wątku,

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));
mkobit
źródło
0

HPROF Java Profiler

Nie musisz nawet tego robić w kodzie. Możesz dołączyć Java HPROF do swojego procesu i w dowolnym momencie nacisnąć Control- \, aby wydrukować zrzut sterty, uruchomić wątki itp. . . bez zepsucia kodu aplikacji. Jest to trochę przestarzałe, Java 6 jest dostarczana z konsolą Jconsole GUI, ale nadal uważam, że HPROF jest bardzo przydatny.

Gandalf
źródło
0

Odpowiedź Rob Di Marco jest zdecydowanie najlepiej, jeśli są zadowoleni z wyjściem dostderr .

Jeśli chcesz przechwycić do a String, z nowymi wierszami zgodnie ze zwykłym śladem, możesz to zrobić:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
mike gryzoń
źródło