From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

CVI debugger: unable to read memory

Solved!
Go to solution

Hello,

 

today I encountered a debugger view not seen before:

 

memory_error.png

 

I would be glad if someone could explain why this message appears.

I would assume that 'external process' refers to the CVI debugger?

 

(using CVI 2013SP1)

 

Thanks!

0 Kudos
Message 1 of 13
(8,618 Views)

Hello Wolfgang!

 

The error you are seeing is usually displayed when the CVI debugger isn't able to read the value of some data or memory inside your CVI application (i.e. external process).

A valid case for this happening would be for example, when a variable-length array is declared being the size of an uninitialized variable (when run-time checking is enabled):

int n;

char *file_list[n];

 

However, in order to determine whether what you are experiencing is a problem indeed, it would be helpful if you would:

  1. provide the declaration of the file_list array (e.g. is file_list a variable-length array?);
  2. mention the way in which you initialize the file_list elements (i.e. statically vs. dynamically);
  3. mention any additional steps that are required for reproducing the error message in the Watch window or the Variables window (e.g. any specific order in which you are stepping through code if necessary).

 

Thank you and best regards!

- Johannes

0 Kudos
Message 2 of 13
(8,594 Views)

Hello Wolfgang!

 

The external process is the application that you are debugging. You are trying to find out what is stored in memory in your application at address 0xFEEEFEEEFEEEFEEE. The most likely explanation of why we cannot read memory at that address is that the address is simply invalid. It does not map to a physical location in memory. That is, the virtual memory manager of the OS has not allocated any memory for the process at that address.

 

You probably didn't see the error in previous versions of CVI because those versions probably did not allow expansion of that item in the Variables window. I'm going to file a suggestion to reimplement the old behavior and to prevent confusion in the future.

 

Thanks,

 

Peter

Message 3 of 13
(8,588 Views)

Hello Johannes, hello Peter,

 

Thanks for your feedback!

 

This is how I set up file_array:

 

    char **file_list = NULL;

    selection_status = MultiFileSelectPopupEx ( "", "*.asc", "Spectrum File (*.asc)", "Select Spectrum Files", 0, 0, &number_of_selected_files, &file_list );

This is all fine, the problem is with a buggy function of mine, i.e., when I call

 

MultiFileSelectionError ( number_of_selected_files, file_list );

 

void MultiFileSelectionError ( int number_of_files, char **file_list )

{
    int index = 0;
    
    for ( index = 0; index < number_of_files; index ++ )
    {      
        MemoryFreeDynamicMemory ( file_list [ index ] );
    }
    MemoryFreeDynamicMemory ( file_list );
}

 

with the following:

 

#define MemoryFreeDynamicMemory(x) MemoryFreeDynamicMemoryI ( ( void ** ) &x )

and

 

void MemoryFreeDynamicMemoryI ( void **memory_block_pointer )

{
    free ( *memory_block_pointer );
    *memory_block_pointer = NULL;  
    return;
}

Looking at the screenshot you will note that indeed the memory addresses are strange, i.e., most probbly invalid, so I would expect that the debugger is right. But then, how come that 'user protection' did not tell me...

 

I am using 'Debugging level' set to extended, which is supposed to validate every attempt to free dynamically allocated memory by verifying that the address passed is actually the beginning of an allocated block...

0 Kudos
Message 4 of 13
(8,585 Views)

Unfortunately, user-protection can be a bit delicate some times. Here, you're losing the original user protection information for the memory blocks when you cast to a (void **) in your macro. As a result, the blocks are not marked as having been freed. One way to fix this is to rewrite the macro and function:

 

#define MemoryFreeDynamicMemory(x) MemoryFreeDynamicMemoryI(x, (void **)&x)

void MemoryFreeDynamicMemoryI(void *memory, void **pointer_to_memory)
{
    free(memory);
    *pointer_to_memory = NULL;  
}

Now, the Variables window is showing file_list as "Invalid: Freed" on my machine.

Message 5 of 13
(8,574 Views)

