Debugando Webassembly Com Prints, Adição do Hexdump

Imprimir valores hexadecimais é uma adição importante para a nossa infraestrutura de debug do Webassembly. HexDump (a impressão de valores hexadecimais) é muito útil quando você precisa ver o conteúdo de buffers. Atualmente estou trabalhando na implementação do SHA256 para o Webassembly, que é o próximo passo no Roadmap deste blog, e senti a necessidade para ter a função HexDump para me ajudar a descobrir porque meu SHA256 não estava funcionando.

O HexDump complementa a prévia implementação do Debug no Webassembly, dê uma olhada para mais detalhes.

Como Ele Se Parece?

Veja no gif seguir como o HexDump se comporta. O buffer de entrada (abc) e buffer com o resultado pode ser vistos, cada linha contém o dado em formato hexadecimal e ascii.

HexDump-Webassembly-Implementation

Implementação do HexDump

Você pode ver a implementação abaixo. Não é difícil, mas necessita atenção. Basicamente um buffer chamado line é construído, a primeira parte de line contém os bytes convertidos em valores hexadecimais enquanto que a segunda parte contém os bytes no formato ascii. Cada uma destas partes contém seus próprios índices (idx_hex and idx_ascii), o que faz com que o código fique muito mais limpo. Os comentários no código estão bem detalhados, dê uma olhada nele, e me avise se algo não esta claro ou errado.

Macro para ser adicionado ao arquivo debug-wasm.h, veja aqui para detalhes.

Esta é a macro utilizada para imprimir os valores hexadecimais e não a função C, desta maneira é possível colher informações sobre a sua localização como nome do arquivo, linha, etc…


#define DBG_DUMP(level, addr, len, fmt, ...) \ 
do \ 
{ \ 
   if (DEBUG_WASM_ENABLED) \ 
   { \ 
       Dbg_Dump(level, addr, len,"%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, __VA_ARGS__); \ 
   } \ 
} while (0) 

Função C a ser adicionada no arquivo debug-wasm.c, veja aqui para detalhes.


// 
// Dump memory 
// 
void Dbg_Dump(const DEBUG_WASM_LEVELS level, const void *addr, const size_t len, const char *fmt, ...) 
{ 
   // Only logs the message if the wasm debug level is equal or higher than this message level 
   if( level <= debugWasmLevel ) 
   { 
        // Print a preamble with location and number of bytes 
       va_list args; 
       va_start(args, fmt); 
        fprintf( stderr, "MEMORY DUMP(" ); 
       vfprintf(stderr, fmt, args); 
        fprintf( stderr, ", %d bytes):\n", len ); 
         
        char line[16*3+2+16+1+1]; // Where the line is going to be build, format: 
                                  // hex hex ... | asc asc ... \n 
                                  // hex is 3 bytes for each byte 
                                  // asc is 1 byte for each byte 
        line[16*3+0]='|';line[16*3+1]=' '; // Fill with the divisor | 
        line[sizeof(line)-2]='\n';line[sizeof(line)-1]='\0'; // Line end 
        size_t idx_hex=0; // index for the hex area 
        size_t idx_ascii=16*3+2; // index for the ascii area 
         
        // Go through all bytes in the buffer 
        for( size_t cnt=0; cnt<len; cnt++) 
        { 
            // Get a byte from the buffer and convert it to hex 
            // Nibble less than 0xa is converted to ascii by adding '0' 
            // Nibble more than 0x9 is converted to ascii by adding 'A'-0xa 
            unsigned char b=((unsigned char*)addr)[cnt]; 
            line[idx_hex++]= (b>>4)<0xa  ? (b>>4)+'0'  : (b>>4)+'A'-0xa; 
            line[idx_hex++]= (b&0xf)<0xa ? (b&0xf)+'0' : (b&0xf)+'A'-0xa; 
            line[idx_hex++]= ' '; 

            // Printable ? 
            if( b>=' ' && b<='~' ) 
            { 
                line[idx_ascii++] = b;  // Yes, put the character 
            } 
            else 
            { 
                line[idx_ascii++] = '.'; // No, print the dot 
            } 

            // Time to print?  
            if((cnt+1) % 16 == 0 || cnt+1 == len) 
            { 
                // One full line is built? 
                if( (cnt+1) % 16 == 0 ) 
                { 
                    // Yes, prepare indexes for the next line 
                    idx_hex=0; 
                    idx_ascii=16*3+2; 
                } 
                else 
                { 
                    // No, it means the final line needs to be printed, fill in the 
                    // missing bytes with spaces 
                    while( idx_ascii<(sizeof(line)-2) ) 
                    { 
                        line[idx_hex++]     = ' '; 
                        line[idx_hex++]     = ' '; 
                        line[idx_hex++]     = ' '; 
                        line[idx_ascii++]   = ' '; 
                    } 
                } 
                 
                // Print the line 
                fprintf( stderr, "%s", line); 
            } 
        } 
    } 
} 

Colocando HexDump Dentro Do Seu Código Webassembly

Use a macro DBG_DUMP. No exemplo a seguir eu adiciono DBG_DUMP antes e depois da chamada à função sha256Compute(), o que me permite ver o dado de entrada da função e seu resultado.


EMSCRIPTEN_KEEPALIVE void sha256Test(void) 
{ 
    unsigned char shaInput[3]={'a','b','c'}; 
    unsigned char shaOutput[256/8]; 
     
    // DUMP SHA256 input data 
    DBG_DUMP(DEBUG_WASM_DEBUG, shaInput, sizeof(shaInput), "%s", "SHA Input"); 
     
    sha256Compute(shaInput, sizeof(shaInput), shaOutput); 
     
    // DUMP SHA256 result 
    DBG_DUMP(DEBUG_WASM_DEBUG, shaOutput, sizeof(shaOutput), "%s", "SHA Output"); 
} 

Conclusão

Este foi um post rápido, mas ele traz uma característica importante para a infraestrutura de debug que estamos criando. Ele já me ajudou muito em entender porque a minha implementacao do SHA256 não estava funcionando.


Leave a message below. Webassembly is evolving rapidly, please let me know if this post got outdated.

Enjoyed this post?

Don't miss new posts: Share it with your friends:

Você pode gostar...

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *