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

时间:2019-07-12 23:06:24

标签: c++ linux cmake protocol-buffers

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

基本上,我现在正尝试在protobuf中使用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://stackoverflow.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://stackoverflow.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 $ENV{USER})
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://stackoverflow.com/questions/20824194/cmake-with-google-protocol-buffers
# https://stackoverflow.com/questions/32647517/protocol-buffer-with-cmakelists
# https://stackoverflow.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 个答案:

答案 0 :(得分:0)

如果要使用set_allocated_XXX,则不能使用具有局部作用域的对象,因为您通过该函数传递了对象的所有权。在第一个迭代结束时,robdata超出范围。因此它将被销毁。由于它是有效负载的所有者,因此也会销毁/删除有效负载。

代替:

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);
}