Peter,

 

Thank you very much!

 

- In your example the debugger should show NULL and not invalid: freed (if I remember correctly this is already treated as a CAR)

- If the blocks are not marked as having been freed, as you wrote, why does the debugger show a strange address instead? According to your explanation I would have assumed that the memory has been freed, but due to the void cast the debugger is unaware of it, still assuming the original memory address.

- Maybe the error message of the debugger could be changed to avoid the confusion about the external process, something like 'unable to read memory in the debugged process'?

0 Kudos
Message 6 of 13
(8,539 Views)

The debugger does not show file_list == NULL because file_list is not reset to NULL in the main function. You have to change your code slightly:

 

void MultiFileSelectionError(int number_of_files, char ***file_list) // additional level of indirection for file_list
{
    int index = 0;
    
    for (index = 0; index < number_of_files; index++)
    {      
        MemoryFreeDynamicMemory((*file_list)[index]);
    }
    MemoryFreeDynamicMemory(*file_list);
}

... MultiFileSelectionError(number_of_selected_files, &file_list); // pass file_list by pointer so MemoryFree can reset it ...

 

When the run-time engine frees memory blocks, it overwrites them with 0xFEEEFEEE as a debugging aid. (I don't know if it always does this, or only when some other flags are set somewhere.) Obviously, 0xFEEEFEEE is garbage, and that's the point. If you debug your program without user-protection (Debugging Level: No Run-Time Checking), your program will crash when you try to dereference the garbage values and the debugger will tell you where it happened.

 

Other operations on garbage values should also quickly return obviously incorrect results. And when you inspect memory in the Variables or Memory windows, you can quickly tell if a memory block has been freed (correctly or incorrectly) and should not be accessed anymore.

 

It is not foolproof. The memory is marked as having been freed and it can be reallocated for something else. But it's better than nothing.

 

As I mentioned earlier, you free file_list in your program but you do not reset the file_list pointer to NULL. It continues to point to the memory block that used to contain your file list. That memory block was freed however and its contents were overwritten with 0xFEEEFEEE. Those strange addresses in the Variables window are the current contents of the memory block that used to contain your file_list.

 

I'll file a suggestion to change the error message. Thanks!

0 Kudos
Message 7 of 13
(8,532 Views)

@Peter_Ilberg wrote:

When the run-time engine frees memory blocks, it overwrites them with 0xFEEEFEEE as a debugging aid.


THIS is interesting to learn, thanks.

0 Kudos
Message 8 of 13
(8,523 Views)

I know I am stretching your patience, but... why does the debugger not show "Invalid: Freed" in this case:

 

s1.png

 

s2.png

 

 

... thanks !

0 Kudos
Message 9 of 13
(8,515 Views)
Solution
Accepted by topic author Wolfgang

It's one of those dark corners of user-protection. You're losing the user-protection information for memory_block_pointer in your macro:

 

#define MemoryFreeDynamicMemory(x) MemoryFreeDynamicMemoryI ( ( void ** ) &x )

It's really an artefact of how user-protection is implemented and how the information is passed through the program.

 

When you free that memory block, the memory block itself is freed, but the user-protection that's associated with that block is not marked as having been freed, because the reference to that information was lost in the combination of (void **) and &.

 

The debugger sees the original user-protection information and thinks that (*memory_block_pointer) is still pointing to a valid, allocated block of memory.

 

It works when you pass the memory block and the memory block pointer separately, because you avoid the (void **) and & on the parameter that is passed to free():

 

#define MemoryFreeDynamicMemory(x) MemoryFreeDynamicMemoryI(x, (void **)&x)

void MemoryFreeDynamicMemoryI(void *memory, void **pointer_to_memory)  // original u-p survives for 'memory'
// but is lost for 'pointer_to_memory' { free(memory); // can mark original u-p as having been freed *pointer_to_memory = NULL; // doesn't need original u-p }

As I said earlier, user-protection is very delicate in some areas.

Message 10 of 13
(8,509 Views)