Làm thế nào để kết xuất các đường xếp chồng theo quy trình?


Câu trả lời:


107

Để in dấu vết ngăn xếp cho quy trình hiện tại , hãy sử dụng PrintStack()từruntime/debug .

PrintStack in ra lỗi tiêu chuẩn dấu vết ngăn xếp do Stack trả về.

Ví dụ:

import(
   "runtime/debug"
)
...    
debug.PrintStack()

Để in dấu vết ngăn xếp cho tất cả các goroutines sử dụng LookupWriteTotừ runtime/pprof.

func Lookup(name string) *Profile
// Lookup returns the profile with the given name,
// or nil if no such profile exists.

func (p *Profile) WriteTo(w io.Writer, debug int) error
// WriteTo writes a pprof-formatted snapshot of the profile to w.
// If a write to w returns an error, WriteTo returns that error.
// Otherwise, WriteTo returns nil.

Mỗi Hồ sơ có một tên duy nhất. Một số cấu hình được xác định trước:

goroutine - dấu vết ngăn xếp của tất cả
đống goroutines hiện tại - lấy mẫu tất cả phân bổ heap
luồng tạo - dấu vết ngăn xếp dẫn đến việc tạo
khối luồng hệ điều hành mới - dấu vết ngăn xếp dẫn đến việc chặn trên nguyên thủy đồng bộ hóa

Ví dụ:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)

1
Nó có in dấu vết ngăn xếp của tất cả các goroutines không?

Nó phải, nó gọi Stack. "Stack trả về một dấu vết ngăn xếp được định dạng của quy trình gọi nó. Đối với mỗi quy trình, nó bao gồm thông tin dòng nguồn và giá trị PC, sau đó cố gắng khám phá, đối với các hàm Go, hàm hoặc phương thức gọi và văn bản của dòng chứa lời kêu gọi. "
Intermernet

1
Xin lỗi, nó chỉ in dấu vết ngăn xếp quy trình hiện tại.

4
@HowardGuo Tôi đã thêm một ví dụ sử dụng runtime / pprof để kết xuất tất cả các dấu vết ngăn xếp.
Intermernet

1
Tôi nghĩ rằng đây chỉ kết quả đầu ra goroutine mỗi chủ đề hiện đang chạy, không tất cả goroutines, ví dụ: play.golang.org/p/0hVB0_LMdm
rogerdpack

39

Có một giao diện người dùng HTTP cho runtime/pprofgói được đề cập trong câu trả lời của Intermernet. Nhập gói net / http / pprof để đăng ký một trình xử lý HTTP cho /debug/pprof:

import _ "net/http/pprof"
import _ "net/http"

Bắt đầu trình nghe HTTP nếu bạn chưa có:

go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

Sau đó, trỏ trình duyệt đến http://localhost:6060/debug/pprofmột menu hoặc http://localhost:6060/debug/pprof/goroutine?debug=2cho một kết xuất ngăn xếp quy trình đầy đủ.

Có những điều thú vị khác mà bạn có thể tìm hiểu về mã đang chạy của mình theo cách này. Xem bài đăng trên blog để biết ví dụ và biết thêm chi tiết: http://blog.golang.org/profiling-go-programs


tôi đã làm cho nó chạy bởi nó chỉ hiển thị các goroutines được thực thi theo những gì tôi thấy. Có cách nào tôi có thể xem tất cả các "phương thức" được thực thi sau khi khởi chạy main.go không?
Lukas Lukac

38

Để bắt chước hành vi Java của stack-dump trên SIGQUIT nhưng vẫn để chương trình chạy:

go func() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGQUIT)
    buf := make([]byte, 1<<20)
    for {
        <-sigs
        stacklen := runtime.Stack(buf, true)
        log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
    }
}()

4
Tôi nghĩ đây là điều mà tác giả thực sự đang tìm kiếm - bắt chước những gì Java làm khi bạn gửi một kill -QUIT. Một thay đổi nhỏ mà tôi phải thực hiện là thay đổi dòng đầu tiên của vòng lặp for () thành: "<- sigs". Nói cách khác, chỉ cần loại bỏ tín hiệu sau khi chờ nó. Các phiên bản gần đây của Go sẽ không cho phép bạn khai báo một biến mà sau này không sử dụng nó.
George Armhold

