Memory leak with cJson library

Hi everyone,

I'm collecting some data from the adxl362 accelerometer and from the env. sensor and sending them over MQTT.

This is the main.c file:

#include <zephyr.h>
#include <cJSON.h>
#include <stdio.h>
#include <drivers/uart.h>
#include <string.h>
#include <random/rand32.h>
#include <net/mqtt.h>
#include <net/socket.h>
#include <modem/at_cmd.h>
#include <modem/lte_lc.h>
#include <logging/log.h>
#include <dk_buttons_and_leds.h>
#include <cJSON_OS.h>
#include <date_time.h>
#include <time.h>
#include "env_sensors.c"
#include "accel.c"
#include "net_controller.c"
#include "certificates.h"


/*Sensor data structures*/
const struct device *env_sensor, *accel;

/*Message Buffers, counters & locks*/
char *buf1[10]; int counter_buf1; bool lock_buf1;
char *buf2[10]; int counter_buf2; bool lock_buf2;

int BUFFERING_TIMER_RELOAD_VALUE_MS;

/*String to contain time*/
static char time_string[80];
int64_t unix_time_ms = 0;



/*Button handler function - sends pre-defined message on publish configured topic */
#if defined(CONFIG_DK_LIBRARY)
static void button_handler(uint32_t button_states, uint32_t has_changed)
{
        if (has_changed & button_states &
	    BIT(CONFIG_BUTTON_EVENT_BTN_NUM - 1)){
              ;
              /*
              
              printk("Disconnecting MQTT client...\n");

              err = mqtt_disconnect(&client);
              if (err) {
                      printk("Could not disconnect MQTT client. Error: %d\n", err);
              }*/
	}
}
#endif




char *time_to_string(int64_t time_ms)
{

	time_t time = time_ms / 1000;
	struct tm local_time;
	local_time = *localtime(&time);
	strftime(time_string, 80, "%Y-%m-%d %H:%M:%S", &local_time);
	return time_string;

}


/* Date time function using NTP Server */

struct k_work datetime_work;
struct k_timer datetime_timer;

void get_datetime_work_handler(){

	int err = 0;
        
        date_time_update_async(NULL);
        err = date_time_now(&unix_time_ms);

        // Check if we have successfully acquired a timestamp, and print it out if we have
        if(err != 0){
            printk("Error getting date_time (%i)!\n", err);
        }

}

K_WORK_DEFINE(datetime_work, get_datetime_work_handler);

void datetime_expires(struct k_timer *timer_id) {
	k_work_submit(&datetime_work);
}

K_TIMER_DEFINE(datetime_timer, datetime_expires, NULL);



/*Encode Json packet in string (all channels encoded)*/
char * cJson_encode(int env_sensor_data[], double accel_sensor_data[]){

    static char *string = NULL;
    cJSON *device_name = NULL;
    cJSON *timestamp = NULL;

    //create Json Object
    cJSON *packet = cJSON_CreateObject();

    //create device name
    device_name = cJSON_CreateString(CONFIG_MQTT_CLIENT_ID);
    //adding device name to root of json structure
    cJSON_AddItemToObject(packet, "device_name", device_name);

    //create timestamp
    if(unix_time_ms > 0 && time_to_string(unix_time_ms) != NULL){
      timestamp = cJSON_CreateString(time_to_string(unix_time_ms));
    }else {
      timestamp = cJSON_CreateString("timestamp placeholder");
    }
    cJSON_AddItemToObject(packet, "timestamp", timestamp);

    cJSON_AddNumberToObject(packet, "temp", env_sensor_data[0]);
    cJSON_AddNumberToObject(packet, "press", env_sensor_data[1]);
    cJSON_AddNumberToObject(packet, "humid", env_sensor_data[2]);
    cJSON_AddNumberToObject(packet, "gas", env_sensor_data[3]);

    cJSON_AddNumberToObject(packet, "accel_x", accel_sensor_data[0]);
    cJSON_AddNumberToObject(packet, "accel_y", accel_sensor_data[1]);
    cJSON_AddNumberToObject(packet, "accel_z", accel_sensor_data[2]);

    cJSON_AddNumberToObject(packet, "mov_avg_x", moving_average_x);
    cJSON_AddNumberToObject(packet, "mov_avg_y", moving_average_y);
    cJSON_AddNumberToObject(packet, "mov_avg_z", moving_average_z);

    string = cJSON_Print(packet);
    cJSON_Delete(packet);
      
    return string;

}


