CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.
Bài viết trên blog CFSDN Cách bảo tồn các cảnh sự cố của chương trình Go được tác giả sưu tầm và biên soạn. Nếu các bạn quan tâm đến bài viết này thì nhớ like nhé.

Không có viên đạn bạc nào có thể xóa sạch mọi thứ và không có chương trình nào đảm bảo sẽ không bao giờ sai sót. Làm cách nào để phát hiện lỗi chương trình Go? Tôi nghĩ phản ứng đầu tiên của học sinh là: log.
Nhưng khả năng của nhật ký lỗi bị hạn chế. Đầu tiên, nhật ký là thông tin in được nhà phát triển xác định trong mã. Chúng tôi không thể đảm bảo rằng thông tin nhật ký có thể chứa tất cả các điều kiện lỗi. Thứ hai, khi xảy ra sự cố trong chương trình Go, không phải lúc nào chúng ta cũng nắm bắt được nó thông qua quá trình khôi phục (không có cách nào để chèn mã nhật ký).
Sau khi chương trình Go trực tuyến bị treo đột ngột và không thể giải thích được, khi bản ghi nhật ký không bao gồm tình huống lỗi, có cách nào khác để khắc phục sự cố không?
đổ lõi
Kết xuất lõi, còn được gọi là kết xuất lõi, chỉ đơn giản là một ảnh chụp nhanh bộ nhớ được tạo khi chương trình kết thúc đột ngột. Chúng tôi có thể gỡ lỗi chương trình thông qua tệp kết xuất lõi và tìm ra nguyên nhân gây ra sự cố.
Trên nền tảng Linux, bạn có thể xem cấu hình kết xuất lõi thông qua lệnh ulimit -c. Mặc định của hệ thống là 0, cho biết chức năng ghi kết xuất lõi không được bật.
- $ulimit-c
- 0
Bạn có thể sử dụng lệnh ulimit -c [size] để chỉ định kích thước của tệp kết xuất lõi đã ghi, nhằm cho phép ghi kết xuất lõi. Tất nhiên, nếu tài nguyên máy tính đủ để tránh mất kết xuất lõi hoặc ghi không đầy đủ, bạn cũng có thể thực thi ulimit -c không giới hạn mà không giới hạn kích thước tệp kết xuất lõi.
Vậy làm cách nào để kích hoạt core dump trong chương trình Go?
TRỞ LẠI
Chúng ta đã thảo luận về ma thuật đen của việc chuyển đổi chuỗi thành []byte trong bài viết Bạn có thực sự hiểu việc chuyển đổi giữa chuỗi và []byte không, như trong ví dụ sau.
- góichính
-
- nhập khẩu(
- "phản ánh"
- "không an toàn"
- )
-
- funcString2Bytes(chuỗi)[]byte{
- sh:=(*reflect.StringHeader)(không an toàn.Con trỏ(&s))
- bh:=phản chiếu.SliceHeader{
- Dữ liệu: sh.Data,
- Len: sh.Len,
- Cap:sh.Len,
- }
- trở lại*(*[]byte)(không an toàn.Con trỏ(&bh))
- }
-
- chức năngBiến đổi(){
- một:="Xin chào"
- b:=Chuỗi2Byte(a)
- b[0]='H'
- }
-
- hàm
- Biến đổi()
- }
Không thể sửa đổi chuỗi. Khi chúng tôi chuyển đổi loại chuỗi thành []byte thông qua ma thuật đen và cố gắng sửa đổi giá trị của nó, chương trình sẽ tạo ra một lỗi mà quá trình khôi phục không thể phát hiện được.
- $gorunmain.go
- địa chỉ lỗi bất ngờ0x106a6a4
- lỗi nghiêm trọng:lỗi
- [signalSIGBUS:buserrorcode=0x2addr=0x106a6a4pc=0x105b01a]
-
- goroutine1[đang chạy]:
- thời gian chạy.throw({0x106a68b,0x0})
- /chúng ta/địa phương/go/src/runtime/panic.go:1198+0x71fp=0xc000092ee8sp=0xc000092eb8pc=0x102bad1
- runtime.sigpanic()
- /chúng ta/địa phương/go/src/runtime/signal_unix.go:732+0x1d6fp=0xc000092f38sp=0xc000092ee8pc=0x103f2f6
- chủ yếu.Biến đổi(...)
- /Users/slp/github/PostDemo/coreDemo/main.go:21
- chính.main()
- /Users/slp/github/PostDemo/coreDemo/main.go:25+0x5afp=0xc000092f80sp=0xc000092f38pc=0x105b01a
- runtime.main()
- /chúng ta/địa phương/go/src/runtime/proc.go:255+0x227fp=0xc000092fe0sp=0xc000092f80pc=0x102e167
- runtime.goexit()
- /chúng ta/địa phương/go/src/runtime/asm_amd64.s:1581+0x1fp=0xc000092fe8sp=0xc000092fe0pc=0x1052dc1
- trạng thái thoát2
Thông tin ngăn xếp này được kiểm soát bởi biến GOTRACEBACK để kiểm soát độ chi tiết in, có năm cấp độ.
- không có, không hiển thị bất kỳ thông tin ngăn xếp goroutine nào
- single, cấp độ mặc định, hiển thị thông tin ngăn xếp goroutine hiện tại
- tất cả, hiển thị thông tin ngăn xếp goroutine được tạo bởi tất cả người dùng (không bao gồm thời gian chạy)
- hệ thống, hiển thị tất cả thông tin ngăn xếp goroutine được tạo bởi người dùng + thời gian chạy
- Sự cố, giống như in hệ thống, nhưng sẽ tạo tệp kết xuất lõi (trên hệ thống Unix, sự cố sẽ kích hoạt SIGABRT để kích hoạt kết xuất lõi)
Nếu chúng ta đặt GOTRACEBACK thành hệ thống, chúng ta sẽ thấy tất cả thông tin trạng thái goroutine khi chương trình gặp sự cố.
- $GOTRACEBACK=systemgorunmain.go
- địa chỉ lỗi bất ngờ0x106a6a4
- lỗi nghiêm trọng:lỗi
- [signalSIGBUS:buserrorcode=0x2addr=0x106a6a4pc=0x105b01a]
-
- goroutine1[đang chạy]:
- thời gian chạy.throw({0x106a68b,0x0})
- ...
-
- goroutine2[lực lượnggc(nhàn rỗi)]:
- thời gian chạy.gopark(0x0,0x0,0x0,0x0,0x0)
- ...
- tạoquathời gian chạy.init.7
- /chúng ta/địa phương/go/src/runtime/proc.go:294+0x25
-
- goroutine3[GCsweepwait]:
- thời gian chạy.gopark(0x0,0x0,0x0,0x0,0x0)
- ...
- tạoquaruntime.gcenable
- /chúng ta/địa phương/go/src/runtime/mgc.go:181+0x55
-
- goroutine4[GCscavengewait]:
- thời gian chạy.gopark(0x0,0x0,0x0,0x0,0x0)
- ...
- tạoquaruntime.gcenable
- /chúng ta/địa phương/go/src/runtime/mgc.go:182+0x65
- trạng thái thoát2
Nếu bạn muốn lấy tệp kết xuất lõi, bạn nên đặt giá trị GOTRACEBACK thành lỗi. Tất nhiên, chúng ta cũng có thể đặt mức in ngăn xếp thông qua phương thức SetTraceback trong gói thời gian chạy/gỡ lỗi.
gỡ lỗi sâu
delve là trình gỡ lỗi chương trình Go được viết bằng ngôn ngữ Go. Chúng ta có thể gỡ lỗi kết xuất lõi thông qua lệnh dlv core.
Đầu tiên, cài đặt delve thông qua lệnh sau.
- goget-ugithub.com/go-delve/delve/cmd/dlv
Lấy ví dụ trên làm ví dụ, chúng tôi lấy tệp kết xuất lõi bằng cách đặt GOTRACEBACK ở mức sự cố.
- $cây
- .
- └──main.go
- $ulimit-cunlimited
- $gobuildmain.go
- $GOTRACEBACK=sụp đổ./chính
- ...
- Đã hủy bỏ (coredumped)
- $cây
- .
- ├──cốt lõi
- ├──chính
- └──main.go
- $ls-alhcore
- -rw
Tại thời điểm này, lõi tệp kết xuất lõi được lấy trong cùng một thư mục cấp (tên tệp, đường dẫn lưu trữ và việc có thêm số quy trình hay không đều có thể được định cấu hình và sửa đổi).
Sử dụng trình gỡ lỗi dlv để gỡ lỗi tệp lõi và thực thi định dạng lệnh tệp lõi tên tệp thực thi dlv lõi.
- $dlvcoremaincore
- Kiểu'giúp đỡ'vìdanh sáchcủalệnh.
- (dlv)
Ra lệnh cho goroutine để lấy tất cả thông tin liên quan đến goroutine.
- (dlv)goroutines
- *Goroutine1-Người sử dụng:./main.go:21main.main(0x45b81a)(thread18061)
- Goroutine2-Người sử dụng:/chúng ta/địa phương/go/src/runtime/proc.go:367runtime.gopark(0x42ed96)[lực lượnggc(nhàn rỗi)]
- Goroutine3-Người sử dụng:/chúng ta/địa phương/go/src/runtime/proc.go:367runtime.gopark(0x42ed96)[GCsweepwait]
- Goroutine4-Người sử dụng:/chúng ta/địa phương/go/src/runtime/proc.go:367runtime.gopark(0x42ed96)[GCscavengewait]
- [4goroutines]
- (dlv)
Goroutine 1 là goroutine được đề cập (với * đại diện cho frame hiện tại), chuyển sang stack frame của nó thông qua lệnh goroutine 1.
- (dlv)goroutine1
- Đã chuyển đổitừ1ĐẾN1(thread18061)
- (dlv)
Thực hiện lệnh bt (theo dõi điểm dừng) để xem chi tiết khung ngăn xếp hiện tại.
- (dlv) bt
- 00x0000000000454bc1TRONGthời gian chạy.nâng cao
- Tại/chúng ta/địa phương/go/src/runtime/sys_linux_amd64.s:165
- 10x0000000000452f60TRONGthời gian chạy.systemstack_switch
- Tại/chúng ta/địa phương/go/src/runtime/asm_amd64.s:350
- 20x000000000042c530TRONGruntime.fatalthrow
- Tại/chúng ta/địa phương/go/src/runtime/panic.go:1250
- 30x000000000042c2f1TRONGthời gian chạy.ném
- Tại/chúng ta/địa phương/go/src/runtime/panic.go:1198
- 40x000000000043fa76TRONGthời gian chạy.sigpanic
- Tại/chúng ta/địa phương/go/src/runtime/signal_unix.go:742
- 50x000000000045b81aTRONGchủ yếu.Biến đổi
- Tại./main.go:21
- 60x000000000045b81aTRONGtay.tay
- Tại./main.go:25
- 70x000000000042e9c7TRONGthời gian chạy. chính
- Tại/chúng ta/địa phương/go/src/runtime/proc.go:255
- 80x0000000000453361TRONGthời gian chạy.goexit
- Tại/chúng ta/địa phương/go/src/runtime/asm_amd64.s:1581
- (dlv)
Hàm tìm thấy mã lỗi được tìm thấy thông qua 5 0x000000000045b81a trong main.Modify và khung lệnh 5 được thực thi để nhập mã cụ thể của hàm.
- (dlv)khung5
- >runtime.raise()/usr/địa phương/go/src/runtime/sys_linux_amd64.s:165(PC:0x454bc1)
- Cảnh báo: gỡ lỗi được tối ưu hóachức năng
- Khung 5:./main.go:21(PC:45b81a)
- 16:}
- 17:
- 18: chức năngBiến đổi(){
- 19:a:="Xin chào"
- 20:b:=Chuỗi2Byte(a)
- =>21:b[0]='H'
- 22:}
- 23:
- 24:funcmain(){
- 25:Biến đổi()
- 26:}
- (dlv)
Kể từ đó, vụ việc đã được giải quyết và vấn đề nằm ở việc sửa đổi trái phép giá trị cơ bản của chuỗi.
Mac không dùng được
Một điều cần lưu ý là ví dụ về tạo kết xuất lõi ở trên được thực hiện trong hệ thống Linux, nhưng nó không thể thực hiện được trên hệ thống Mac amd64 (điều đó rất khó chịu và khiến tôi mất hai đêm làm việc).
Điều này là do Go trong hệ thống mac giới hạn việc tạo các tệp kết xuất lõi. Điều này được giải thích trong mã nguồn Go src/runtime/signal_unix.go.
- //đi:nosplit
- hàm
- //OSXcoredumps là lineardumpscủabộ nhớ được ánh xạ,
- //từcáiĐầu tiênbyte ảoĐẾNcáicuối cùng,vớisố khôngTRONGcác khoảng trống.
- //Bởi vìcủathewaywearrangetheaddresskhông gianTRÊN64-chúthệ thống,
- //điều này có nghĩa là tệp OSXcore sẽ> 128GBVàthậm chíTRÊNnhanh nhẹn
- //workstationcantakeOSXwelloverangiờĐẾNviết (không bị gián đoạn).
- //Lưu người dùngtừmắc phải sai lầm đó.
- nếuGOOS=="darwin"&&ĐÁNH GIÁ=="amd64"{
- trở lại
- }
-
- theFromSignal(_SIGABRT)
- }
Tóm tắt
Tệp kết xuất lõi là một công cụ mạnh mẽ được hệ điều hành cung cấp cho chúng ta. Nó là một ảnh chụp nhanh bộ nhớ được tạo khi chương trình kết thúc bất ngờ. Sử dụng kết xuất lõi, chúng tôi có thể khôi phục hiện trường sự cố tốt hơn sau khi chương trình gặp sự cố và hỗ trợ khắc phục sự cố.
Tất nhiên, việc tạo các tệp kết xuất lõi cũng có những nhược điểm. Tệp kết xuất lõi lớn. Nếu bản thân dịch vụ trực tuyến chiếm nhiều bộ nhớ thì chi phí bộ nhớ và thời gian để tạo tệp kết xuất lõi sẽ rất lớn. Ngoài ra, chúng tôi thường sắp xếp các trình nền dịch vụ. Nếu chương trình của chúng tôi gặp sự cố và khởi động lại thường xuyên, một số lượng lớn tệp kết xuất lõi sẽ được tạo ra (với bộ quy tắc đặt tên lõi+pid), dẫn đến nguy cơ ổ đĩa bị đầy (nếu kernel được phát hành) Giới hạn ulimit -c không giới hạn).
Cuối cùng, nếu chúng tôi lo lắng rằng nhật ký lỗi không thể giúp chúng tôi xác định các vấn đề về mã Go, chúng tôi có thể kích hoạt chức năng kết xuất lõi cho nó và thêm những điều bất ngờ vào hotfix. Đối với các dịch vụ có daemon, nên đặt giới hạn kích thước ulimt -c.
Liên kết gốc: https://mp.weixin.qq.com/s/RktnMydDtOZFwEFLLYzlCA.
Cuối cùng, bài viết về cách bảo toàn hiện trường sự cố chương trình Go kết thúc tại đây. Nếu bạn muốn biết thêm về cách bảo toàn hiện trường sự cố chương trình Go, vui lòng tìm kiếm các bài viết của CFSDN hoặc tiếp tục duyệt các bài viết liên quan. trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!