[an error occurred while processing this directive]

Diving and Visualizing Data

This part is based on simple5_a.c and simple5_b.c. We start with compiling the first mentioned into simple5 via: gcc -g -O0 -o simple5 simple5_a.c and start it within totalview

module add totalview/latest
totalview ./simple5
and press Go in the toolbar. The execution stops with a segmentation fault displaying assembler code in the source panel and a stack frame with a huge number of init_s calls.

We return to the source code after pressing Restart in the toolbar insert a breakpoint at the call of init_s followed by Go. We may press Next or Step and see their difference. With Step we follow the execution into init_s and set a breakpoint there init_s calls itself recursively. Pressing Go several times and watching the value of variable depth reveals quickly that incrementing depth should be moved in front of calling init_s.

Now the program runs without fault, just change it in a editor window, recompile and press Restart, but returns wrong results. We set a breakpoint at the brace ending main and Go again. Structure.x is added to elements of A, which are wrong, so we dive (right mouse on Structure) into it.

We could continue diving and click on SI. As this is a pointer, totalview automatically displays it as an array with 10 elements:

We already see, that too many values are marked red indicating that the initialization did not work properly. Looking at the source, we see, that Structure is passed to init_s by value. Thus init_s initializes a copy of Structure which of course makes no sense. A pointer should be passed instead. All necessary changes are done in simple5_b.
Never pass a structure by value. Structures may contain a lot of data and all will be copied when passed by value.

There is something else about structures which is very important and easily recognized with a debugger. Let's work with the corrected source simple5_b and dive into Structure.

We thereby use the submenue behind the image in the head of the table as indicated and push the button Address. The entry name occupies the address ending with 60 (HEX) or 6*16+0=96 (Decimal). The next entry, double x occupies 68 (=104), 8 bytes after name.


In the View tab we can push the button Padding and look what actually is going on. After name appears the entry $padding which is actually an array with 7 elements of 1 byte.

Due to the hardware architecture of RAM-Memory memory is randomly accessible, therefore its name. But the access the access is organized in two steps like a shelf in a pharmacy. First you open a drawer then you select an item inside. And usually all packages of a certain drug are within a single drawer. In RAM-Memory the smallest unit for a drawer is a double precision word that is 8 byte or 64 bits. Thus, if a structure starts with a character (drawer 1) the following double will be put 7 bytes away in drawer 2 and so on. Therefore we see another padding following the 3 floats (each 4 bytes) which need 2 drawers there the 2nd is half filled.

When organizing data in a structure. Specify the larger items first to save memory and reduce padding.

But back to our error. Now that we know that the information in Structure is correct, what about A. Some information first: A is suposed to be a dense 2D matrix. It is allocated as 1D array which is the usual way in HPC programs and the common data layout in many third party libraries, like BLAS or LAPACK. The layout is not limited to square matrices but can be used for rectangular matrices as well and more important und useful for sub-matrices as well. Something like - work on a 5x5 matrix within a 20x50 matrix - requires no special coding.

In i_matrix an initialization of any matrix size or sub-matrix is possible. The only information required is the element of the matrix to start with, in an x-y layout the lower left edge, than of course the size of the sub-matrix, in i_matrix this is the 1st argument, and finally the x-dimension of the embedding matrix (2nd argument). In BLAS this is called the leading dimension, or lda and corresponds to the number of x-elements in the embedding matrix.

Now let us inspect the elements of A. Totalview displays a vector with 10 elements when diving into a pointer. But the size may not only be enlarged but as well extended to several dimensions. In our case, there the matrix is sized 20x20 (nmax=20) we change it as indicated.
But we can even do more. The Tools tab opens an array viewer and a visualization. Here we see, that A[i*nmax+i]+=Structure.x; adds the values correct to 10 diagonal elements of A (black bar), but that the initialization in i_matrix went wrong and initialized not a slice of A instead of the intended 10x10 sub-matrix.

Instead of a x-y contour the data may be covered by a surface as given below after having corrected the error. While i_matrix is written correct, the calling parameters are wrong. Understanding the intention of initializing a 10x10 sub-matrix of A, the 2nd parameter has to specify the leading dimension of the matrix, but doesn't. To correct the error, change the 2nd parmeter to nmax and everything works fine. And here, finally, is the prove. We have initialized a 10x10 sub-matrix of a 20x20 matrix A.

A different way to control, whether all diagonals are set, uses the Filter in the pane. Here all arithmetic comparisions in C syntax may be specified.

We even can do more than that. If we instead of diving into A select Properties in the context menu (right mouse). This opens the possibility to insert a conditional breakpoint and for example to call the intrinsic totalview function visualize. This opens the possibility to visualize arrays and there changes during execution. Stopping at the 1st and the 3rd time, we see, that in the beginning the matrix has several entries below 4 and it looks like a slice. Then entries of size 25 are added to the diagonals and so on.

I end this section with a question: What would happen, if instead of A &A[n*nmax+n] is passed to i_matrix?