This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1352R: Memory leak of simple_broadcaster example

Part Number: CC1352R

Hi Team,

SDK version: 4.30

My customer observed a memory leak of simple_broadcaster example, and it can be reproduced by the following steps:

1.Add GAP_ADV_EVT_MASK_END event:

// Set event mask
status = GapAdv_setEventMask(advHandleLegacy,
                    GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END |  
                    GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                     GAP_ADV_EVT_MASK_SET_TERMINATED);

2.Add "case GAP_EVT_ADV_END" in SimpleBroadcaster_processAdvEvent:

static bool SimpleBroadcaster_processAdvEvent(sbGapAdvEventData_t *pEventData)
{
        bool safeToDealloc = TRUE;

        static volatile uint8_t test;

       switch (pEventData->event)
       {
                 case GAP_EVT_ADV_START_AFTER_ENABLE:
                               Display_printf(dispHandle, 2, 0, "Advertising");
                               break;

                 case GAP_EVT_INSUFFICIENT_MEMORY:
                              safeToDealloc = FALSE;
                              break;


                 case GAP_EVT_ADV_END:
                             test++;
                             break;


                default:
                         // Do nothing.
                            break;
       }

       return(safeToDealloc);
}

Please check the whole source code of simple_broadcaster.c here: 

/******************************************************************************

 @file  simple_broadcaster.c

 @brief This file contains the Simple Broadcaster sample application for
        use with the CC2650 Bluetooth Low Energy Protocol Stack.

 Group: WCS, BTS
 Target Device: cc13x2_26x2

 ******************************************************************************
 
 Copyright (c) 2011-2020, Texas Instruments Incorporated
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 *  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 *  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

 *  Neither the name of Texas Instruments Incorporated nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 ******************************************************************************
 
 
 *****************************************************************************/

/*********************************************************************
 * INCLUDES
 */

#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Queue.h>
#include <ti/display/Display.h>

#include "util.h"
#include <icall.h>
/* This Header file contains all BLE API and icall structure definition */
#include <icall_ble_api.h>
#include <devinfoservice.h>

#ifdef USE_RCOSC
#include "rcosc_calibration.h"
#endif //USE_RCOSC

#include <ti_drivers_config.h>
#include "ti_ble_config.h"
#include "simple_broadcaster.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

// Task configuration
#define SB_TASK_PRIORITY                     1

#ifndef SB_TASK_STACK_SIZE
#define SB_TASK_STACK_SIZE                   1024
#endif

#define SB_ADV_EVT                           1

// Internal Events for RTOS application
#define SB_ICALL_EVT                         ICALL_MSG_EVENT_ID // Event_Id_31
#define SB_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30

#define SB_ALL_EVENTS                        (SB_ICALL_EVT | \
                                               SB_QUEUE_EVT)

// Spin if the expression is not true
#define SIMPLEBROADCASTER_ASSERT(expr) if (!(expr)) HAL_ASSERT_SPINLOCK;
/*********************************************************************
 * TYPEDEFS
 */

// App event passed from stack modules. This type is defined by the application
// since it can queue events to itself however it wants.
typedef struct
{
  uint8_t event;                // event type
  void    *pData;               // pointer to message
} sbEvt_t;

/*********************************************************************
 * GLOBAL VARIABLES
 */

// Display Interface
Display_Handle dispHandle = NULL;

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

// Entity ID globally used to check for source and/or destination of messages
static ICall_EntityID selfEntity;

// Event globally used to post local events and pend on system and
// local events.
static ICall_SyncHandle syncEvent;

// Queue object used for app messages
static Queue_Struct appMsgQueue;
static Queue_Handle appMsgQueueHandle;

// Task configuration
Task_Struct sbTask;
#if defined __TI_COMPILER_VERSION__
#pragma DATA_ALIGN(sbTaskStack, 8)
#else
#pragma data_alignment=8
#endif
uint8_t sbTaskStack[SB_TASK_STACK_SIZE];

// Container to store advertising event data when passing from advertising
// callback to app event. See the respective event in GapAdvScan_Event_IDs
// in gap_advertiser.h for the type that pBuf should be cast to.
typedef struct
{
  uint32_t event;
  void *pBuf;
} sbGapAdvEventData_t;

// Advertising handles
static uint8 advHandleLegacy;

// Address mode
static GAP_Addr_Modes_t addrMode = DEFAULT_ADDRESS_MODE;

/*********************************************************************
 * LOCAL FUNCTIONS
 */