/*Work that reads measures and encodes data in json format to buf1 or buf2*/ 

struct k_work buffering_work;
struct k_timer buffering_timer;

void buffering_work_handler (struct k_work *work){

      int y;   

      static char * payload = NULL; 

      double accel_data_double[3];

      int env_data[4];


      //Getting data from accel.
      if (get_accel_data(accel, accel_data_double) == -1){
         LOG_ERR("Failed getting data from env. sensor");
      }
      

      //Getting data from env sensor
      if (get_env_sensor_data(env_sensor, env_data) == -1){
         LOG_ERR("Failed getting data from env. sensor");
      }
      
      
      payload = cJson_encode(env_data, accel_data_double);

      //Concurrent buffering on buf1 and buf2
      if(lock_buf1==false){

        buf1[counter_buf1] = payload;

        counter_buf1 = counter_buf1 + 1;
  
        if(counter_buf1 == 10){
          counter_buf2 = 0; 
          lock_buf1 = true;
          lock_buf2 = false;         
          for(y=0;y<10;y++){           
            free(buf2[y]);
         }
        }
      }

      if(lock_buf2 == false){

        
        buf2[counter_buf2] = payload;

        counter_buf2 = counter_buf2 + 1;
  
        if(counter_buf2 == 10){
          counter_buf1 = 0; 
          lock_buf1 = false;
          lock_buf2 = true;
          for(y=0;y<10;y++){
            free(buf1[y]);
          }
        }
      }
      
      if(payload != NULL)
        free(payload);


}


K_WORK_DEFINE(buffering_work, buffering_work_handler);

void buffering_expires(struct k_timer *timer_id) {
	k_work_submit(&buffering_work);
}

K_TIMER_DEFINE(buffering_timer, buffering_expires, NULL);




/*Work to keep alive MQTT connection and submit bufs*/ 

struct k_work submitting_work;
struct k_timer submitting_timer;

void submitting_work_handler(struct k_work *work){

    int err, ret, y;

    
    //Polling MQTT Socket waiting for event
    err = poll(&fds, 1, mqtt_keepalive_time_left(&client));
    if (err < 0) {
            LOG_ERR("poll: %d", errno);
    }

    //Upkeep the connection 
    err = mqtt_live(&client);
    if ((err != 0) && (err != -EAGAIN)) {
            LOG_ERR("ERROR: mqtt_live: %d", err);
    }

    //If event occurred and poll succed, call mqtt input
    if ((fds.revents & POLLIN) == POLLIN) {
            err = mqtt_input(&client);
            if (err != 0) {
                    LOG_ERR("mqtt_input: %d", err);
            }
    }

    //Error on polling from MQTT
    if ((fds.revents & POLLERR) == POLLERR) {
            LOG_ERR("POLLERR");
    }

    //Error on polling from MQTT
    if ((fds.revents & POLLNVAL) == POLLNVAL) {
            LOG_ERR("POLLNVAL");
    }

    if(lock_buf1==true){   
      for(y=0;y<10;y++){

            ret = data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE, buf1[y], strlen(buf1[y]), CONFIG_MQTT_PUB_D2C_TOPIC);
            if (ret)
              LOG_ERR("Publish failed on %s: %d", CONFIG_MQTT_PUB_D2C_TOPIC, ret);
          
          
      }
    }

    if(lock_buf2==true){   
      for(y=0;y<10;y++){
            ret = data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE, buf2[y], strlen(buf2[y]), CONFIG_MQTT_PUB_D2C_TOPIC);
            if (ret)
              LOG_ERR("Publish failed on %s: %d", CONFIG_MQTT_PUB_D2C_TOPIC, ret);
          
      }
    }

}



K_WORK_DEFINE(submitting_work, submitting_work_handler);

void submitting_expires(struct k_timer *timer_id) {
	k_work_submit(&submitting_work);
}

K_TIMER_DEFINE(submitting_timer, submitting_expires, NULL);





/*Log application boot info*/
void application_boot_logs(void){


  	LOG_INF("Starting MQTT connection and sampling data...");
        k_sleep(K_MSEC(500));

        LOG_INF("Network mode:");
        k_sleep(K_MSEC(500));
        if(IS_ENABLED(CONFIG_LTE_NETWORK_MODE_LTE)){
          LOG_INF("\t-LTE-M");
          k_sleep(K_MSEC(500));
        }
        if(IS_ENABLED(CONFIG_LTE_NETWORK_MODE_NBIOT)){
          LOG_INF("\t-NB-IoT");
          k_sleep(K_MSEC(500));
        }
        if(BROKER_HOSTNAME != NULL){
          LOG_INF("MQTT Broker Host: %s", BROKER_HOSTNAME);
          k_sleep(K_MSEC(500));
        }
        if(BROKER_PORT != 0){
          LOG_INF("MQTT Broker Port: %d", BROKER_PORT);
          k_sleep(K_MSEC(500));
        }
        if(CONFIG_MQTT_CLIENT_ID != NULL){
          LOG_INF("MQTT Client ID: %s", CONFIG_MQTT_CLIENT_ID);
          k_sleep(K_MSEC(500));
        }
        if(CONFIG_MQTT_PUB_D2C_TOPIC != NULL){
          LOG_INF("MQTT Publish Topic: %s", CONFIG_MQTT_PUB_D2C_TOPIC);
          k_sleep(K_MSEC(500));
        }
        if(CONFIG_MQTT_SUB_TOPIC != NULL){
          LOG_INF("MQTT Subscribe Topic: %s", CONFIG_MQTT_SUB_TOPIC);
          k_sleep(K_MSEC(500));
        }

        LOG_INF("Using Sensors:");
        k_sleep(K_MSEC(500));
        
        if(IS_ENABLED(CONFIG_ADXL362)){
          LOG_INF("\t-ADXL362 3-Axis Accelerometer");
          k_sleep(K_MSEC(500));
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_2G)){
            LOG_INF("\t\t-2G Range acceleration");
                      k_sleep(K_MSEC(500));

          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_4G)){
            LOG_INF("\t\t-4G Range acceleration");
                      k_sleep(K_MSEC(500));

          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_8G)){
            LOG_INF("\t\t-8G Range acceleration");
                      k_sleep(K_MSEC(500));

          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_RUNTIME)){
            LOG_INF("\t\t-Range Acceleration set on runtime");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_100)){
            LOG_INF("\t\t-Output data rate 100 Hz");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_12_5)){
            LOG_INF("\t\t-Output data rate 12.5 Hz");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_200)){
            LOG_INF("\t\t-Output data rate 200 Hz");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_400)){
            LOG_INF("\t\t-Output data rate 400 Hz");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_25)){
            LOG_INF("\t\t-Output data rate 25 Hz");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_50)){
            LOG_INF("\t\t-Output data rate 50 Hz");
                      k_sleep(K_MSEC(500));
          }
          if(IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_RUNTIME)){
            LOG_INF("\t\t-Output data rate set on runtime");
                      k_sleep(K_MSEC(500));
          }           

        }
        if(IS_ENABLED(CONFIG_BME680)){
          LOG_INF("\t-BME680 Environment Sensor: temp, humidity, pression, air_quality");
          k_sleep(K_MSEC(500));
        }

        LOG_INF("Sampling Period: %d ms", BUFFERING_TIMER_RELOAD_VALUE_MS);
          k_sleep(K_MSEC(500));
        LOG_INF("");
          k_sleep(K_MSEC(500));


}



static void config_init(void){

  
  //BUFFERING_TIMER_RELOAD_VALUE_MS = 1000; //DEBUG
  BUFFERING_TIMER_RELOAD_VALUE_MS = CONFIG_SAMPLING_FREQUENCY;

  #if defined(CONFIG_MQTT_BROKER)

    if(strcmp(CONFIG_MQTT_BROKER,"test_eclipse")==0){
      
      BROKER_HOSTNAME = CONFIG_MQTT_ECLIPSE_BROKER_HOSTNAME;
      BROKER_PORT = CONFIG_MQTT_ECLIPSE_BROKER_PORT;

    }

    if(strcmp(CONFIG_MQTT_BROKER, "AWS_IoT")==0){
      
      BROKER_HOSTNAME = CONFIG_MQTT_AWS_BROKER_HOSTNAME;
      BROKER_PORT = CONFIG_MQTT_AWS_BROKER_PORT;

    }
  #endif



}


