user2917478
user2917478

Reputation:

Interesting behavior in sequential code execution

EDIT 2: I'm still stuck here, but the problem is most likely located in the auto-vectorization rewriting system, which is irrelevant to this question. I'm updating this question again on valgrind's SIGILL output mentioned in the comments below

==10478== Process terminating with default action of signal 4 (SIGILL) 
==10478== Illegal opcode at address 0x423F4C ==10478== at 0x423F4C: sp_private_9_compute (smmintrin.h:209) 
==10478== by 0x4247F4: sp_private_9__timeCompute 

The FIRST thing to check if you see SIGILL raised by valgrind when debugging SSE code is that YOUR VALGRIND IS UP-TO-DATE In my case, I was using V3.6 which had bugs in SSE4 intrinsic support. SIGILL was raised b/c it didn't understand the _mm_mullo_epi32 instruction. After updating it to V3.9, valgrind can better locate the real problem of the SSE program.

I'll update this question again if I find more relevant debugging tips.


EDIT 1: I hope someone can confirm my observation of the non-sequential behavior in the debugging trace is accurate.


First of all, this is a very specific question about C debugging. I really appreciate if you read it through and provide any feedback.

Background: I'm working with a code generation system, and I recently keep getting a "double free or corruption" from glibc. So I recompiled the auto-generated code with extra debugging information and ran it with gdb. And either I'm brain-dead right now, or I actually observed a non-sequential behavior in sequential code execution.

Codes:

LINE:6851     if(self->_garbage != 0) {
LINE:6852            sp_env_list_free_children(self->_garbage);
LINE:6853            sp_env_list___del__(self->_garbage);
LINE:6854            sp_env_free(self->_garbage, sizeof(sp_env_list_t));
LINE:6855     }//End of if
LINE:6856   }//End of an outer if structure
LINE:6857 }//End of the function call containing the if structure

I'll explain the free and del funcs after the debugging trace.

Debugging Trace

6851   if (((self->_garbage != 0))) {
(gdb) step
6852   sp_env_list_free_children(self->_garbage);
(gdb) print self->_garbage 
$1 = (sp_env_list_t *) 0x649080
(gdb) step
6853   sp_env_list___del__(self->_garbage); 
(gdb) print self->_garbage 
$2 = (sp_env_list_t *) 0x649080
(gdb) step
6854      sp_env_free(self->_garbage, sizeof(sp_env_list_t )); 
(gdb) print self->_garbage 
$3 = (sp_env_list_t *) 0x649080
(gdb) step
6857   }
(gdb) print self->_garbage 
$4 = (sp_env_list_t *) 0x649080
(gdb) step
sp_private_11___del__ (self=<value optimized out>) at sp_moddft_int32.c:6854
6854      sp_env_free(self->_garbage, sizeof(sp_env_list_t )); 
(gdb) print self->_garbage 
Cannot access memory at address 0x18
(gdb) step
Single stepping until exit from function sp_env_free, 
which has no line number information.
*__GI___libc_free (mem=0x649080) at malloc.c:3692
3692    malloc.c: No such file or directory.
    in malloc.c

As you can see in the debugging trace, it reaches to Line 6857 after Line 6854, and then comes back to Line 6854 again. Since self->_garbage has been freed completely, this should be where the "double free or corruption" happens. However, the entire function is sequential with no loops. I don't understand why it jumped back.

For the completeness of the question, the three functions in the if structure are for:

I can provide any additional information if it's not clear. I hope someone can at least confirm my observation of the non-sequential behavior is accurate. Thanks so much!

Upvotes: 2

Views: 180

Answers (1)

rici
rici

Reputation: 241771

Optimized code often appears to jump around randomly when you single-step through it. The optimizer is free to reorganize code flow as long as the defined ordering relations are maintained.

In this case, I suspect you're seeing the result of tail call optimization (TCO). Your final call to sp_env_free is a tail-call, assuming that the function it is in has void return. Consequently, it is possible for the function to restore its callee-save registers (that is, execute the epilogue), and then jump to sp_env_free, rather than calling sp_env_free and then executing the epilogue. That saves a stack frame and a few instructions to set the stack frame up.

Since the epilogue is at the end of the function, it nominally has line number 6857, even though all that is visible on that line is a close brace. So TCO is completely consistent with the sequence of line numbers you see. I'm sure it's not the only possibility, though. In general, it's not easy to second guess a good optimizer; if you really want to know, you could try looking at the generated assembler code with -S, but given that your file has at least 6857 lines, that might not be easy to decipher either.

If you want to sensibly debug the program, turn optimization off.

Upvotes: 4

Related Questions