static void     SimpleBroadcaster_init(void);
static void     SimpleBroadcaster_taskFxn(UArg a0, UArg a1);

static void     SimpleBroadcaster_processStackMsg(ICall_Hdr *pMsg);
static void     SimpleBroadcaster_processGapMessage(gapEventHdr_t *pMsg);
static void     SimpleBroadcaster_processAppMsg(sbEvt_t *pMsg);
static void     SimpleBroadcaster_advCallback(uint32_t event, void *pBuf, uintptr_t arg);
static bool     SimpleBroadcaster_processAdvEvent(sbGapAdvEventData_t *pEventData);
static status_t SimpleBroadcaster_enqueueMsg(uint8_t event, void *pData);

/*********************************************************************
 * PROFILE CALLBACKS
 */

// GAP Role Callbacks
/*
static gapRolesCBs_t simpleBroadcaster_BroadcasterCBs =
{
  SimpleBroadcaster_stateChangeCB   // Profile State Change Callbacks
};
*/
/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      SimpleBroadcaster_createTask
 *
 * @brief   Task creation function for the Simple Broadcaster.
 *
 * @param   none
 *
 * @return  none
 */
void SimpleBroadcaster_createTask(void)
{
  Task_Params taskParams;

  // Configure task
  Task_Params_init(&taskParams);
  taskParams.stack = sbTaskStack;
  taskParams.stackSize = SB_TASK_STACK_SIZE;
  taskParams.priority = SB_TASK_PRIORITY;

  Task_construct(&sbTask, SimpleBroadcaster_taskFxn, &taskParams, NULL);
}

/*********************************************************************
 * @fn      SimpleBroadcaster_init
 *
 * @brief   Initialization function for the Simple Broadcaster App
 *          Task. This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notification ...).
 *
 * @param   none
 *
 * @return  none
 */
static void SimpleBroadcaster_init(void)
{
	// ******************************************************************
  // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
  // ******************************************************************
  // Register the current thread as an ICall dispatcher application
  // so that the application can send and receive messages.
  ICall_registerApp(&selfEntity, &syncEvent);

#ifdef USE_RCOSC
  // Set device's Sleep Clock Accuracy
#if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )
  HCI_EXT_SetSCACmd(500);
#endif // (CENTRAL_CFG | PERIPHERAL_CFG)
  RCOSC_enableCalibration();
#endif // USE_RCOSC

  // Create an RTOS queue for message from profile to be sent to app.
  appMsgQueueHandle = Util_constructQueue(&appMsgQueue);

  // Open LCD
  dispHandle = Display_open(Display_Type_ANY, NULL);

  // Register with GAP for HCI/Host messages. This is needed to receive HCI
  // events. For more information, see the HCI section in the User's Guide:
  // http://software-dl.ti.com/lprf/ble5stack-latest/
  GAP_RegisterForMsgs(selfEntity);

  //Initialize GAP layer for Peripheral role and register to receive GAP events
  GAP_DeviceInit(GAP_PROFILE_BROADCASTER, selfEntity, addrMode, &pRandomAddress);

  Display_printf(dispHandle, 0, 0, "BLE Broadcaster");
}

/*********************************************************************
 * @fn      SimpleBroadcaster_processEvent
 *
 * @brief   Application task entry point for the Simple Broadcaster.
 *
 * @param   none
 *
 * @return  none
 */
