当前位置: 首页 > news >正文

【go/方法记录】cgo静态库编译以及使用dlv定位cgo崩溃问题

目录

  • 说在前面
  • 文件树
  • 静态库编译
  • cgo使用
  • 崩溃模拟
  • 使用dlv定位崩溃
  • 参考

说在前面

  • 测试环境:WSL2
  • go版本:go version go1.23.1 linux/amd64
  • gcc版本:gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  • cmake版本:3.22.1

文件树

├── buffer # go package
│   ├── buffer.go
│   ├── buffer_go.h
│   └── libbuffer.a
├── c # c/c++源代码
│   ├── CMakeLists.txt # cmake
│   ├── buffer # c/c++源代码
│   │   ├── buffer.h
│   │   ├── buffer_go.cpp
│   │   └── buffer_go.h
│   ├── lib # 静态库目录
│   │   └── libbuffer.a
│   └── main.cpp
└── main.go

静态库编译

  • 这部分和go无关,按照正常的静态库编译走就行,这里我使用cmake进行编译
  • buffer.h
    #ifndef __BUFFER_H__
    #define __BUFFER_H__
    #include <string>struct Buffer {std::string* s_;Buffer(int size) {this->s_ = new std::string(size, char('\0'));}~Buffer() {delete this->s_;}int Size() const {return this->s_->size();}char* Data() {return (char*)this->s_->data();}
    };#endif
    
  • 然后我们需要对其进行封装,这部分可以参考这里
  • buffer_go.h
    #define DLLIMPORT#ifdef __cplusplus
    extern "C" {
    #endiftypedef struct Buffer_T Buffer_T;Buffer_T* NewBuffer(int size);
    void DeleteBuffer(Buffer_T* p);
    int BufferSize(Buffer_T* p);#ifdef __cplusplus
    }
    #endif
    
  • buffer_go.cpp
    #include "buffer.h"
    #include "buffer_go.h"#ifdef __cplusplus
    extern "C" {
    #endifstruct Buffer_T: Buffer {Buffer_T(int size): Buffer(size) {}~Buffer_T() {}
    };DLLIMPORT Buffer_T* NewBuffer(int size) {auto p = new Buffer_T(size);return p;
    }DLLIMPORT void DeleteBuffer(Buffer_T* p) {delete p;
    }DLLIMPORT int BufferSize(Buffer_T* p) {return p->Size();
    }#ifdef __cplusplus
    }
    #endif
    
  • 然后就是cmake
    cmake_minimum_required(VERSION 3.2)
    project(Buffer)
    set(CMAKE_BUILD_TYPE "RELEASE")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")# 设置编译后库文件目录
    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)include_directories(${PROJECT_SOURCE_DIR}/buffer)
    # 添加编译可执行文件
    aux_source_directory(${PROJECT_SOURCE_DIR}/buffer SRC)add_library(buffer STATIC ${SRC})
    add_executable(buffer_exemain.cpp)# 链接主程序
    target_link_libraries(buffer_exePRIVATEbuffer)
    
  • 执行编译
    cd ./c
    cmake .
    make
    
    动态库将生成在目录./c/lib

cgo使用

  • 首先将buffer_go.hlibbuffer.a拷贝到./buffer目录下,并创建buffer.go文件。注意,以下注释中的#include "buffer_go.h"import "C"不要有空行
  • buffer.go
    package buffer// #cgo CFLAGS: -I.
    // #cgo CXXFLAGS: -std=c++11
    // #cgo LDFLAGS: -L./ -lbuffer -lstdc++
    //
    // #include "buffer_go.h"
    import "C"type BufferT C.Buffer_Tfunc NewBuffer(size int) *BufferT {p := C.NewBuffer(C.int(size))return (*BufferT)(p)
    }func DeleteBuffer(p *BufferT) {C.DeleteBuffer((*C.Buffer_T)(p))
    }func BufferSize(p *BufferT) C.int {return C.BufferSize((*C.Buffer_T)(p))
    }
    
  • 然后是main.go
    package mainimport ("fmt"
    )func main() {b := buffer.NewBuffer(2)fmt.Println(buffer.BufferSize(b))buffer.DeleteBuffer(b)
    }
    
  • 执行程序输出
    ~/cgotest$ go run main.go
    2
    

