Torch流程之内存泄漏篇

4 篇文章 0 订阅

问题:

硬件报了个手电筒开关500次之后,无法打开,测量硬件对应的GPIO口一直被拉低。只有重新开机才可以恢复

我本地写了个脚本复现了几遍发现是必现的问题,在开关这个操作执行1000次左右是必现的。

脚本本内容:

#!/bin/bash

cnt=0

while true
do
        sleep 0.8
        adb shell input tap 338 298
        echo cnt = $cnt
        let cnt++
done

分析过程:

从我抓的log的时间点看是出现了out of memory,出现一次之后后面操作手电筒,都是在报这个错误,只有重新开机才会恢复。

06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][CSL    ] camxcslhwinternal.cpp:3239 CSLHwInternalDefaultIoctl2() Ioctl returned -1, failed for device /dev/video0 (Type:CSLHwRequestManager, FD:10, Index:-1)with error reason Out of memory
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][CSL    ] camxcslhw.cpp:2474 CSLAllocHW() Allocating CSL Buffer failed for len = 32
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][MEMMGR ] camxcmdbuffermanager.cpp:417 InitializePool() Buffer Info 0xb40000770a2849d0, CamxResult: 1
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][UTILS  ] camxcmdbuffermanager.cpp:435 InitializePool() Out of memory
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][CORE   ] camxnode.cpp:5287 CreateCmdBufferManager() Failed to Create command Buffer Manager(CBM_TorchPipeline_(null)0_FlashRERCmdManager)
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][SENSOR ] camxtorchnode.cpp:324 ProcessingNodeFinalizeInitialization() Fail to create FlashRERCmdManager
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][HAL    ] camxsession.cpp:2204 InitializeNewPipelines() FinalizePipeline(0) failed!
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][HAL    ] camxchisession.cpp:79 Create() CHISession Initialize failed!
06-04 06:24:29.953 24817 24858 E CamX    : [ERROR][CORE   ] camxsession.cpp:690 Flush() Flush is called before session 0xb40000761a657110 is initialized
06-04 06:24:29.954 24817 24858 I CamX    : [CONFIG][CORE   ] camxsession.cpp:1118 Destroy() Session (0xb40000761a657110) Destroy
06-04 06:24:29.954 24817 24858 E CamX    : [ERROR][CHI    ] camxchicontext.cpp:4110 CreateSession() Unable to create session

看到这个报错之后就感觉是发生了内存泄漏,在对应的kernel也可以发现对应的报错。

06-04 06:24:17.629 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
06-04 06:24:19.399 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
06-04 06:24:21.150 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
06-04 06:24:29.968 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12

我们平时hal使用的buffer其实是通过hal层的buffer pool、buffer manager去申请释放的,但是真正申请释放的地方是在kernel。

申请:

 static int32_t cam_mem_get_slot(void)
 {   
     int32_t idx;
     
     mutex_lock(&tbl.m_lock);
     idx = find_first_zero_bit(tbl.bitmap, tbl.bits);
     if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
         mutex_unlock(&tbl.m_lock);
         return -ENOMEM;
     }   
     
     set_bit(idx, tbl.bitmap);
     tbl.bufq[idx].active = true;
     mutex_init(&tbl.bufq[idx].q_lock);
     mutex_unlock(&tbl.m_lock);
     
     return idx;
 }

释放:

int cam_mem_mgr_release(struct cam_mem_mgr_release_cmd *cmd)
{     
    int idx;     
    int rc = 0;     
     
    if (!atomic_read(&cam_mem_mgr_state)) {  
        CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized");
        return -EINVAL;     
    }    
     
    if (!cmd) {     
        CAM_ERR(CAM_MEM, "Invalid argument");
        return -EINVAL;     
    }    
     
    idx = CAM_MEM_MGR_GET_HDL_IDX(cmd->buf_handle);
    if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) { 
        CAM_ERR(CAM_MEM, "Incorrect index %d extracted from mem handle",
            idx);
        return -EINVAL;
    }    
     
    if (!tbl.bufq[idx].active) {
        CAM_ERR(CAM_MEM, "Released buffer state should be active");
        return -EINVAL;
    }    
     
    if (tbl.bufq[idx].buf_handle != cmd->buf_handle) {
        CAM_ERR(CAM_MEM,
            "Released buf handle %d not matching within table %d, idx=%d",
            cmd->buf_handle, tbl.bufq[idx].buf_handle, idx);
        return -EINVAL;
    }    
     
    CAM_DBG(CAM_MEM, "Releasing hdl = %x, idx = %d", cmd->buf_handle, idx);
     
    if (kref_put(&tbl.bufq[idx].krefcount, cam_mem_util_unmap))
        CAM_DBG(CAM_MEM,
            "Called unmap from here, buf_handle: %u, idx: %d", 
            cmd->buf_handle, idx);
     
    return rc;
}