static void SimpleBroadcaster_taskFxn(UArg a0, UArg a1)
{
  // Initialize application
  SimpleBroadcaster_init();

  // Application main loop
  for (;;)
  {
    // Get the ticks since startup
    uint32_t tickStart = Clock_getTicks();

    uint32_t events;

    events = Event_pend(syncEvent, Event_Id_NONE, SB_ALL_EVENTS,
                        ICALL_TIMEOUT_FOREVER);

    if (events)
    {
      ICall_EntityID dest;
      ICall_ServiceEnum src;
      ICall_HciExtEvt *pMsg = NULL;

      if (ICall_fetchServiceMsg(&src, &dest,
                                (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
      {
        if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
        {
          // Process inter-task message
          SimpleBroadcaster_processStackMsg((ICall_Hdr *)pMsg);
        }

        if (pMsg)
        {
          ICall_freeMsg(pMsg);
        }
      }

      // If RTOS queue is not empty, process app message.
      if (events & SB_QUEUE_EVT)
      {
        while (!Queue_empty(appMsgQueueHandle))
        {
          sbEvt_t *pMsg = (sbEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
          if (pMsg)
          {
            // Process message.
            SimpleBroadcaster_processAppMsg(pMsg);

            // Free the space from the message.
            ICall_free(pMsg);
          }
        }
      }
    }
  }
}

/*********************************************************************
 * @fn      SimpleBroadcaster_processStackMsg
 *
 * @brief   Process an incoming stack message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void SimpleBroadcaster_processStackMsg(ICall_Hdr *pMsg)
{
  switch (pMsg->event)
  {
    case GAP_MSG_EVENT:
      SimpleBroadcaster_processGapMessage((gapEventHdr_t*) pMsg);
      break;

    default:
      // do nothing
      break;
  }
}

/*********************************************************************
 * @fn      SimpleBroadcaster_processGapMessage
 *
 * @brief   Process an incoming GAP event.
 *
 * @param   pMsg - message to process
 */
static void SimpleBroadcaster_processGapMessage(gapEventHdr_t *pMsg)
{
  switch(pMsg->opcode)
  {
    case GAP_DEVICE_INIT_DONE_EVENT:
    {
      bStatus_t status = FAILURE;

      gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;

      if(pPkt->hdr.status == SUCCESS)
      {
        // Store the system ID
        uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];

        // use 6 bytes of device address for 8 bytes of system ID value
        systemId[0] = pPkt->devAddr[0];
        systemId[1] = pPkt->devAddr[1];
        systemId[2] = pPkt->devAddr[2];

        // set middle bytes to zero
        systemId[4] = 0x00;
        systemId[3] = 0x00;

        // shift three bytes up
        systemId[7] = pPkt->devAddr[5];
        systemId[6] = pPkt->devAddr[4];
        systemId[5] = pPkt->devAddr[3];

        // Set Device Info Service Parameter
        DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);

        // Display device address
        Display_printf(dispHandle, 1, 0, "%s Addr: %s",
                       (addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
                       Util_convertBdAddr2Str(pPkt->devAddr));
        Display_printf(dispHandle, 2, 0, "Initialized");

        if (addrMode > ADDRMODE_RANDOM)
        {
          // Display the RPA.
          Display_printf(dispHandle, 3, 0, "RP Addr: %s",
                         Util_convertBdAddr2Str(GAP_GetDevAddress(FALSE)));
        }

        // Setup and start Advertising
        // For more information, see the GAP section in the User's Guide:
        // http://software-dl.ti.com/lprf/ble5stack-latest/

        #ifndef BEACON_FEATURE
          advParams1.eventProps = GAP_ADV_PROP_SCANNABLE | GAP_ADV_PROP_LEGACY;
        #else
          advParams1.eventProps = GAP_ADV_PROP_LEGACY;
        #endif // !BEACON_FEATURE

        // Create Advertisement set
        status = GapAdv_create(&SimpleBroadcaster_advCallback, &advParams1,
                               &advHandleLegacy);
        SIMPLEBROADCASTER_ASSERT(status == SUCCESS);

        // Load advertising data
        status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
                                     sizeof(advData1), advData1);
        SIMPLEBROADCASTER_ASSERT(status == SUCCESS);

        // Load scan response data
        status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
                                     sizeof(scanResData1), scanResData1);
        SIMPLEBROADCASTER_ASSERT(status == SUCCESS);

        // Set event mask
        status = GapAdv_setEventMask(advHandleLegacy,
                                     GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END|
                                     GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
                                     GAP_ADV_EVT_MASK_SET_TERMINATED);
        SIMPLEBROADCASTER_ASSERT(status == SUCCESS);

        // Enable legacy advertising
        status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
        SIMPLEBROADCASTER_ASSERT(status == SUCCESS);
      }

      break;
    }

    default:
      Display_clearLine(dispHandle, 2);
      break;
  }
}

/*********************************************************************
 * @fn      SimpleBroadcaster_enqueueMsg
 *
 * @brief   Creates a message and puts the message in RTOS queue.
 *
 * @param   event - message event.
 * @param   state - message state.
 */
static status_t SimpleBroadcaster_enqueueMsg(uint8_t event, void *pData)
{
  uint8_t success;
  sbEvt_t *pMsg = ICall_malloc(sizeof(sbEvt_t));

  // Create dynamic pointer to message.
  if(pMsg)
  {
    pMsg->event = event;
    pMsg->pData = pData;

    // Enqueue the message.
    success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg);
    return (success) ? SUCCESS : FAILURE;
  }

  return(bleMemAllocError);
}

