0

I am using ubuntu 18.04 LTS.

I am embedding python to C++ for uploading logs to azure application insights. My code worked well with python3.6 but now support is not available for python3.6 for the python core team. So I am trying to use a higher version of python for my code, but it causes a segmentation fault when repetitive calls to py_Initialize() and py_Finalize() are made. If py_Finalize() is called only once, there is no crash, but the logs are not uploaded to the cloud. I want to keep the application running.

Install Python & App Insight Dependencies: a) sudo apt-get update b) sudo apt install python3.6 (or use a higher version) c) python3 -V (Use to check python3 version) d) sudo apt-get install python3-dev e) sudo apt-get install libpython3.6-dev f) sudo apt-get install python3-pip h) sudo apt install rustc i) sudo -H pip3 install setuptools_rust g) sudo -H pip3 install opencensus-ext-azure applicationinsights

Code Sample:

#include <stdio.h>
#include <Python.h>
#include <iostream>
#include <string>
#include <stdint.h>

void CallAppInsightUploadFunction();

int main()
{

    for (int i = 0; i <= 5; i++)
    {
        Py_Initialize();
        CallAppInsightUploadFunction();
        std::cout << "Loop count: " + std::to_string(i) << std::endl;
        Py_Finalize();
    }

    printf("\nGood Bye...\n");
    return 0;
}

void CallAppInsightUploadFunction()
{

    PyRun_SimpleString("import sys");

    PyRun_SimpleString("if not hasattr(sys, 'argv'):  sys.argv  = ['']");

    PyRun_SimpleString("import logging");

    PyRun_SimpleString("from opencensus.ext.azure.log_exporter import AzureLogHandler");

    PyRun_SimpleString("logger = logging.getLogger(__name__)");

    PyRun_SimpleString("logger.addHandler(AzureLogHandler(connection_string='InstrumentationKey=<YOUR-INSTRUMENTATION-KEY>'))");
    PyRun_SimpleString("logger.setLevel(logging.INFO)");
    PyRun_SimpleString("logger.info('Testing AppInsight Uploads from VM...')");
}

CMakeLists.txt file: cmake_minimum_required(VERSION 3.13) project(AppInsightTest)

find_package(PythonLibs 3 REQUIRED) include_directories(include) include_directories(${PYTHON_INCLUDE_DIRS})

message("Python Include directory:") message("${PYTHON_INCLUDE_DIRS}")

message("Python Library:") message("${PYTHON_LIBRARIES}")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

include_directories(${CMAKE_SOURCE_DIR}/include)

file(GLOB SOURCES src/*.cpp)

add_executable(AppInsightTest ${SOURCES})

target_link_libraries(AppInsightTest PRIVATE ${PYTHON_LIBRARIES} )

Output when using python3.6: Loop count: 0 context.c:55: warning: mpd_setminalloc: ignoring request to set MPD_MINALLOC a second time

Loop count: 1 context.c:55: warning: mpd_setminalloc: ignoring request to set MPD_MINALLOC a second time

Loop count: 2 context.c:55: warning: mpd_setminalloc: ignoring request to set MPD_MINALLOC a second time

Loop count: 3 context.c:55: warning: mpd_setminalloc: ignoring request to set MPD_MINALLOC a second time

Loop count: 4 context.c:55: warning: mpd_setminalloc: ignoring request to set MPD_MINALLOC a second time

Loop count: 5

Good Bye...

Expected output:

When I run the same code with a python version higher than 3.6, the code should work and give the same output as above. I use the following flag in cmake command when using a higher python version(python 3.8): -DPYTHON_LIBRARY=/usr/lib/aarch64-linux-gnu/libpython3.8.so -DPYTHON_INCLUDE_DIR=/usr/include/python3.8

Gaurang
  • 11
  • 1

1 Answers1

1

I found solution for the issue I was facing.

Isuue as per my understandings: My concern was to upload data to cloud on every iteration. I was calling Py_Initialize() and Py_FinalizeEx() in order to do that.

But with python versions higher than 3.6, memory for external library was not getting freed correctly. That is why when it comes to the second iteration in for loop and tries to import it again it was causing segmentation fault.

Workaround that worked for me: Rather than using Py_Initialize() and Py_FinalizeEx() multiple times in a running application, I should use: PyRun_SimpleString("handler.flush()");

It pushes data to cloud at every iteration in the for loop. Now I can call Py_Initialize() and Py_FinalizeEx() only once in my code as the data are pushed to the cloud.

Note: It takes some time for your data to get reflected on the portal.

Modified Code which works fine

#include <stdio.h>
#include <Python.h>
#include <iostream>
#include <string>
#include <stdint.h>
#include <thread>

void CallAppInsightUploadFunction();

int main()
{
Py_Initialize();

PyRun_SimpleString("import sys");

PyRun_SimpleString("if not hasattr(sys, 'argv'):  sys.argv  = ['']");

PyRun_SimpleString("import logging");

PyRun_SimpleString("from opencensus.ext.azure.log_exporter import AzureLogHandler");

PyRun_SimpleString("logger = logging.getLogger(__name__)");
PyRun_SimpleString("handler = AzureLogHandler(connection_string='InstrumentationKey=<INSTRUMENTATION-KEY>')");
PyRun_SimpleString("logger.addHandler(handler)");
PyRun_SimpleString("logger.setLevel(logging.INFO)");

for (int i = 0; i <= 5; i++)
{
    CallAppInsightUploadFunction();
    std::cout << "Loop count: " + std::to_string(i) << std::endl;
    // Uncomment following lines to verify that logs are being uploaded when the application is running
    std::cout << "Device is going to sleep for 120 seconds..." << std::endl;
    sleep(120);
    std::cout << "Device wake-up from sleep..." << std::endl;
    std::cout << "Please check the logs on web portal" << std::endl;
}
Py_FinalizeEx();

printf("\nGood Bye...\n");
return 0;
}

void CallAppInsightUploadFunction()
{
PyRun_SimpleString("logger.info('Testing AppInsight Uploads...')");
PyRun_SimpleString("handler.flush()");
}

Output Log of modified code ~/AppInsightTest/build/bin$ sudo ./AppInsightTest Loop count: 0 Device is going to sleep for 120 seconds... Device wake-up from sleep... Please check the logs on web portal Loop count: 1 Device is going to sleep for 120 seconds... Device wake-up from sleep... Please check the logs on web portal Loop count: 2 Device is going to sleep for 120 seconds... Device wake-up from sleep... Please check the logs on web portal Loop count: 3 Device is going to sleep for 120 seconds... Device wake-up from sleep... Please check the logs on web portal Loop count: 4 Device is going to sleep for 120 seconds... Device wake-up from sleep... Please check the logs on web portal Loop count: 5 Device is going to sleep for 120 seconds... Device wake-up from sleep... Please check the logs on web portal

Good Bye... ~/AppInsightTest/build/bin$

Snapshot of log from web portal: Web portal log

Gaurang
  • 11
  • 1
  • Thanks so much - very helpful! in my case I have a Go application/service (that uses the Python C interface) that was just randomly crashing with SIGSEGV. I was eventually able to figure out it happened consistently if I invoked python more than once in a single session. Is this documented somewhere? – Tim Feb 28 '23 at 03:11