Apakah ada cara untuk membuang jejak stack tanpa membuang pengecualian di java?

159

Saya berpikir untuk membuat alat debug untuk aplikasi Java saya.

Saya bertanya-tanya apakah mungkin untuk mendapatkan jejak stack, seperti Exception.printStackTrace()tetapi tanpa benar-benar melempar pengecualian?

Tujuan saya adalah, dalam metode apa pun, membuang tumpukan untuk melihat siapa pemanggil metode itu.

corgrath
sumber

Jawaban:

84

Anda juga dapat mencoba Thread.getAllStackTraces()untuk mendapatkan peta jejak tumpukan untuk semua utas yang masih hidup.

Prabhu R
sumber
250

Ya, cukup gunakan

Thread.dumpStack()
Rob Di Marco
sumber
44
... tapi tidak membuangnya.
Rob
5
Itulah jawaban yang sebenarnya menjawab pertanyaan itu.
Bruno
7
Sangat sederhana dan elegan. Ini seharusnya jawaban yang diterima.
deLock
11
Fwiw, sumber dari metode ini: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK
Sejauh ini, ini adalah jawaban terbaik untuk pertanyaan jika Anda senang mengeluarkannya stderr, yang tampaknya merupakan implikasi dari pertanyaan tersebut.
mike rodent
46

Jika Anda ingin jejak hanya untuk utas saat ini (daripada semua utas dalam sistem, seperti saran Ram), lakukan:

Thread.currentThread (). getStackTrace ()

Untuk menemukan penelepon, lakukan:

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

Dan panggil metode itu dari dalam metode yang perlu tahu siapa peneleponnya. Namun, kata peringatan: indeks frame panggilan dalam daftar dapat bervariasi sesuai dengan JVM! Itu semua tergantung pada berapa banyak lapisan panggilan yang ada di dalam getStackTrace sebelum Anda mencapai titik di mana jejak dihasilkan. Solusi yang lebih kuat adalah dengan mendapatkan jejak, dan beralihlah mencari frame untuk getCallingMethodName, kemudian ambil dua langkah lebih jauh untuk menemukan penelepon yang sebenarnya.

Tom Anderson
sumber
2
Jadi buat saja Pengecualian baru sendiri dan gunakan [1] sebagai indeks!
Daniel
3
Judul pertanyaan ini mengatakan "tanpa melemparkan pengecualian". Memang, Anda menyarankan untuk membuat pengecualian tetapi tidak membuangnya, tetapi saya masih berpikir itu tidak dalam semangat pertanyaan.
Tom Anderson
1
Tentu saja. Membuat Pengecualian adalah cara paling rendah untuk mendapatkan stacktrace. Bahkan Thread.currentThread Anda (). GetStackTrace () melakukannya, tetapi cara saya melakukannya lebih cepat :).
Daniel
@Aniel Ada pertimbangan lain: dengan banyak kerangka kerja pengujian, misalnya Spock, hanya membuat Pengecualian (tanpa membuangnya) akan cukup bagi kerangka kerja untuk melanjutkan: pengujian kemudian akan mempertimbangkan bahwa Pengecualian telah "terjadi" dan pengujian akan berakhir dengan gagal.
mike rodent
@mikerodent: Apakah Anda yakin? Spock harus menggunakan agen untuk melakukan itu. Tapi bagaimanapun, karena saya hanya perlu memanggil kelas saya menggunakan Reflection.getCallerClass (2) dalam fungsi utilitas. Anda tidak akan mendapatkan fungsi dan nomor baris seperti itu, pikir.
Daniel
37

Anda bisa mendapatkan jejak tumpukan seperti ini:

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

Jika Anda ingin mengakses frame, Anda bisa menggunakan t.getStackTrace () untuk mendapatkan array stack frames.

Perlu diketahui bahwa stacktrace ini (seperti yang lainnya) mungkin kehilangan beberapa frame jika kompiler hotspot sibuk mengoptimalkan hal-hal.

Gerco Dries
sumber
Saya suka jawaban ini karena menawarkan saya kesempatan untuk mengarahkan output. Saya bisa mengganti t.printStackTracedengan t.printStackTrace(System.out).
4
Dalam satu baris:new Throwable().printStackTrace(System.out);
1
Ini harus menjadi jawaban yang diterima. Sederhana dan lurus ke depan! Terima kasih telah berbagi
Sam
2
dan mudah untuk mengirim ke java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead
13

Perhatikan bahwa Thread.dumpStack () benar-benar melempar pengecualian:

new Exception("Stack trace").printStackTrace();
Ariel T
sumber
12
Itu menciptakan pengecualian, tetapi tidak membuangnya.
MatrixFrog
6

Anda juga dapat mengirim sinyal ke JVM untuk mengeksekusi Thread.getAllStackTraces () pada proses Java yang sedang berjalan dengan mengirimkan sinyal QUIT ke proses.

Di Unix / Linux gunakan:

kill -QUIT process_id, di mana process_id adalah nomor proses dari program Java Anda.

Di Windows, Anda dapat menekan Ctrl-Break di aplikasi, meskipun Anda biasanya tidak akan melihat ini kecuali Anda menjalankan proses konsol.

JDK6 memperkenalkan opsi lain, perintah jstack, yang akan menampilkan tumpukan dari proses JDK6 yang sedang berjalan di komputer Anda:

jstack [-l] <pid>

Opsi ini sangat berguna untuk aplikasi yang berjalan di lingkungan produksi dan tidak dapat dimodifikasi dengan mudah. Mereka sangat berguna untuk mendiagnosis kebuntuan runtime atau masalah kinerja.

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

Andrew Newdigate
sumber
6

Jika Anda perlu menangkap keluaran

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
Ricardo Jl Rufino
sumber
6

Java 9 memperkenalkan StackWalkerdan mendukung kelas untuk menjalankan stack.

Berikut beberapa cuplikan dari Javadoc:

The walkMetode membuka aliran berurutan StackFramesuntuk thread saat ini dan kemudian menerapkan fungsi yang diberikan untuk berjalan StackFramesungai. Streaming melaporkan elemen-elemen bingkai stack secara berurutan, dari frame paling atas yang mewakili titik eksekusi di mana stack dihasilkan ke frame paling bawah. The StackFramealiran tertutup ketika metode berjalan kembali. Jika ada upaya untuk menggunakan kembali aliran tertutup, IllegalStateExceptionakan dibuang.

...

  1. Untuk mengambil snapshot 10 bingkai tumpukan teratas dari utas saat ini,

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

HPROF Java Profiler

Anda bahkan tidak perlu melakukan ini dalam kode. Anda dapat memasang Java HPROF ke proses Anda dan kapan saja tekan Control- \ to output heap dump, running threads, dll. . . tanpa memperbaiki kode aplikasi Anda. Ini agak ketinggalan jaman, Java 6 hadir dengan GUI jconsole, tapi saya masih menemukan HPROF sangat berguna.

Gandalf
sumber
0

The jawaban dengan Rob Di Marco adalah jauh terbaik jika Anda senang untuk output ke stderr.

Jika Anda ingin menangkap ke String, dengan baris baru sesuai jejak normal, Anda bisa melakukan ini:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
mike rodent
sumber