Thứ Năm, 25 tháng 10, 2018

Sơ suất do viết code rườm rà khiến chương trình bị crash

Lesson learn.

Trong quá trình viết một tính năng mới cho sản phẩm tôi đang làm tôi đã gặp phải một lỗi nhỏ nhưng gây hậu quả nghiêm trọng cho ứng dụng của tôi. Chính vì vậy nên tôi viết lại bài này để mô tả việc tôi đã tìm và sửa lỗi như thế nào.

Chuyện là tôi đang viết một ứng dụng định kỳ (cho sản phẩm tôi đang làm) thực hiện tác vụ kiểm tra kết nối giữa hai thiết bị để bật tắt đèn LED trên thiết bị của tôi theo từng trại thái kết nối tương ứng.
Khi tôi hoàn thành việc viết code demo và chạy thử thì bỗng dưng phát hiện ứng dụng của tôi chỉ chạy được lần đầu lần chạy tiếp theo nó đã bỗng dưng chết bất đắc kỳ tử với một mớ log của kernel bắn ra như sau:

ledwifictl:error:67.867:checkPledwifictl/512: potentially unexpected fatal signal 11.

Cpu 0
$ 0   : 00000000 10008400 00000000 ffff0000
$ 4   : 00000001 00400000 00000000 00000001
$ 8   : 00000000 fffffffe 00000000 6b486f73
$12   : 2066756e 00000800 00000400 6374696f
$16   : 00400000 7fa75a14 00400000 00400000
$20   : 7fa75d60 00400000 00401f5c 00409c08
$24   : 00000003 77758570                 
$28   : 0041a120 7fa759e8 004096a8 0040137c
Hi    : 00000001
Lo    : 00000000
epc   : 004014a4 0x4014a4
    Tainted: P           
ra    : 0040137c 0x40137c
Status: 00008413    USER EXL IE
Cause : 00000008
BadVA : 00000008
PrId  : 00029033 (Broadcom BMIPS3300)
airedHostWiFi:242:NBQ: ledctl PAIR on

 ở trong chương trình mà tôi viết có hai hàm thực hiện hai chức năng khác nhau, một hàm là checkPairedHostWiFi() dùng để kiểm tra xem thiết bị WiFi của tôi đã kết nối với Router được chưa nhằm bật/tắt LED. Hàm còn lại là hàm checkHostWiFiConnecting() thực hiện quét sóng WiFi của router để hiển thị đèn LED tương ứng với giá trị RSSI của sóng WiFi của Router đang kết nối tới.
Sau khi thấy lỗi xuất hiện với dòng log "ledwifictl:error:67.867:checkPledwifictl/512: potentially unexpected fatal signal 11." tôi cứ chắc mẫm là lỗi phải nằm trong hàm checkPairedHostWiFi() thế là hì  hục đặt log vào các vị trí trong hàm này để tìm lỗi. Tuy nhiên sau rất nhiều lần build firmware thay đổi các vị trí đặt log tôi vẫn chưa thể tìm ra nguyên nhân. Chán nản, tôi cảm thấy nghi ngờ dự đoán của mình. Vậy là tôi quyết định comment lời gọi hàm của hàm này và build lại firmware để chạy thử thì thấy lỗi vẫn còn đó. Từ đó tôi đã chuyển nghi ngờ của mình sang hàm checkHostWiFiConnecting() và tiến hành comment lời gọi hàm này và kết quả đã như dự đoán. Lỗi đích thị là do hàm này. Tôi tiến hành nhìn vào hàm mà mình đã viết, ôi thôi thật là rối rắm.


static int checkHostWiFiConnecting(void)
{
    FILE *pFile;
    char line[256] = {0};
    char cmd[64] = {0};
    char *result = NULL;
    WlVirtIntfCfgObject *intfObj = NULL;
    WlBaseCfgObject *wlBaseCfgObj = NULL;
    char rssi[4] = {0};
    int rssi_int = 0;

    if ((cmsLck_acquireLock()) != CMSRET_SUCCESS)
    {
        cmsLog_error("failed to get lock");
        cmsLck_dumpInfo ();
    }
    else
    {
        if ((getWlBaseCfgObjParams(&wlBaseCfgObj)) != CMSRET_SUCCESS)
        {
            cmsLck_releaseLock();
            return 0;
        }

        if ((1 != wlBaseCfgObj->wlEnbl) || (1 != wlBaseCfgObj->wlEnableUre))
        {
            cmsLck_releaseLock();
            return 0;
        }
        else
        {
            if (CMSRET_SUCCESS != getWlVirtIntfCfgObjParams(1, &intfObj))
            {
                cmsLog_error("Failed to get intfObj1");
                cmsObj_free((void **) &wlBaseCfgObj);
                cmsLck_releaseLock();
                return 0;
            }
            else
            {
                if (intfObj->wlEnblSsid == 0)
                {
                    cmsLck_releaseLock();
                    return 0;
                }
            }
        }
    }
    cmsLck_releaseLock(); /*Thì ra lỗi là do tôi thiếu dòng này cho nên chương trình chưa thực hiện release lock dẫn đến lần gọi hàm sau chương trình bị crash ngay lập tức*/
    sprintf(cmd, "wlctl scan --ssid=%s", intfObj->wlSsid);
    prctl_runCommandInShellBlocking ((char*)cmd);
    sleep(1);
    prctl_runCommandInShellBlocking ("wl scanresults > /var/tmp/wl_scanresults 2> /dev/null");

    cmsObj_free((void **) &wlBaseCfgObj); /*don't need it anymore*/
    cmsObj_free((void **) &intfObj); /*don't need it anymore*/
    pFile = fopen("/var/tmp/wl_scanresults", "r");
    if (!pFile)
    {
        cmsLog_error("Error open file.\n");
    }
    else
    {
        while (fgets(line, 256, pFile) != NULL)
        {
            result = strstr(line, "RSSI:");
            if (result != NULL )
            {
                sscanf(result, "RSSI: %s", rssi);
                fclose(pFile);
                rssi_int = atoi(rssi);
                if (rssi_int > -50)
                {
                    prctl_runCommandInShellBlocking ("ledctl NET on");
                }
                else if ((rssi_int < -50) || (rssi_int > -60))
                {
                    prctl_runCommandInShellBlocking ("ledctl NET blink");
                }
                else
                {
                    prctl_runCommandInShellBlocking ("ledctl NET off");
                }
                return 1;
            }
            else
            {
                continue;
            }
        }
        fclose(pFile);
    }
    return 0;
}
Tôi đọc lại hàm này thật kỹ thì phát hiện rằng do đoạn code rối rắm mà tôi đã quên thực hiện release lock cho trường hợp đọc các tham số thành công dẫn đến chương trình khi chạy lần thứ hai nó lại tiếp tục thực hiện requireLock và gây crash chương trình.
Vậy là chỉ một sơ suất nhỏ đã khiến tối mất khá nhiều thời gian không đáng có để sửa lỗi cho chương trình của mình.



Sơ suất do viết code rườm rà khiến chương trình bị crash

Lesson learn. Trong quá trình viết một tính năng mới cho sản phẩm tôi đang làm tôi đã gặp phải một lỗi nhỏ nhưng gây hậu quả nghiêm trọng ...