@Bryan, bạn có sẵn sàng cấp phép điều này theo BSD hoặc các điều khoản dễ dàng hơn khác bổ sung cho CC-BY-SA 3.0 mà StackOverflow yêu cầu không?
Charles Duffy

1
@CharlesDuffy, bạn có thể tìm thấy nhiều thứ tương tự ở đây theo giấy phép Apache: github.com/weaveworks/weave/blob/…
Bryan

37

Tương tự như Java, SIGQUIT có thể được sử dụng để in dấu vết ngăn xếp của một chương trình Go và các goroutines của nó.
Tuy nhiên, một điểm khác biệt chính là theo mặc định, việc gửi SIGQUIT tới các chương trình Java không chấm dứt chúng, trong khi các chương trình Go thoát.

Cách tiếp cận này không yêu cầu thay đổi mã để in dấu vết ngăn xếp của tất cả các goroutines của các chương trình hiện có.

Biến môi trường GOTRACEBACK ( xem tài liệu của gói thời gian chạy ) kiểm soát số lượng đầu ra được tạo. Ví dụ: để bao gồm tất cả các goroutines, hãy đặt GOTRACEBACK = all.

Việc in dấu vết ngăn xếp được kích hoạt bởi một điều kiện thời gian chạy không mong muốn (tín hiệu không được xử lý), ban đầu được ghi lại trong cam kết này , làm cho nó có sẵn kể từ ít nhất Go 1.1.


Ngoài ra, nếu sửa đổi mã nguồn là một tùy chọn, hãy xem các câu trả lời khác.


Lưu ý rằng trong một thiết bị đầu cuối Linux, SIGQUIT có thể được gửi một cách thuận tiện bằng tổ hợp phím Ctrl+ \.


5
Trong khi xem qua các tài liệu, tôi không tìm thấy bất kỳ đề cập nào về SIGQUIT, đúng hơn là SIGABRT. Từ các thử nghiệm của riêng tôi (với go 1.7), cái sau cũng hoạt động hơn cái trước.
soltysh

4
đây phải là câu trả lời hàng đầu.
Steven Soroka

Tài liệu đề cập đến "khi chương trình Go không thành công do sự cố không được khắc phục hoặc điều kiện thời gian chạy không mong muốn". Một tín hiệu chưa được ghi (SIGQUIT, v.v.) là một trong những tín hiệu sau. Tại sao tôi lại đề cập đến SIGQUIT? Bởi vì OP thể hiện tình yêu của họ đối với việc sử dụng SIGQUIT với Java, và câu trả lời này nhấn mạnh sự tương đồng. Ghi lại câu trả lời để làm cho nó rõ ràng hơn.
Rodolfo Carvalho

26

Bạn có thể sử dụng runtime.Stack để lấy dấu vết ngăn xếp của tất cả các goroutines:

buf := make([]byte, 1<<16)
runtime.Stack(buf, true)
fmt.Printf("%s", buf)

Từ tài liệu:

func Stack(buf []byte, all bool) int

Stack định dạng một dấu vết ngăn xếp của goroutine đang gọi thành buf và trả về số byte được ghi vào buf. Nếu tất cả đều đúng, các định dạng Stack sẽ xếp dấu vết của tất cả các goroutines khác vào buf sau dấu vết cho goroutine hiện tại.


Điều này bao gồm các dấu vết từ tất cả các goroutines, tốt đẹp!
rogerdpack

Đây có phải là định dạng mà một kẻ hoảng sợ chưa được phục hồi sẽ sử dụng không?
Ztyx

2
Đừng quên thêm chuỗi (buf) nếu không bạn sẽ in các byte thô ở đó.
koda

2
Có thể tôi đang làm gì đó sai, hoặc có lẽ chức năng đã thay đổi, nhưng điều này không truy xuất bất kỳ thứ gì cho tôi ngoại trừ một lát byte trống?
17xande

1
@koda không cần phải làm string(buf)ở đây, fmt.Printf("%s", buf)fmt.Printf("%s", string(buf))làm điều tương tự (xem tài liệu về fmtgói); sự khác biệt duy nhất ở đây là stringphiên bản sẽ sao chép các byte từ bufkhông cần thiết
kbolino

20

Nhấn CTRL + \

(Nếu bạn chạy nó trong một thiết bị đầu cuối và chỉ muốn giết chương trình của mình và kết xuất các quy trình hoạt động, v.v.)