其实buffer要整整的释放是需要执行cam_mem_util_unmap这个函数,但是要执行这个函数需要满足if (kref_put(&tbl.bufq[idx].krefcount, cam_mem_util_unmap))这个条件,这里就要简单介绍一下kref这个相关的函数

kerf的浅析:

struct kref结构体是一个引用计数器,它被嵌套进其它的结构体中,记录所嵌套结构的引用计数。引用计数用于检测内核中有多少地方使用了某个对象,每当内核的一个部分需要某个对象所包含的信息时,则该对象的引用计数加1,如果不需要相应的信息,则对该对象的引用计数减1,当引用计数为0时,内核知道不再需要该对象,将从内存中释放该对象。

struct kref {
    refcount_t refcount;
};

对应的函数:

kref_init(struct kref *kref) 会初始化为1

kref_read(const struct kref *kref) 返回当前引用的次数

kref_get(struct kref *kref) 对引用的次数进行+1的操作

kref_put(struct kref *kref, void (*release)(struct kref *kref)) 对引用的次数进行-1的操作,当引用计数器的值为0时,则调用release函数

通过打印log发现打log发现在打开手电筒和关闭手电筒在hal调用的个数和次数的流程是没有问题的。

#define CAM_MEM_BUFQ_MAX 1024 

struct cam_mem_table {
     struct mutex m_lock;
     void *bitmap;   
     size_t bits;   
     struct cam_mem_buf_queue bufq[CAM_MEM_BUFQ_MAX];
     struct dentry *dentry;   
     bool alloc_profile_enable;
     size_t dbg_buf_idx;
     bool force_cache_allocs;
 #if IS_REACHABLE(CONFIG_DMABUF_HEAPS)
     struct dma_heap *system_heap;
     struct dma_heap *system_uncached_heap;
     struct dma_heap *camera_heap;
     struct dma_heap *camera_uncached_heap;
     struct dma_heap *secure_display_heap;
 #endif   
};

 这是kernel对应的memory定义,有个结构体数组存放1024个单元,如果申请的数量超过1024个就会出现问题

在kernel对应的get 和 unmap对应的个数对应不上,打开的时候会申请9个buffer,在释放的时候只释放了8个buffer。

在前面的地方提到过kref_put这个函数要krefcount这个数值为0才会去调用unmap这个函数。我在调用前后发现有一个buffer在退出的时候krefcount这个数值不为0,说明在执行的过程中,对buffer的引用操作出现了异常。

因为这个只使用了flash的硬件,所以只需要在flash的驱动里面去排查操作krefcount的地方。最后添加log定位到是有个地方使用buffer的时候只krefcount进行了+1的操作,使用完成之后没有进行-1的操作。导致退出的时候这个bufer的krefcount异常,没有进行正常的关闭。

static int32_t cam_flash_platform_probe(struct platform_device *pdev)
{
    fctrl->func_tbl.parser = cam_flash_i2c_pkt_parser;
    fctrl->func_tbl.apply_setting = cam_flash_i2c_apply_setting;
    fctrl->func_tbl.power_ops = cam_flash_i2c_power_ops;
    fctrl->func_tbl.flush_req = cam_flash_i2c_flush_request;
}

static int32_t cam_flash_driver_cmd(struct cam_flash_ctrl *fctrl,
        void *arg, struct cam_flash_private_soc *soc_private)
{ 
    case CAM_CONFIG_DEV: {
        CAM_DBG(CAM_FLASH, "CAM_CONFIG_DEV");
        rc = fctrl->func_tbl.parser(fctrl, arg);
        if (rc) {             
            CAM_ERR(CAM_FLASH, "Failed Flash Config: rc=%d\n", rc);
            goto release_mutex;
        }    
        break;
}

int cam_flash_i2c_pkt_parser(struct cam_flash_ctrl *fctrl, void *arg)
{
    cam_mem_get_cpu_buf(config.packet_handle,&generic_ptr, &len_of_buffer); //会对引用的数值+1
    cam_mem_put_cpu_buf(config.packet_handle); //会对引用的数值-1
}

经过这个问题对hal的buffer manager是有很大的联系的,krefcount这个定义在hal是有对应上的,这个buffer的流转在手电筒还是比较简单的,后面有机会的话打算对camera预览场景这些把buffer流转研究一下。

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值