protobuf“oneof”子protobuf对象指针杀死程序

Posted

技术标签:

【中文标题】protobuf“oneof”子protobuf对象指针杀死程序【英文标题】:protobuf "oneof" sub-protobuf object pointer kills program 【发布时间】:2019-07-12 23:06:24 【问题描述】:

我已经成功使用 google protobufs 一个半月了,我遇到了一个我无法克服的问题。

基本上,我现在正在尝试在 protobufs 中使用 oneof 功能,我可以在单个消息中包含多种类型的消息。基本上,我只需要能够发送 1 条消息,但包含多个不同的子消息选项。我发现很多 SO 帖子都推荐了 oneof 功能,所以现在我正在尝试使用它。

https://developers.google.com/protocol-buffers/docs/proto#oneof

好的,那么问题是什么? 好吧,我的代码得到了一个巨大的堆栈转储。这是多个代码文件。 这是正在崩溃的.cpp 脚本:

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <thread>
// First, we need to import the main message proto file.
#include "Robotmessage.pb.h"
// Then we include the sub-message
#include "SimpleController.pb.h"
// This is for cout.
#include <iostream>
// Google
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
// https://***.com/questions/1641182/how-can-i-catch-a-ctrl-c-event
#include <signal.h>

using namespace google::protobuf::io;
using namespace std;

// Ctrl+C handler
void my_handler(int s)
    printf("Caught signal %d\n",s);
    exit(1); 


// simple wrapper for code cleanup 
void sleepApp(int ms)

    std::this_thread::sleep_for(std::chrono::milliseconds(ms));


int main(int argc, char** argv)

    // https://developers.google.com/protocol-buffers/docs/cpptutorial
    // Verify that the version of the library that we linked against is
    // compatible with the version of the headers we compiled against.
    GOOGLE_PROTOBUF_VERIFY_VERSION;

    // SIGINT registration
    struct sigaction sigIntHandler;
    sigIntHandler.sa_handler = my_handler;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;
    sigaction(SIGINT, &sigIntHandler, NULL);

    // If an event is found, allow for printing.
    bool eventupdate = false;

    // For debugging
    int printcounter = 0;

    // I would like to create the object here instead...
    //Robotmessage robotdata;

    // This is the joystick part of the message
    Simplecontroller payload;

    string joystickname = "joy_1";
    const int joystickid = 12345;

    // Joystick characteristics
    payload.set_name(joystickname);
    payload.set_id(joystickid);
    // Joystick axis doubles
    payload.set_leftstickx(0);
    payload.set_leftsticky(0);
    payload.set_rightstickx(0);
    payload.set_rightsticky(0);
    payload.set_lefttrigger(0);
    payload.set_righttrigger(0);
    // buttons
    payload.set_leftstickbutton(true);
    payload.set_rightstickbutton(true);
    //
    payload.set_xbutton(false);
    payload.set_abutton(false);
    payload.set_ybutton(true);
    payload.set_bbutton(true);
    //
    payload.set_rightbutton(false);
    payload.set_leftbutton(false);
    //
    payload.set_startbutton(true);
    payload.set_backbutton(false);
    payload.set_centbutton(true);
    //
    payload.set_pov(11);

    // Keep reading the state of the joystick in a loop
    while (true) 

        // Create the new payload object
        Robotmessage robotdata;
        Simplecontroller * pointeddata = &payload;

        // Now we add the joystick payload to the full data holder.
        robotdata.Clear();
        robotdata.set_msgheader("robot_drive");
        robotdata.set_allocated_heartbt(NULL);
        // This line works
        robotdata.set_allocated_control(NULL);
        // THIS LINE DOES NOT WORK!!!!!!!
        robotdata.set_allocated_control(pointeddata);
        // Show that the script worked.
        if (printcounter++ > 2000)
        
            cout << "I worked." << endl;
        
        // Now we clear the object
        //payload.Clear();
        // Delay so that the socket does not die for some odd reason.
        sleepApp(9); // milliseconds
    

    google::protobuf::ShutdownProtobufLibrary();
    return 0;

这是我正在使用的.proto 文件: Heartbeat.proto:


syntax = "proto3";

// protoc --cpp_out=./ SimpleController.proto

message Heartbeat 
    // Identifiers
    int32 beat = 1;

Robotmessage.proto:


syntax = "proto3";

// I need to import the other message definitions
import "Heartbeat.proto";
import "SimpleController.proto";

// protoc --cpp_out=./ SimpleController.proto

message Robotmessage 
    // Identifiers
    string msgheader = 1;
    oneof robmessage 
        Heartbeat heartbt = 2;  
        Simplecontroller control = 3;
    

SimpleController.proto:


syntax = "proto3";

// protoc --cpp_out=./ SimpleController.proto

message Simplecontroller 
    // https://***.com/questions/9496101/protocol-buffer-over-socket-in-c\

    // Identifiers
    string name = 1;
    int32 id = 2;
    // 2 axis throttles with integrated buttons
    double leftstickX = 3;
    double leftstickY = 4;
    bool leftstickbutton = 5;
    double rightstickX = 6;
    double rightstickY = 7;
    bool rightstickbutton = 8;
    // 2 extra throtle axis
    double lefttrigger = 9;
    double righttrigger = 10;
    // grid of 4 buttons
    bool xbutton = 11;
    bool abutton = 12;
    bool bbutton = 13;
    bool ybutton = 14;
    // buttons next to triggers.
    bool leftbutton  = 15;
    bool rightbutton = 16;
    // butons next to center logo.
    bool startbutton = 17;
    bool backbutton = 18;
    // The center button
    bool centbutton = 19;
    // The hat on the left
    int32 pov = 20;

这是我用来构建代码的CMakeLists.txt 文件:

set(PROJECT_NAME test_protobuf)
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
PROJECT( $PROJECT_NAME LANGUAGES C CXX )
message(STATUS "Is the C++ compiler loaded? $CMAKE_CXX_COMPILER_LOADED")
if(CMAKE_CXX_COMPILER_LOADED)
message(STATUS "The C++ compiler ID is: $CMAKE_CXX_COMPILER_ID")
message(STATUS "Is the C++ from GNU: $CMAKE_COMPILER_IS_GNUCCX")
message(STATUS "The C++ compiler version is: $CMAKE_CXX_COMPILER_Version")
endif()

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_definitions(-std=c++11)
# CMAKE_CURRENT_SOURCE_DIR - This is the directory where CMake is running.
set(CMAKE_BINARY_DIR $CMAKE_SOURCE_DIR/build)
set(EXECUTABLE_OUTPUT_PATH $CMAKE_BINARY_DIR)
set(LIBRARY_OUTPUT_PATH $CMAKE_BINARY_DIR/lib)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

find_package( Boost REQUIRED system thread timer chrono)
if(Boost_FOUND)
    message("Boost was found.")
    message($Boost_INCLUDE_DIR)
endif()

set(THE_USER $ENVUSER)
message("This is the com user_:" $THE_USER)

set(PROGRAMS_TO_COMPILE 
demobrokeprotobuf
)

set(PROTO_MESSAGES
Robotmessage.proto
SimpleController.proto
Heartbeat.proto
)

# These lines are for autogenerating code from the *.proto files with CMake.
find_package(Protobuf REQUIRED)
if(PROTOBUF_FOUND)
    message("Google Protobuf has been found.")
endif()

# Make sure protoc is present, as apparently the above find_package() doesn't check that.
find_program(Protobuf_PROTOC_LOC NAMES protoc)
if (NOT Protobuf_PROTOC_LOC)
  message(FATAL_ERROR "Cannot find required 'protoc', cannot process Protobuf files without it. Aborting.")
endif()

# https://cmake.org/cmake/help/git-master/module/FindProtobuf.html
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS $PROTO_MESSAGES)

foreach(aprogram $PROGRAMS_TO_COMPILE)
    add_executable($aprogram $PROTO_SRCS $PROTO_HDRS $PROJECT_SOURCE_DIR/$aprogram.cpp)
    target_link_libraries($aprogram $Boost_LIBRARIES)
    target_link_libraries($aprogram $PROTOBUF_LIBRARIES)
    # include directories
    # We only include the directories for these specific executables.
    target_include_directories($aprogram PRIVATE $Protobuf_INCLUDE_DIRS)
    target_include_directories($aprogram PRIVATE $CMAKE_CURRENT_BINARY_DIR)
endforeach()

# Links I have refered to
# https://cmake.org/examples/
# https://***.com/questions/20824194/cmake-with-google-protocol-buffers
# https://***.com/questions/32647517/protocol-buffer-with-cmakelists
# https://***.com/questions/38408486/how-to-build-google-protobuf-environment-with-cmake-on-windows

还有我用来编译所有内容的build.sh 文件:

#!/bin/bash
cmake -H. -Bbuild
availablethreads=`nproc`
cmake --build build -- -j$availablethreads -l$availablethreads

然后,最后,错误:

*** Error in `./build/demobrokeprotobuf': double free or corruption (out): 0x00007ffdad290950 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fd30e5267e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fd30e52f37a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fd30e53353c]
./build/demobrokeprotobuf[0x40733d]
./build/demobrokeprotobuf[0x403ce0]
./build/demobrokeprotobuf[0x4062fd]
./build/demobrokeprotobuf[0x403bbe]
./build/demobrokeprotobuf[0x40d114]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fd30e4cf830]
./build/demobrokeprotobuf[0x4035c9]
======= Memory map: ========
00400000-00413000 r-xp 00000000 103:03 13111091                          /home/$USER/demobrokenprotobuf/build/demobrokeprotobuf
00612000-00613000 r--p 00012000 103:03 13111091                          /home/$USER/demobrokenprotobuf/build/demobrokeprotobuf
00613000-00614000 rw-p 00013000 103:03 13111091                          /home/$USER/demobrokenprotobuf/build/demobrokeprotobuf
0148d000-014bf000 rw-p 00000000 00:00 0                                  [heap]
7fd308000000-7fd308021000 rw-p 00000000 00:00 0 
7fd308021000-7fd30c000000 ---p 00000000 00:00 0 
7fd30df8c000-7fd30e094000 r-xp 00000000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e094000-7fd30e293000 ---p 00108000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e293000-7fd30e294000 r--p 00107000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e294000-7fd30e295000 rw-p 00108000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e295000-7fd30e2ae000 r-xp 00000000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e2ae000-7fd30e4ad000 ---p 00019000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e4ad000-7fd30e4ae000 r--p 00018000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e4ae000-7fd30e4af000 rw-p 00019000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e4af000-7fd30e66f000 r-xp 00000000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e66f000-7fd30e86f000 ---p 001c0000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e86f000-7fd30e873000 r--p 001c0000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e873000-7fd30e875000 rw-p 001c4000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e875000-7fd30e879000 rw-p 00000000 00:00 0 
7fd30e879000-7fd30e891000 r-xp 00000000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30e891000-7fd30ea90000 ---p 00018000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30ea90000-7fd30ea91000 r--p 00017000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30ea91000-7fd30ea92000 rw-p 00018000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30ea92000-7fd30ea96000 rw-p 00000000 00:00 0 
7fd30ea96000-7fd30eaac000 r-xp 00000000 103:03 34896                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd30eaac000-7fd30ecab000 ---p 00016000 103:03 34896                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd30ecab000-7fd30ecac000 rw-p 00015000 103:03 34896                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd30ecac000-7fd30ee1e000 r-xp 00000000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30ee1e000-7fd30f01e000 ---p 00172000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30f01e000-7fd30f028000 r--p 00172000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30f028000-7fd30f02a000 rw-p 0017c000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30f02a000-7fd30f02e000 rw-p 00000000 00:00 0 
7fd30f02e000-7fd30f2cd000 r-xp 00000000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f2cd000-7fd30f4cd000 ---p 0029f000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f4cd000-7fd30f4d6000 r--p 0029f000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f4d6000-7fd30f4dc000 rw-p 002a8000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f4dc000-7fd30f4dd000 rw-p 00000000 00:00 0 
7fd30f4dd000-7fd30f503000 r-xp 00000000 103:03 31751                     /lib/x86_64-linux-gnu/ld-2.23.so
7fd30f6b5000-7fd30f6bb000 rw-p 00000000 00:00 0 
7fd30f700000-7fd30f702000 rw-p 00000000 00:00 0 
7fd30f702000-7fd30f703000 r--p 00025000 103:03 31751                     /lib/x86_64-linux-gnu/ld-2.23.so
7fd30f703000-7fd30f704000 rw-p 00026000 103:03 31751                     /lib/x86_64-linux-gnu/ld-2.23.so
7fd30f704000-7fd30f705000 rw-p 00000000 00:00 0 
7ffdad271000-7ffdad293000 rw-p 00000000 00:00 0                          [stack]
7ffdad306000-7ffdad309000 r--p 00000000 00:00 0                          [vvar]
7ffdad309000-7ffdad30b000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

我用$USER 替换了我的计算机用户帐户。 我已将问题缩小到这一行:

// THIS LINE DOES NOT WORK!!!!!!!
robotdata.set_allocated_control(pointeddata);

我试图将一个 protobuf 对象传递给另一个 protobuf 指针对象,这完全使程序崩溃。我不知道为什么会崩溃。我已经从我的程序中删除了所有额外的代码:这是演示我的错误的最低要求。我不知道如何解决这个问题。

我还要提一下:如果您还没有弄清楚如何使用 google protobuf 进行 CMake,这是我一直使用的通用格式。恕我直言,这是一个很好的资源。 :)

【问题讨论】:

【参考方案1】:

如果您想使用set_allocated_XXX,则不能使用具有本地范围的对象,因为您通过该函数传递对象的所有权。在您的第一次迭代结束时, robdata 超出范围。所以它会被摧毁。由于它是payload的所有者,它也会销毁/删除payload。

代替:

Simplecontroller payload;
// Keep reading the state of the joystick in a loop
while (true) 

   // Create the new payload object
   Robotmessage robotdata;
   Simplecontroller * pointeddata = &payload;
   // THIS LINE DOES NOT WORK!!!!!!!
   robotdata.set_allocated_control(pointeddata);

试试:

// Keep reading the state of the joystick in a loop
while (true) 

   Simplecontroller* payload = new Simplecontroller();
   // Create the new payload object
   Robotmessage robotdata;
   // THIS LINE SHOULD WORK!!!!!!!
   robotdata.set_allocated_control(payload);

【讨论】:

在阅读了您的帖子和代码之后,我认为您的意思是:“...具有 global 范围的对象...”。无论如何,这完美地解决了我的问题。我猜谷歌的家伙希望我们 cpp 开发人员事先了解所有这些。哈哈谢谢伙计

以上是关于protobuf“oneof”子protobuf对象指针杀死程序的主要内容,如果未能解决你的问题,请参考以下文章

golang使用protobuf中的oneof

Protobuf-net - 如何使用 oneof

.proto 文件中带有 oneof 的 Protobuf-net

为啥protobuf序列化“oneof”消息使用if-else

ProtoBuf练习

如果子消息没有字段,如何在 protobuf 消息上分配 oneof 字段?