labapart / gattlib

Library to access GATT information from BLE (Bluetooth Low Energy) devices
http://labapart.com/
454 stars 160 forks source link

gattlib_read_char_by_uuid memory leak #122

Open chengyingyuan opened 5 years ago

chengyingyuan commented 5 years ago

I installed gattlib from git repository, built on my ubuntu 18.04 x86_64. It runs well, except using memory very quickly and seems never free occupied memory.

My code as following:

while (!g_sigquit) { char* buf = nullptr; size_t buflen = 0; ret = gattlib_read_char_by_uuid(g_connection, &g_uuid, reinterpret_cast<void**>(&buf), &buflen); if (ret != GATTLIB_SUCCESS) { break; } printf("Read %lu bytes\n", buflen); free(buf); }

Valgrind output as following:

... ==14174== 85,120 bytes in 665 blocks are still reachable in loss record 1,730 of 1,732 ==14174== at 0x6312B00: g_type_create_instance (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==14174== by 0x62F3747: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==14174== by 0x62F55BF: g_object_new_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==14174== by 0x5FA1645: g_initable_new_valist (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x5FA1708: g_initable_new (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x4E5D62E: org_bluez_gatt_characteristic1_proxy_new_for_bus_sync (org-bluez-gattcharacteristic1.c:2549) ==14174== by 0x4E4CA86: handle_dbus_gattcharacteristic_from_path (gattlib_char.c:44) ==14174== by 0x4E4CE29: get_characteristic_from_uuid (gattlib_char.c:158) ==14174== by 0x4E4D322: gattlib_read_char_by_uuid (gattlib_char.c:294) ==14174== by 0x10B3CD: main (gattproxy.cpp:216) ==14174== ... ==14174== 742,448 (63,840 direct, 678,608 indirect) bytes in 570 blocks are definitely lost in loss record 1,732 of 1,732 ==14174== at 0x6312B00: g_type_create_instance (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==14174== by 0x62F3747: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==14174== by 0x62F55BF: g_object_new_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==14174== by 0x5FA1645: g_initable_new_valist (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x5FA1708: g_initable_new (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x6019338: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x6019F6C: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x601A67D: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x5FA1656: g_initable_new_valist (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x5FA1708: g_initable_new (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x601B1E6: g_dbus_object_manager_client_new_for_bus_sync (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==14174== by 0x4E4CD1E: get_characteristic_from_uuid (gattlib_char.c:131) ==14174== ==14174== LEAK SUMMARY: ==14174== definitely lost: 74,032 bytes in 933 blocks ==14174== indirectly lost: 982,160 bytes in 29,133 blocks ==14174== possibly lost: 2,464 bytes in 26 blocks ==14174== still reachable: 1,753,621 bytes in 37,619 blocks ==14174== of which reachable via heuristic: ==14174== length64 : 1,432 bytes in 22 blocks ==14174== newarray : 1,776 bytes in 31 blocks ==14174== suppressed: 0 bytes in 0 blocks ==14174==

Any hints please?

oliviermartin commented 5 years ago

Would you mind to also add the code for the connection to help me to potentially fix all memory leaks? I will try to have a look.

oliviermartin commented 5 years ago

Actually, I have just modified the example read_write to make a loop of 20 reads and used valgrind to check more memory leak.

I fixed them in this commit: https://github.com/labapart/gattlib/commit/51c6cbeee2c7af15347d40389dd9a05ac829f674

Here is the status:

==803== LEAK SUMMARY:
==803==    definitely lost: 0 bytes in 0 blocks
==803==    indirectly lost: 0 bytes in 0 blocks
==803==      possibly lost: 2,432 bytes in 26 blocks
==803==    still reachable: 950,353 bytes in 19,076 blocks
==803==                       of which reachable via heuristic:
==803==                         length64           : 1,432 bytes in 22 blocks
==803==                         newarray           : 1,776 bytes in 31 blocks
==803==         suppressed: 0 bytes in 0 blocks

For future reference, I used the command line: G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --leak-check=full ./examples/read_write/read_write E7:3D:E2:B3:F3:57 read EF680206-9B35-4933-9B10-52FFA9740042

chengyingyuan commented 5 years ago

Would you mind to also add the code for the connection to help me to potentially fix all memory leaks? I will try to have a look.

I'm planning to program a 24x7 service, so memory usage is critical to me. Here goes my main loop:

`static void ble_connect() { g_connection = gattlib_connect(nullptr, g_addr, GATTLIB_CONNECTION_OPTIONS_LEGACY_DEFAULT); g_connection_tried = true; }

static bool proxy_connect() { bool ret = false; while(!g_sigquit) { LOG_INFO("Connecting to device %s", g_addr); g_connection_tried = false; std::thread t(ble_connect); t.detach(); while (!g_sigquit && !g_connection_tried) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); } if (g_connection_tried && g_connection==nullptr) { LOG_ERROR("Fail to connect to device, try again."); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); continue; } if (g_sigquit) { LOG_INFO("Quit signal received when connecting."); break; } assert(g_connection != nullptr); LOG_INFO("Connected to device %s", g_addr); ret = true; break; } return ret; }

int main(int argc, char argv[]) { //... while (!g_sigquit) { if (!proxy_connect()) { continue; } while (!g_sigquit) { char buf = nullptr; size_t buflen = 0; ret = gattlib_read_char_by_uuid(g_connection, &g_uuid, reinterpret_cast<void**>(&buf), &buflen); //ret = gattlib_read_char_by_uuid_async(g_connection, &g_uuid, ble_read); if (ret != GATTLIB_SUCCESS) { LOG_ERROR("Error reading GATT with ret %d", ret); proxy_disconnect(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); break; } printf("Read %lu bytes\n", buflen); free(buf); } } //... } `

chengyingyuan commented 5 years ago

Actually, I have just modified the example read_write to make a loop of 20 reads and used valgrind to check more memory leak.

I fixed them in this commit: 51c6cbe

Here is the status:

==803== LEAK SUMMARY:
==803==    definitely lost: 0 bytes in 0 blocks
==803==    indirectly lost: 0 bytes in 0 blocks
==803==      possibly lost: 2,432 bytes in 26 blocks
==803==    still reachable: 950,353 bytes in 19,076 blocks
==803==                       of which reachable via heuristic:
==803==                         length64           : 1,432 bytes in 22 blocks
==803==                         newarray           : 1,776 bytes in 31 blocks
==803==         suppressed: 0 bytes in 0 blocks

For future reference, I used the command line: G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --leak-check=full ./examples/read_write/read_write E7:3D:E2:B3:F3:57 read EF680206-9B35-4933-9B10-52FFA9740042

I tried your new commit. It does work. Definitely lost memory is vanish. But there is a lot of memory still reachable. Is there a way to free it? It goes increasing.

Valgrind report as following:

==5669== by 0x6003C64: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==5669== by 0x60038F9: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==5669== by 0x6005D42: g_dbus_message_new_from_blob (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==5669== ==5669== 845,440 bytes in 6,605 blocks are still reachable in loss record 1,694 of 1,694 ==5669== at 0x6312B00: g_type_create_instance (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==5669== by 0x62F3747: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==5669== by 0x62F55BF: g_object_new_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5600.4) ==5669== by 0x5FA1645: g_initable_new_valist (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==5669== by 0x5FA1708: g_initable_new (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.5600.4) ==5669== by 0x4E5D796: org_bluez_gatt_characteristic1_proxy_new_for_bus_sync (org-bluez-gattcharacteristic1.c:2549) ==5669== by 0x4E4CB72: handle_dbus_gattcharacteristic_from_path (gattlib_char.c:44) ==5669== by 0x4E4CF21: get_characteristic_from_uuid (gattlib_char.c:160) ==5669== by 0x4E4D458: gattlib_read_char_by_uuid (gattlib_char.c:301) ==5669== by 0x10B3CD: main (gattproxy.cpp:216) ==5669== ==5669== LEAK SUMMARY: ==5669== definitely lost: 0 bytes in 0 blocks ==5669== indirectly lost: 0 bytes in 0 blocks ==5669== possibly lost: 2,464 bytes in 26 blocks ==5669== still reachable: 15,808,405 bytes in 357,191 blocks ==5669== of which reachable via heuristic: ==5669== length64 : 1,432 bytes in 22 blocks ==5669== newarray : 1,776 bytes in 31 blocks ==5669== suppressed: 0 bytes in 0 blocks ==5669==

chengyingyuan commented 5 years ago

Actually, I have just modified the example read_write to make a loop of 20 reads and used valgrind to check more memory leak.

I fixed them in this commit: 51c6cbe

Here is the status:

==803== LEAK SUMMARY:
==803==    definitely lost: 0 bytes in 0 blocks
==803==    indirectly lost: 0 bytes in 0 blocks
==803==      possibly lost: 2,432 bytes in 26 blocks
==803==    still reachable: 950,353 bytes in 19,076 blocks
==803==                       of which reachable via heuristic:
==803==                         length64           : 1,432 bytes in 22 blocks
==803==                         newarray           : 1,776 bytes in 31 blocks
==803==         suppressed: 0 bytes in 0 blocks

For future reference, I used the command line: G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --leak-check=full ./examples/read_write/read_write E7:3D:E2:B3:F3:57 read EF680206-9B35-4933-9B10-52FFA9740042

My valgrind arguments as

valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind.log

oliviermartin commented 5 years ago

I had a deeper look and I could see some of the memory allocations were looking for a g_main_loop. I added a new example to the repository to demonstrate the use of g_main_loop: https://github.com/labapart/gattlib/blob/master/examples/read_write_memory/read_write_memory.c