崩溃模拟

  • 正常go中的崩溃,例如空指针调用等是可以通过recover处理的,例如

    func main() {go func() {defer func() {if err := recover(); err != nil {fmt.Println("err: ", string(debug.Stack()))}}()time.Sleep(5 * time.Second)var b uint32var a uint32_ = b / a}()var i intfor {time.Sleep(time.Second)i++fmt.Println(i)}
    }
    

    可以看到进程可以继续运行

    ~/cgotest$ go run main.go
    1
    2
    3
    4
    err:  goroutine 18 [running]:
    runtime/debug.Stack()/usr/local/go/src/runtime/debug/stack.go:26 +0x5e
    main.main.func1.1()/home/xx/cgotest/main.go:14 +0x2a
    panic({0x49cb60?, 0x54d840?})/usr/local/go/src/runtime/panic.go:785 +0x132
    main.main.func1()/home/xx/cgotest/main.go:23 +0x3f
    created by main.main in goroutine 1/home/xx/cgotest/main.go:11 +0x1a5
    6
    
  • 而对于cgo中的崩溃,目前go是没法进行recover的,会导致进程直接崩溃

    package mainimport ("cgotest/cgotest/buffer""fmt""runtime/debug""time"
    )func main() {go func() {defer func() {if err := recover(); err != nil {fmt.Println("err: ", string(debug.Stack()))}}()time.Sleep(5 * time.Second)b := buffer.NewBuffer(2)buffer.DeleteBuffer(b)b = nilfmt.Println(buffer.BufferSize(b))}()var i intfor {time.Sleep(time.Second)i++fmt.Println(i)}
    }
    
    ~/cgotest$ go run main.go
    1
    2
    3
    4
    SIGSEGV: segmentation violation
    PC=0x4930b4 m=5 sigcode=1 addr=0x0
    signal arrived during cgo executiongoroutine 6 gp=0xc000007dc0 m=5 mp=0xc000100008 [syscall]:
    runtime.cgocall(0x492550, 0xc000076750)goroutine 6 gp=0xc000007dc0 m=5 mp=0xc000100008 [syscall]:
    runtime.cgocall(0x492550, 0xc000076750)/usr/local/go/src/runtime/cgocall.go:167 +0x4b fp=0xc000076728 sp=0xc0000766f0 pc=0x462f8b
    cgotest/cgotest/buffer._Cfunc_BufferSize(0x0)_cgo_gotypes.go:52 +0x47 fp=0xc000076750 sp=0xc000076728 pc=0x475b87
    main.main.func1.BufferSize.3(0x0)/home/xx/cgotest/buffer/buffer.go:22 +0x3b fp=0xc000076788 sp=0xc000076750 pc=0x4923fb
    cgotest/cgotest/buffer.BufferSize(...)/home/xx/cgotest/buffer/buffer.go:22
    main.main.func1()/home/xx/cgotest/main.go:29 +0x6c fp=0xc0000767e0 sp=0xc000076788 pc=0x49234c
    runtime.goexit({})/usr/local/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0000767e8 sp=0xc0000767e0 pc=0x46e601
    created by main.main in goroutine 1/home/xx/cgotest/main.go:12 +0x1a
    
  • 可以看到进程退出并输出了堆栈信息