Tôi tìm thấy câu hỏi này đang tìm kiếm chuỗi khóa. Tôi chỉ muốn một cách nhanh chóng và dễ dàng để biết liệu chương trình của tôi có bị rò rỉ quy trình hoạt động hay không :)


11

Trên hệ thống * NIX (bao gồm OSX) gửi tín hiệu hủy bỏ SIGABRT:

pkill -SIGABRT program_name


Rõ ràng, việc gửi SIGQUIT tới một quy trình Java không kết thúc nó như SIGABRT sẽ làm.
Dave C

Tôi thấy đây là giải pháp đơn giản nhất và phù hợp nhất cho câu hỏi ban đầu. Thường thì bạn cần một stacktrace ngay lập tức mà không cần thay đổi mã của mình.
jotrocken

5

Cần sử dụng độ dài được trả về runtime.Stack()để tránh in một loạt các dòng trống sau dấu vết ngăn xếp của bạn. Hàm khôi phục sau sẽ in ra một dấu vết được định dạng độc đáo:

if r := recover(); r != nil {
    log.Printf("Internal error: %v", r))
    buf := make([]byte, 1<<16)
    stackSize := runtime.Stack(buf, true)
    log.Printf("%s\n", string(buf[0:stackSize]))
}

Không có runtime.Trace; runtime.Stack đã được đề cập cách đây một năm rưỡi .
Dave C

Tôi chưa bao giờ thấy điều đó; bạn đang chạy trên nền tảng nào?
Bryan,

Nó là gì bạn chưa thấy? Mã phải chạy trên tất cả các nền tảng; Tôi đã thử nghiệm nó trên Windows 7, Ubuntu 14.04 và Mac.
David Tootill

Không bao giờ thấy dòng trống.
Bryan

4

Theo mặc định, nhấn ^\các phím ( CTRL + \ ) để kết xuất dấu vết ngăn xếp của tất cả các goroutines.


Nếu không, để kiểm soát chi tiết hơn, bạn có thể sử dụng panic. Cách đơn giản kể từ Go 1.6+ :

go func() {
    s := make(chan os.Signal, 1)
    signal.Notify(s, syscall.SIGQUIT)
    <-s
    panic("give me the stack")
}()

Sau đó, chạy chương trình của bạn như sau:

# Press ^\ to dump the stack traces of all the user-created goroutines
$ GOTRACEBACK=all go run main.go

Nếu bạn cũng muốn in goroutines thời gian chạy:

$ GOTRACEBACK=system go run main.go

Đây là tất cả các tùy chọn GOTRACEBACK:

  • GOTRACEBACK=none bỏ qua hoàn toàn dấu vết ngăn xếp goroutine.
  • GOTRACEBACK=single (mặc định) hoạt động như mô tả ở trên.
  • GOTRACEBACK=all thêm dấu vết ngăn xếp cho tất cả các goroutines do người dùng tạo.
  • GOTRACEBACK=system giống như `` tất cả '' nhưng thêm khung ngăn xếp cho các chức năng thời gian chạy và hiển thị các goroutines được tạo nội bộ bởi thời gian chạy.
  • GOTRACEBACK=crashgiống như `` hệ thống '' nhưng bị treo theo cách dành riêng cho hệ điều hành thay vì thoát ra. Ví dụ, trên các hệ thống Unix, sự cố xảy ra SIGABRTđể kích hoạt kết xuất lõi.

Đây là tài liệu

Biến GOTRACEBACK kiểm soát lượng đầu ra được tạo ra khi chương trình Go bị lỗi do sự cố không được khắc phục hoặc điều kiện thời gian chạy không mong muốn.

Theo mặc định, lỗi sẽ in dấu vết ngăn xếp cho quy trình hiện tại, làm sáng tỏ các chức năng bên trong hệ thống thời gian chạy và sau đó thoát với mã thoát 2. Lỗi in dấu vết ngăn xếp cho tất cả các quy trình nếu không có quy trình hiện tại hoặc lỗi là bên trong thời gian chạy.

Vì lý do lịch sử, cài đặt GOTRACEBACK 0, 1 và 2 là từ đồng nghĩa với không, tất cả và hệ thống, tương ứng.

Hàm SetTraceback của gói thời gian chạy / gỡ lỗi cho phép tăng số lượng đầu ra tại thời gian chạy, nhưng nó không thể giảm số lượng dưới mức được chỉ định bởi biến môi trường. Xem https://golang.org/pkg/runtime/debug/#SetTraceback .

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.