/*********************************************************************
 * @fn      SimpleBroadcaster_advCallback
 *
 * @brief   GapAdv module callback
 *
 * @param   pMsg - message to process
 */
static void SimpleBroadcaster_advCallback(uint32_t event, void *pBuf, uintptr_t arg)
{
  sbGapAdvEventData_t *pData = ICall_malloc(sizeof(sbGapAdvEventData_t));

  if (pData)
  {
    pData->event = event;
    pData->pBuf = pBuf;

    if(SimpleBroadcaster_enqueueMsg(SB_ADV_EVT, pData) != SUCCESS)
    {
      ICall_free(pData);
    }
  }
}

/*********************************************************************
 * @fn      SimpleBroadcaster_processAppMsg
 *
 * @brief   Process an incoming callback from a profile.
 *
 * @param   pMsg - message to process
 *
 * @return  None.
 */
static void SimpleBroadcaster_processAppMsg(sbEvt_t *pMsg)
{
  bool safeToDealloc = TRUE;

  switch (pMsg->event)
  {
    case SB_ADV_EVT:
      safeToDealloc = SimpleBroadcaster_processAdvEvent((sbGapAdvEventData_t*)(pMsg->pData));
      break;

    default:
      // Do nothing.
      break;
  }

  if (safeToDealloc)
  {
    // Free message data
    if (pMsg->pData)
    {
      ICall_free(pMsg->pData);
    }
  }
}

/*********************************************************************
 * @fn      SimpleBroadcaster_processAdvEvent
 *
 * @brief   Process advertising event in app context
 *
 * @param   pEventData
 */
static bool SimpleBroadcaster_processAdvEvent(sbGapAdvEventData_t *pEventData)
{
  bool safeToDealloc = TRUE;

  static volatile uint8_t test;

  switch (pEventData->event)
  {
    case GAP_EVT_ADV_START_AFTER_ENABLE:
      Display_printf(dispHandle, 2, 0, "Advertising");
      break;

    case GAP_EVT_INSUFFICIENT_MEMORY:
      safeToDealloc = FALSE;
      break;
    case GAP_EVT_ADV_END:
      test++;
      break;
    default:
      // Do nothing.
      break;
  }

  return(safeToDealloc);
}

/*********************************************************************
*********************************************************************/

3.Add breakpoints in line "test++;"

4.Everytime hits the breakpoint, we'll see a memory leak, please check the images below:

  • Hi Viki

    I will have someone looking into this for you.

    BR

    Siri

  • Is there any update? Any suggestion that related to this problem is appreciated.

  • Hi Viki,

    Thank you for your patience. I get assigned to this thread only this morning.

    The problem seems to be due to not properly freeing the buffer passed within pEventData. 

    Here is a code snippet that should solve your issue:

    static bool SimpleBroadcaster_processAdvEvent(sbGapAdvEventData_t *pEventData)
    {
            bool safeToDealloc = TRUE;
    
            static volatile uint8_t test;
    
           switch (pEventData->event)
           {
                     case GAP_EVT_ADV_START_AFTER_ENABLE:
                                   Display_printf(dispHandle, 2, 0, "Advertising");
                                   break;
    
                     case GAP_EVT_INSUFFICIENT_MEMORY:
                                  safeToDealloc = FALSE;
                                  break;
    
    
                     case GAP_EVT_ADV_END:
                                 test++;
                                 ICall_free(pEventData->pBuf); // fixed memory leak
                                 break;
    
    
                    default:
                             // Do nothing.
                                break;
           }
    
           return(safeToDealloc);
    }

    Best regards,

  • Hi Clément,

    Thanks a lot!

  • Hi Viki - I think you're still going to leak memory, just not as fast.  I found the same memory leak occurred for the events:

    • GAP_EVT_ADV_START_AFTER_ENABLE
    • GAP_EVT_ADV_END_AFTER_DISABLE
    • GAP_EVT_ADV_START
    • GAP_EVT_ADV_END

    Basically all the cases where gap.h says to cast pBuf as a uint8_t to find the advertising handle, what they meant was that pBuf is a pointer to a uint8_t holding the advertising handle and you are responsible for freeing it.  So the example you're showing still leaks a byte from the GAP_EVT_ADV_START_AFTER_ENABLE case.

    I'm glad someone else has had the same issue, I recorded my experience here: e2e.ti.com/.../3600553

  • Hi James,

    Thanks for your note, it's really helpful:)