使用dlv定位崩溃

  • 虽然崩溃时的堆栈信息有标准输出,但是有时标准输出中的堆栈信息可能会被覆盖掉,少了最前面的信息,这个时候可以开启coredump并使用dlv来定位
  • 启用coredump
    ulimit -c unlimited
    GOTRACEBACK=crash go run main.go
    
    在进程崩溃后会生成coredump文件
  • 查看coredump文件目录
    ~/cgotest$ cat /proc/sys/kernel/core_pattern
    /mnt/wslg/dumps/core.%e
    
    可以看到文件生成在/mnt/wslg/dumps/
    ~/cgotest$ ll /mnt/wslg/dumps/
    total 6684
    -rw------- 1 le   le   83562496 Sep 21 16:04 core.main
    
  • 安装dlv
    go install github.com/go-delve/delve/cmd/dlv@latest
    
    可以在gopath文件夹下找到
    ~/cgotest$ /home/xx/go/bin/
    dlv          gopls        staticcheck
    
  • 使用
    # dlv core 二进制程序 coredump文件
    ~/go/bin/dlv core cgotest /mnt/wslg/dumps/core.main 
    
    结果如下
    ~/cgotest$ ~/go/bin/dlv core cgotest /mnt/wslg/dumps/core.main 
    Type 'help' for list of commands.
    (dlv) bt0  0x000000000046fde1 in runtime.raiseat /usr/local/go/src/runtime/sys_linux_amd64.s:1541  0x000000000044b625 in runtime.dieFromSignalat /usr/local/go/src/runtime/signal_unix.go:9422  0x000000000044bc86 in runtime.sigfwdgoat /usr/local/go/src/runtime/signal_unix.go:11543  0x000000000044a625 in runtime.sigtrampgoat /usr/local/go/src/runtime/signal_unix.go:4324  0x000000000046fde1 in runtime.raiseat /usr/local/go/src/runtime/sys_linux_amd64.s:1535  0x000000000044b625 in runtime.dieFromSignalat /usr/local/go/src/runtime/signal_unix.go:9426  0x000000000044b1a6 in runtime.crashat /usr/local/go/src/runtime/signal_unix.go:1031
    Sending output to pager...0  0x000000000046fde1 in runtime.raiseat /usr/local/go/src/runtime/sys_linux_amd64.s:1541  0x000000000044b625 in runtime.dieFromSignalat /usr/local/go/src/runtime/signal_unix.go:9422  0x000000000044bc86 in runtime.sigfwdgoat /usr/local/go/src/runtime/signal_unix.go:11543  0x000000000044a625 in runtime.sigtrampgoat /usr/local/go/src/runtime/signal_unix.go:4324  0x000000000046fde1 in runtime.raiseat /usr/local/go/src/runtime/sys_linux_amd64.s:1535  0x000000000044b625 in runtime.dieFromSignalat /usr/local/go/src/runtime/signal_unix.go:9426  0x000000000044b1a6 in runtime.crashat /usr/local/go/src/runtime/signal_unix.go:10317  0x000000000044b1a6 in runtime.sighandlerat /usr/local/go/src/runtime/signal_unix.go:8068  0x000000000044a726 in runtime.sigtrampgoat /usr/local/go/src/runtime/signal_unix.go:4909  0x00000000004930b4 in ???at ?:-1
    10  0x000000000049256b in C._cgo_a4c0d8419d5e_Cfunc_BufferSizeat /tmp/go-build/buffer.cgo2.c:55
    11  0x000000000046e284 in runtime.asmcgocallat /usr/local/go/src/runtime/asm_amd64.s:923
    12  0x0000000000000000 in ???at ?:-1
    13  0x0000000000462fb5 in runtime.cgocallat /usr/local/go/src/runtime/cgocall.go:185
    14  0x0000000000475b87 in cgotest/cgotest/buffer._Cfunc_BufferSizeat _cgo_gotypes.go:52
    15  0x00000000004923fb in main.main.func1.BufferSize.3at ./buffer/buffer.go:22
    16  0x0000000000492345 in cgotest/cgotest/buffer.BufferSizeat ./buffer/buffer.go:22
    17  0x0000000000492345 in main.main.func1at ./main.go:29
    18  0x000000000046e601 in runtime.goexitat /usr/local/go/src/runtime/asm_amd64.s:1700
    

参考

  • dlv安装

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • CNN网络训练WISDM数据集:模型仿真及可视化分析
  • 三光吊舱详解!
  • C++之 string(中)
  • Arthas vmoption(查看和修改 JVM里诊断相关的option)
  • 企业身份安全管理面临的问题和解决方案
  • 活动策划灵感TOP10分享-华媒舍
  • 分布式框架 - ZooKeeper
  • 技术周总结 09.16~09.22 周日(架构 C# 数据库)
  • EfficientNet(2019):基于复合缩放的自动化架构搜索高效网络!
  • 通过WinCC在ARMxy边缘计算网关上实现智能运维
  • python-比较月亮大小/数组下标/人见人爱a+b
  • RK3568笔记六十一:MIPI摄像头应用程序编写
  • 使用 Flask-Limiter 和 Nginx 实现接口访问次数限制
  • 网站建设中常见的网站后台开发语言有哪几种,各自优缺点都是什么?
  • SYN Flood攻击原理,SYN Cookie算法
  • SegmentFault for Android 3.0 发布
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 30秒的PHP代码片段(1)数组 - Array
  • HTML-表单
  • iOS 颜色设置看我就够了
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • MYSQL 的 IF 函数
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue-router 实现分析
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 对JS继承的一点思考
  • 规范化安全开发 KOA 手脚架
  • 技术:超级实用的电脑小技巧
  • 前嗅ForeSpider教程:创建模板
  • 实现简单的正则表达式引擎
  • 最近的计划
  • 【干货分享】dos命令大全
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • ​水经微图Web1.5.0版即将上线
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (13):Silverlight 2 数据与通信之WebRequest
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (done) 两个矩阵 “相似” 是什么意思?
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (转)scrum常见工具列表
  • (转)关于多人操作数据的处理策略
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .gitignore不生效的解决方案
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .net mvc 获取url中controller和action
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .NET简谈设计模式之(单件模式)
  • .NET企业级应用架构设计系列之结尾篇
  • /boot 内存空间不够
  • /var/log/cvslog 太大
  • ::什么意思