Có cách nào để bỏ dấu vết ngăn xếp mà không ném ngoại lệ trong java không?


159

Tôi đang nghĩ đến việc tạo ra một công cụ gỡ lỗi cho ứng dụng Java của mình.

Tôi tự hỏi nếu có thể có được một dấu vết ngăn xếp, giống như Exception.printStackTrace()nhưng không thực sự ném một ngoại lệ?

Mục tiêu của tôi là, trong bất kỳ phương thức đã cho nào, kết xuất một ngăn xếp để xem ai là người gọi phương thức.

Câu trả lời:


84

Bạn cũng có thể thử Thread.getAllStackTraces()lấy bản đồ theo dõi ngăn xếp cho tất cả các luồng còn sống.


250

Có, chỉ cần sử dụng

Thread.dumpStack()

44
... nhưng không ném nó.
Cướp

5
Đó là câu trả lời thực sự giải quyết câu hỏi.
bruno

7
Rất đơn giản và thanh lịch. Đây phải là câu trả lời được chấp nhận.
deLock

11
Fwiw, nguồn gốc của phương pháp này: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK

Đây là câu trả lời tốt nhất cho câu hỏi nếu bạn hài lòng với việc xuất ra stderr, dường như đó là hàm ý của câu hỏi.
mike gặm nhấm

46

Nếu bạn muốn theo dõi chỉ cho luồng hiện tại (chứ không phải tất cả các luồng trong hệ thống, như đề xuất của Ram), hãy làm:

Thread.cienThread (). getStackTrace ()

Để tìm người gọi, hãy làm:

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

Và gọi phương thức đó từ bên trong phương thức cần biết người gọi nó là ai. Tuy nhiên, một lời cảnh báo: chỉ mục của khung gọi trong danh sách có thể thay đổi theo JVM! Tất cả phụ thuộc vào số lượng cuộc gọi có trong getStackTrace trước khi bạn nhấn vào điểm mà dấu vết được tạo. Một giải pháp mạnh mẽ hơn là lấy dấu vết và lặp lại nó để tìm khung cho getCallingMethodName, sau đó tiến thêm hai bước để tìm người gọi thực sự.


2
Vì vậy, chỉ cần tự tạo một Ngoại lệ mới và sử dụng [1] làm chỉ mục!
Daniel

3
Tiêu đề của câu hỏi này nói "không ném ngoại lệ". Cấp, bạn đề nghị tạo ngoại lệ nhưng không ném nó, nhưng tôi vẫn nghĩ rằng đó không phải là tinh thần của câu hỏi.
Tom Anderson

1
Tất nhiên là thế rồi. Tạo một ngoại lệ là cách thấp nhất để có được một stacktrace. Ngay cả Thread.cienThread (). GetStackTrace () của bạn cũng làm điều đó, nhưng cách của tôi làm nó nhanh hơn :).
Daniel

@Daniel Có một sự cân nhắc khác: với nhiều khung thử nghiệm, ví dụ như Spock, chỉ cần tạo Ngoại lệ (không ném nó) sẽ đủ để khung đó tiếp tục: thử nghiệm sau đó sẽ xem xét rằng Ngoại lệ đã "xảy ra" và thử nghiệm sẽ kết thúc, với một thất bại.
mike gặm nhấm

@mikerodent: Bạn có chắc không? Spock sẽ phải sử dụng các tác nhân để làm điều đó. Nhưng dù sao đi nữa, vì tôi chỉ cần gọi lớp nên tôi đã sử dụng Reflection.getCallerClass (2) trong một hàm tiện ích. Bạn sẽ không nhận được chức năng và số dòng theo cách đó, nghĩ.
Daniel

37

Bạn có thể nhận được một dấu vết ngăn xếp như thế này:

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

Nếu bạn muốn truy cập vào khung, bạn có thể sử dụng t.getStackTrace () để lấy một mảng các khung ngăn xếp.

Xin lưu ý rằng stacktrace này (giống như mọi thứ khác) có thể bị thiếu một số khung nếu trình biên dịch hotspot đang bận tối ưu hóa mọi thứ.


Tôi thích câu trả lời này vì nó cung cấp cho tôi cơ hội để chỉ đạo đầu ra. Tôi có thể thay thế t.printStackTracebằng t.printStackTrace(System.out).

4
Trong một dòng:new Throwable().printStackTrace(System.out);

1
Đây phải là câu trả lời được chấp nhận. Nó là đơn giản và thẳng về phía trước! Cảm ơn đã chia sẻ
Sam

2
và thật dễ dàng để gửi tới java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead

13

Lưu ý rằng Thread.dumpStack () thực sự ném một ngoại lệ:

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

12
Nó tạo ra một ngoại lệ, nhưng không ném nó.
MatrixFrog

6

Bạn cũng có thể gửi tín hiệu đến JVM để thực thi Thread.get ALLStackTraces () trên một quy trình Java đang chạy bằng cách gửi tín hiệu QUIT đến quy trình.

Trên Unix / Linux sử dụng:

kill -QUIT process_id, trong đó process_id là số tiến trình của chương trình Java của bạn.

Trên Windows, bạn có thể nhấn Ctrl-Break trong ứng dụng, mặc dù bạn thường không thấy điều này trừ khi bạn đang chạy một quy trình điều khiển.

JDK6 đã giới thiệu một tùy chọn khác, lệnh jstack, sẽ hiển thị ngăn xếp từ bất kỳ quy trình JDK6 nào đang chạy trên máy tính của bạn:

jstack [-l] <pid>

Các tùy chọn này rất hữu ích cho các ứng dụng đang chạy trong môi trường sản xuất và không thể sửa đổi dễ dàng. Chúng đặc biệt hữu ích để chẩn đoán các bế tắc thời gian chạy hoặc các vấn đề về hiệu suất.

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


6

Nếu bạn cần đầu ra chụp

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();

6

Java 9 đã giới thiệu StackWalker và các lớp hỗ trợ cho việc đi bộ stack.

Dưới đây là một vài đoạn trích từ Javadoc:

Các walkphương pháp mở ra một dòng tuần tự của StackFramescác chủ đề hiện tại và sau đó áp dụng các chức năng dành cho đi bộ StackFramedòng. Luồng báo cáo các thành phần khung ngăn xếp theo thứ tự, từ khung hình trên cùng đại diện cho điểm thực hiện tại đó ngăn xếp được tạo đến khung hình dưới cùng nhất. Các StackFramedòng được đóng lại khi trở về phương pháp đi bộ. Nếu một nỗ lực được thực hiện để sử dụng lại luồng kín, IllegalStateExceptionsẽ bị ném.

...

  1. Để chụp nhanh 10 khung ngăn xếp trên cùng của chuỗi hiện tại,

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

0

Trình biên dịch Java HPROF

Bạn thậm chí không phải làm điều này trong mã. Bạn có thể đính kèm Java HPROF vào quy trình của mình và tại bất kỳ thời điểm nào, nhấn Control- \ để xuất kết xuất heap, chạy các luồng, v.v. . . mà không mucking mã ứng dụng của bạn. Điều này hơi lỗi thời, Java 6 đi kèm với jconsole GUI, nhưng tôi vẫn thấy HPROF rất hữu ích.


0

Câu trả lời của Rob Di Marco là tốt nhất nếu bạn hài lòng khi xuất ra stderr.

Nếu bạn muốn chụp đến một String, với các dòng mới theo dấu vết thông thường, bạn có thể làm điều này:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.