I've finally managed to capture a failing FlashX driver.
This is using the TWR-K60D100M with MK60DN512VMD10-4N22D and associated 4.1 BSP from the cloning wizard.
The issue appears to be related to memory allocation.
The test code allocates an increasing number of memory segments, each 15 bytes, then tries to write 512 bytes to flashx:swap1.
The contents of the flash write have nothing to do with the memory allocated. They are separate buffers.
After a number of cycles, the write fails.
Specifically when it hits 57 segments (@ 15 bytes each this is 855 bytes).
If I start the loop at 57 segments, the write doesn't fail here. It counts up to some larger number of segments to fail.
So it seems deallocating memory doesn't put the system back in the same state.
Anyway, here is the main.c file. Also attached.
// MQX headers #include <mqx.h> #include <bsp.h> #include <fio.h> #ifdef _DEBUG // Allows you to do an action after the handler, such as return, print something, block, etc. #define ASSERT(cond,action,...) \ do { \ if(!(cond)) {\ assert_handler("[ASSERT]", #cond, __FILE__, __LINE__, ##__VA_ARGS__);\ do {\ action;\ } while(0);\ }\ } while(0) // Stops MQX #define ASSERT_FAIL(cond,...) ((!(cond) && \ assert_handler("[FATAL]", #cond, __FILE__, __LINE__, ##__VA_ARGS__) &&\ (_mqx_fatal_error(-1),1))) #define ASSERT_WARN(cond,...) ((!(cond) && \ assert_handler("[WARNING]", #cond, __FILE__, __LINE__, ##__VA_ARGS__))) #define ASSERT_MSG(cond,...) ((!(cond) && \ assert_handler("[MESSAGE]", #cond, __FILE__, __LINE__, ##__VA_ARGS__))) #else #define ASSERT(cond,action,...) do {(void)sizeof(cond);} while(0) #define ASSERT_FAIL(cond,...) #define ASSERT_WARN(cond,...) #define ASSERT_MSG(cond,...) #endif /* Task IDs */ enum {MAIN_TASK = 1}; // Local function declarations void main_task(uint32_t); const TASK_TEMPLATE_STRUCT MQX_template_list[] = { /* Task Index, Function, Stack, Priority, Name, Attributes, Param, Time Slice */ { MAIN_TASK, main_task, 4096, 10, "main", MQX_AUTO_START_TASK, 0, 0 }, { 0 } }; static bool assert_handler(char *category, char *arg, char *file, int line, ...) { printf("%s: (%s) at line %d in %s\n", category, arg, line, file); va_list args; va_start(args, line); vprintf(va_arg(args, char *), args); va_end(args); printf("\n"); fflush(stdout); return true; } static uint32_t min(uint32_t a, uint32_t b) { return a<b?a:b; } int32_t flash_write_bytes(const char *name, const uint32_t addr, const uint8_t *vals, const uint32_t num_bytes) { MQX_FILE_PTR flash = fopen(name, 0); ASSERT_FAIL(flash != NULL, "Can't open flash file"); //ioctl(flash, FLASH_IOCTL_ENABLE_SECTOR_CACHE, 0); //ioctl(flash, FLASH_IOCTL_ENABLE_BUFFERING, 0); fseek(flash, addr, IO_SEEK_SET); uint32_t result = write(flash, (uint8_t *)vals, num_bytes); if (result != num_bytes) { fclose(flash); return 0; } else { // Yes we were able to write everything fclose(flash); return result; } } static uint8_t static_buff[2048]; uint32_t test_flash(const char *name, uint32_t addr, uint8_t *vals, uint32_t num_bytes) { // Test flash for any region, any address. // This is destructive to content of vals, and obviously // destructive to contents of flash static uint8_t val = 1; memset(static_buff, val++, 2048); // Use local buffer and 2048 if buffer arg and sz are null and zero, respectively uint8_t *buff = vals == NULL ? static_buff : vals; uint32_t sz = num_bytes == 0 ? 2048 : num_bytes; uint32_t result; result = flash_write_bytes(name, addr, buff, sz); printf("Test flash result is: %d\n", result); fflush(stdout); return result; } static void main_task(uint32_t initial_data) { uint32_t num_segments = 1; uint32_t seg_size = 15; uint32_t *sizes; uint32_t **ptrs; uint32_t totalsz = 0; while(1) { //printf("\nAt least %d bytes available at start\n", find_max_memory()); uint32_t allocsz = num_segments * sizeof(uint32_t *); printf("\nAllocating %d bytes to hold %d sizes\n", allocsz, num_segments); sizes = _lwmem_alloc_zero(allocsz); ASSERT(sizes, "Could not allocate %d bytes", allocsz); fflush(stdout); printf("Allocating %d bytes to hold %d ptrs\n", allocsz, num_segments); ptrs = _lwmem_alloc_zero(allocsz); ASSERT(ptrs, "Could not allocate %d bytes", allocsz); fflush(stdout); printf("Allocating %d times\n", num_segments); for(uint32_t i = 0; i < num_segments; i++) { uint32_t sz = seg_size; //printf("\t%d\t Allocating %d bytes\n", i, sz); //fflush(stdout); ptrs[i] = _lwmem_alloc_zero(sz); ASSERT_FAIL(ptrs[i], "Could not allocate %d bytes", sz); sizes[i] = sz; totalsz += sz; } printf("Total allocation: %d bytes\n", totalsz); bool test_result = test_flash("flashx:swap1", 0x00000000, NULL, 512); ASSERT_FAIL(test_result, "Failed flash test"); printf("Flash test passed\n"); fflush(stdout); // Release before starting over printf("Releasing everything\n"); for(uint32_t i = 0; i < num_segments; i++) { sizes[i] = 0; ptrs[i] = NULL; _lwmem_free(ptrs[i]); } _lwmem_free(ptrs); _lwmem_free(sizes); totalsz = 0; num_segments += 1; } }
Original Attachment has been moved to: main.c.txt.zip
Hi,
I tested your code under MQX4.1.1 and CW10.6.
It runs until num_segments=90. After that it blocks because command “ptrs = _lwmem_alloc_zero(allocsz);” failed and ptrs is now NULL.
It never returns message that flash write fail.
I can see problem in that situation where you don’t check result of memory allocation and memory freeing.
In case of dynamically allocated memory, it can simply happen that allocation fail. In that case you have to manage appropriate action (try once again/wait/cancel upper layer command/error message…).
Additionally I don’t understood principle of your Releasing loop.
As first you allocates field of pointers (ptrs). After that you allocate 15 bytes for every pointer in that field.
In release loop you first erase every pointer in field (ptrs[i] = NULL;) and after that you trying release this NULL pointer.
It returns error code 1 (MQX_INVALID_POINTER), but you didn’t check it.
Therefore I suppose that this is root cause of this error.
This quickly leads to memory leak, because you don’t freeing memory.
When I comment out this line (ptrs[i] = NULL;), it runs up to 11932 allocsz loops, where _lwmem_alloc_zero() for ptrs [2982] failure. Total allocation is now 44745 bytes and I suppose that we simply reach end of available memory.
Anyway, I would like to recommend do not use _lwmem commands directly. Please use _mem_alloc commands instead of _lwmem. Otherwise macros like MQX_USE_LWMEM_ALLOCATOR has no sense and you have to keep in mind which set of allocators you use in your application…
In attachment you can find your code with my modifications/fixes.
I hope it helps you.
Have a great day,
RadekS
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Thanks Radek. Yes after posting that code I realized the memory bug. I simplified it and still got the failures after allocating only ~300 bytes (multiple allocations and release of 2-3 bytes each). Also I verified that there is around 100k free memory in that test code, so it stops well before memory runs out.
Also in my full application I removed all dynamic allocations and hand out pointers to a statically allocated chunk of memory (uint_08 ) instead. The failure still occurs.
I'll update the simple application and see if I can replicate the errors again without the allocation scheme you saw.
Thanks again for taking a look.