void main(void){


      int err;
     
      lock_buf1 = false;
      lock_buf2 = true;
      counter_buf1 = 0;
      counter_buf2 = 0;

      config_init();

      application_boot_logs();


      //Memory leak if enabled
      //cJSON_Init();    //Initializing the cJSON library

      /*Stop the UART RX for power consumption reasons - Uncomment on production env.
      if (!IS_ENABLED(CONFIG_AT_HOST_LIBRARY)) {

		NRF_UARTE0_NS->TASKS_STOPRX = 1;
		NRF_UARTE1_NS->TASKS_STOPRX = 1;
	}*/

      #if defined(CONFIG_MQTT_LIB_TLS) && defined(CONFIG_CERTIFICATES_PROVISION)
          err = certificates_provision();
          if (err != 0) {
                  LOG_ERR("Failed to provision certificates");
                  return;
          }
      #endif 

      //Modem setup and init
      do {
          err = modem_configure();
          if (err) {
              LOG_INF("Retrying in %d seconds",
                      CONFIG_LTE_CONNECT_RETRY_DELAY_S);
              k_sleep(K_SECONDS(CONFIG_LTE_CONNECT_RETRY_DELAY_S));
          }
      } while (err);

      //Define button event handler if enabled
      #if defined(CONFIG_DK_LIBRARY)
              dk_buttons_init(button_handler);
      #endif

      //mqtt client init
      err = client_init(&client);
 
      if (err != 0) {
              LOG_ERR("client_init: %d", err);
              return;
      }

      //mqtt client connect
      err = mqtt_connect(&client);
      if (err != 0) {
              LOG_ERR("mqtt_connect %d", err);
              return;
      }

      //mqtt file descriptor init
      err = fds_init(&client);
      if (err != 0) {
              LOG_ERR("fds_init: %d", err);
              return;
      }
    
      //Env. sensor init
      if (init_env_sensor(&env_sensor) != 0)
          LOG_ERR("Failed to initialize environment sensors");

      //Accel. init
      if (init_accel(&accel) != 0)
          LOG_ERR("Failed to initialize accelerometer");


      k_sleep(K_MSEC(2000));

      /*Starting datetime timer: 
      read datetime every 1 sec */
      k_timer_start(&datetime_timer, K_SECONDS(1), K_SECONDS(1));  

      /*Starting buffering timer: 
      performs one shot buffer element fill every TIMER_RELOAD_VALUE_MS*/
      k_timer_start(&buffering_timer, K_MSEC(BUFFERING_TIMER_RELOAD_VALUE_MS), K_MSEC(BUFFERING_TIMER_RELOAD_VALUE_MS));


      /*Starting submitting timer: 
      performs a bulk submit either of buf1 or buf2 (10 elements)
      it lasts TIMER_RELOAD_VALUE_MS*20 and repeats every TIMER_RELOAD_VALUE_MS*10*/
      k_timer_start(&submitting_timer, K_MSEC(BUFFERING_TIMER_RELOAD_VALUE_MS*20), K_MSEC(BUFFERING_TIMER_RELOAD_VALUE_MS*10));

          
}

And this is the accel.c file where I get data from the accelerometer:

#include <zephyr.h>
#include <device.h>
#include <drivers/sensor.h>
#include <stdio.h>

double mov_avg_x_elements[10];
double mov_avg_y_elements[10];
double mov_avg_z_elements[10];
double moving_average_x, moving_average_y, moving_average_z; 
int mov_avg_counter; 
bool first_ten_flag;

uint8_t init_accel(const struct device **dev){
  
  /*binding device*/
  *dev = device_get_binding(DT_LABEL(DT_INST(0, adi_adxl362)));

  /*Init mov. average params*/
  moving_average_x = 0;
  moving_average_y = 0; 
  moving_average_z = 0;
  mov_avg_counter = 0;
  first_ten_flag = false;

  if(*dev == NULL){
    return -1;
  }
  else{
    return 0;
  }

}


/*Update moving average by 1 element*/
void moving_average_update(double mov_avg_element,  double *avg_array_ptr, double *moving_average_ptr){

    int i;
    double sum;
    
    avg_array_ptr[mov_avg_counter] = mov_avg_element;
       
    if(!first_ten_flag && mov_avg_counter==10){
      first_ten_flag = true;
    }

    if(first_ten_flag){
      sum = 0;
      for (i=0; i<10; i++){
        sum += avg_array_ptr[i];
      }
      *moving_average_ptr = sum/10;
    }

    if(mov_avg_counter == 10){
      mov_avg_counter = 0;
    }


}


//get double value 3-axis acceleration 
uint8_t get_accel_data(const struct device *dev, double accel_data_array[]){


          struct sensor_value accel_sensor_data[3];

          if (sensor_sample_fetch(dev) < 0) {
                  printf("Sample fetch error\n");
                  return -1;
          }
          
          if (sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &accel_sensor_data[0]) < 0) {
                  printf("Channel get error\n");
                  return -1;
          }

          if (sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &accel_sensor_data[1]) < 0) {
                  printf("Channel get error\n");
                  return -1;
          }

          if (sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &accel_sensor_data[2]) < 0) {
                  printf("Channel get error\n");
                  return -1;
          }

          
          for(int i=0; i<3; i++){
            accel_data_array[i] = sensor_value_to_double(&accel_sensor_data[i]);
          }

          moving_average_update(accel_data_array[0], mov_avg_x_elements, &moving_average_x);
          moving_average_update(accel_data_array[1], mov_avg_y_elements, &moving_average_y);
          moving_average_update(accel_data_array[2], mov_avg_z_elements, &moving_average_z);
          mov_avg_counter++;

          return 1;
	
        
}

Everything works fine if comment lines 132, 133, 134 on main.c (where I try to encode accel moving average values), but if I uncomment them occurs a databus error (on instruction cJSON_Print() - line 136 on main.c)  after some publishes on MQTT:

[00:00:32.851,684] [1;31m<err> os: ***** BUS FAULT *****[0m
[00:00:32.851,684] [1;31m<err> os:   Precise data bus error[0m
[00:00:32.851,684] [1;31m<err> os:   BFAR Address: 0x83638ead[0m
[00:00:32.851,715] [1;31m<err> os: r0/a1:  0x00000000  r1/a2:  0x2001a2d4  r2/a3:  0x20026d00[0m
[00:00:32.851,715] [1;31m<err> os: r3/a4:  0x636120f1 r12/ip:  0x78c00000 r14/lr:  0x0002b1db[0m
[00:00:32.851,715] [1;31m<err> os:  xpsr:  0x21020000[0m
[00:00:32.851,745] [1;31m<err> os: s[ 0]:  0x00000000  s[ 1]:  0x00000000  s[ 2]:  0x00000000  s[ 3]:  0x00000000[0m
[00:00:32.851,745] [1;31m<err> os: s[ 4]:  0x00000000  s[ 5]:  0x00000000  s[ 6]:  0xffffffff  s[ 7]:  0x00000000[0m
[00:00:32.851,776] [1;31m<err> os: s[ 8]:  0x00000000  s[ 9]:  0x00000000  s[10]:  0x00000000  s[11]:  0x00000000[0m
[00:00:32.851,776] [1;31m<err> os: s[12]:  0x00000000  s[13]:  0x00000000  s[14]:  0x00000000  s[15]:  0x00000000[0m
[00:00:32.851,776] [1;31m<err> os: fpscr:  0x00000002[0m
[00:00:32.851,776] [1;31m<err> os: Faulting instruction address (r15/pc): 0x0002b218[0m
[00:00:32.851,776] [1;31m<err> os: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0[0m
[00:00:32.851,806] [1;31m<err> os: Current thread: 0x20019748 (unknown)[0m
[00:00:33.496,154] [1;31m<err> fatal_error: Resetting system[0m

I suppose it is a memory leak happening because not correctly initializing/freeing some variables.

Could anyone help me with that?

Thank you,

Giuls

Related