2010-02-12 7 views
7

Ho scritto un programma per sondare i limiti delle funzioni C.h di un sistema e scaricarli in JSON. Quindi altre cose che dipendono da quelle funzioni possono conoscere i loro limiti.C'è un modo per eseguire il dump di una struct C?

# system time.h limits, as JSON 
{ 
    "gmtime": { "max": 2147483647, "min": -2147483648 }, 
    "localtime": { "max": 2147483647, "min": -2147483648 }, 
    "mktime": { 
     "max": { "tm_sec": 7, "tm_min": 14, "tm_hour": 19, "tm_mday": 18, "tm_mon": 0, "tm_year": 138, "tm_wday": 1, "tm_yday": 17, "tm_isdst": 0 }, 
     "min": { "tm_sec": 52, "tm_min": 45, "tm_hour": 12, "tm_mday": 13, "tm_mon": 11, "tm_year": 1, "tm_wday": 5, "tm_yday": 346, "tm_isdst": 0 } 
    } 
} 

gmtime() e localtime() sono abbastanza semplici, hanno semplicemente prendere i numeri, ma mktime() prende una struct tm. Ho scritto una funzione personalizzata per trasformare una struttura tm in un hash JSON.

/* Dump a tm struct as a json fragment */ 
char * tm_as_json(const struct tm* date) { 
    char *date_json = malloc(sizeof(char) * 512); 
#ifdef HAS_TM_TM_ZONE 
    char zone_json[32]; 
#endif 
#ifdef HAS_TM_TM_GMTOFF 
    char gmtoff_json[32]; 
#endif 

    sprintf(date_json, 
      "\"tm_sec\": %d, \"tm_min\": %d, \"tm_hour\": %d, \"tm_mday\": %d, \"tm_mon\": %d, \"tm_year\": %d, \"tm_wday\": %d, \"tm_yday\": %d, \"tm_isdst\": %d", 
      date->tm_sec, date->tm_min, date->tm_hour, date->tm_mday, 
      date->tm_mon, date->tm_year, date->tm_wday, date->tm_yday, date->tm_isdst 
    ); 

#ifdef HAS_TM_TM_ZONE 
    sprintf(&zone_json, ", \"tm_zone\": %s", date->tm_zone); 
    strcat(date_json, zone_json); 
#endif 
#ifdef HAS_TM_TM_GMTOFF 
    sprintf(&gmtoff_json", \"tm_gmtoff\": %ld", date->tm_gmtoff); 
    strcat(date_json, gmtoff_json); 
#endif 

    return date_json; 
} 

C'è un modo per farlo in modo generico, per una determinata struttura?

Nota: C, non C++.

risposta

4

Non in C-almeno in generale. Ma se il modulo C è compilato con i simboli di debug e il modulo oggetto è disponibile, è possibile analizzarlo e scoprire tutto sulla struttura. Scommetto che c'è una libreria per il tuo sistema per aiutarti.

+0

+1 per me battere ad esso. –

1

Questo non sarà abbastanza darti quello che stai chiedendo, ma potrebbe aiutare un po ':

#define NAME_AND_INT(buf, obj, param) \ 
     sprintf((buf), "\"%s\": %d, ", #param, (obj)->(param)) 

Si potrebbe quindi iterare, per esempio qualcosa di simile (nota: non testato; considerare questo pseudo-codice):

char * tm_as_json(const struct tm* date) { 
    /* ... */ 
    char buf[BUFSIZ]; /* or, use your date_json */ 

    pos = buf; /* I note you use the equivalent of &buf -- that works too */ 
       /* (not sure which is "better", but I've always left the & off 
       * things like that -- buf is essentially a pointer, it's just 
       * been allocated in a different way. At least that's how I 
       * think of it. */ 
    pos += NAME_AND_INT(pos, date, tm_sec); 
    pos += NAME_AND_INT(pos, date, tm_min); 
    /* ... more like this ... */ 

    /* strip trailing ", " (comma-space): */ 
    pos-=2; 
    *pos = '\0'; 

    /* ... */ 
} 

Si potrebbe definire in modo simile NAME_AND_STRING, NAME_AND_LONG, ecc (per tm_zone e tm_gmtoff), se necessario.

Ancora una volta, non è una soluzione generica, ma almeno ti avvicina un po ', forse.

+0

Grazie, è un po 'più piacevole lavorare con. – Schwern

+0

Bene, sono contento che sia utile. Inoltre, ho appena notato che avrei dovuto avere una parentesi attorno a "buf" nell'espansione della macro. Ho modificato la mia risposta per aggiungerli. – lindes

0

Tom Christiansen ha scritto una volta pstruct/h2ph, che è in perl CORE per analizzare le informazioni .stab dal compilatore utilizzato e creare informazioni leggibili per tutte le strutture di dati.

C structs in JSON è banale basato su h2ph. http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

+0

Grazie, ma è questo l'analisi della struttura C con Perl? – Schwern

+0

È quasi impossibile analizzare una struttura C solo con perl, solo se si riscrivono parti del compilatore in perl. E non è necessario, dal momento che il compilatore potrebbe essere in grado di creare informazioni simboliche per le strutture di questo stesso compilatore: stabs, dwarf, xml. Per xml c'è GCC :: TranslationUnit, e c'è anche Convert :: Binary :: C che usa ucpp. – rurban

0

Questa macro non fa esattamente quello che vuoi (genera un dump JSON di dati C), ma penso che mostri alcune possibilità. Puoi scaricare il contenuto di qualsiasi dato C con un "p (...);" chiamata.

Ho usato gdb come helper esterno per farlo funzionare, ma è possibile implementarne uno con libbfd. In tal caso, puoi controllare completamente il tuo output, ad esempio generare un output compatibile con JSON.

#ifndef PP_H 
#define PP_H 
/* 
* Helper function (macro) for people who loves printf-debugging. 
* This dumps content of any C data/structure/expression without prior 
* knowledge of actual format. Works just like "p" or "pp" in Ruby. 
* 
* Usage: 
* p(anyexpr); 
* 
* NOTE: 
* - Program should be compiled with "-g" and preferrably, with "-O0". 
* 
* FIXME: 
* - Would be better if this doesn't depend on external debugger to run. 
* - Needs improvement on a way prevent variable from being optimized away. 
*/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <stdarg.h> 

// Counts number of actual arguments. 
#define COUNT_(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define COUNT(...) COUNT_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) 

// Dispatches macro call by number of actual arguments. 
// Following is an example of actual macro expansion performed in case 
// of 3 arguments: 
// 
// p(a, b, c) 
// -> FUNC_N(p, COUNT(a, b, c), a, b, c) 
// -> FUNC_N(p, 3, a, b, c) 
// -> p_3(a, b, c) 
// 
// This means calling with simple "p(...)" is fine for any number of 
// arguments, simulating "true" variadic macro. 
#define CONCAT(name, count) name##count 
#define FUNC_N(name, count, ...) CONCAT(name, count)(__VA_ARGS__) 

// Forbids variable from being optimized out, so debugger can access it. 
// 
// FIXME: 
// - Current implementation does not work with certain type of symbols 
#define ENSURE(...) FUNC_N(ENSURE_, COUNT(__VA_ARGS__), __VA_ARGS__) 
#define ENSURE_1(a) asm(""::"m"(a)) 
#define ENSURE_2(a, ...) do { ENSURE_1(a); ENSURE_1(__VA_ARGS__); } while (0) 
#define ENSURE_3(a, ...) do { ENSURE_1(a); ENSURE_2(__VA_ARGS__); } while (0) 
#define ENSURE_4(a, ...) do { ENSURE_1(a); ENSURE_3(__VA_ARGS__); } while (0) 
#define ENSURE_5(a, ...) do { ENSURE_1(a); ENSURE_4(__VA_ARGS__); } while (0) 
#define ENSURE_6(a, ...) do { ENSURE_1(a); ENSURE_5(__VA_ARGS__); } while (0) 
#define ENSURE_7(a, ...) do { ENSURE_1(a); ENSURE_6(__VA_ARGS__); } while (0) 
#define ENSURE_8(a, ...) do { ENSURE_1(a); ENSURE_7(__VA_ARGS__); } while (0) 

// Dumps content of given symbol (uses external GDB for now) 
// 
// NOTE: 
// - Should use libbfd instead of gdb? (but this adds complexity...) 
#define PP_D(...) do { \ 
char *arg[] = { __VA_ARGS__, NULL }; \ 
char **argp = arg; \ 
char cmd[1024]; \ 
FILE *tmp = tmpfile(); \ 
fprintf(tmp, "attach %d\n", getpid()); \ 
fprintf(tmp, "frame 2\n"); \ 
while (*argp) \ 
fprintf(tmp, "p %s\n", *argp++); \ 
fprintf(tmp, "detach\n"); \ 
fflush(tmp); \ 
sprintf(cmd, "gdb -batch -x /proc/%d/fd/%d", \ 
getpid(), fileno(tmp)); \ 
system(cmd); \ 
fclose(tmp); \ 
} while (0) 

#define PP(...) do { \ 
FUNC_N(PP_, COUNT(__VA_ARGS__), __VA_ARGS__); \ 
ENSURE(__VA_ARGS__); \ 
} while (0) 
#define PP_1(a) do { PP_D(#a); } while (0) 
#define PP_2(a,b) do { PP_D(#a,#b); } while (0) 
#define PP_3(a,b,c) do { PP_D(#a,#b,#c); } while (0) 
#define PP_4(a,b,c,d) do { PP_D(#a,#b,#c,#d); } while (0) 
#define PP_5(a,b,c,d,e) do { PP_D(#a,#b,#c,#d,#e); } while (0) 
#define PP_6(a,b,c,d,e,f) do { PP_D(#a,#b,#c,#d,#e,#f); } while (0) 
#define PP_7(a,b,c,d,e,f,g) do { PP_D(#a,#b,#c,#d,#e,#f,#g); } while (0) 
#define PP_8(a,b,c,d,e,f,g,h) do { PP_D(#a,#b,#c,#d,#e,#f,#g,#h); } while (0) 

// Comment this out if you think this is too aggressive. 
#define p PP 

#endif 

rientro si perde in pasta sopra, ma si può afferrare la fonte da: https://github.com/tai/ruby-p-for-c

+0

Grazie per la risposta. Affidarsi a librerie non standard, e soprattutto non a programmi esterni, non è possibile nel mio caso. Anche se sarei interessato a vedere come sarebbe una versione di libbfd. – Schwern

2

Dopo aver incontrato lo stesso problema, ho scritto uno io. https://github.com/jamie-pate/jstruct. È scritto per consentire l'annotazione delle strutture c esistenti, quindi generare informazioni sui metadati in base alle annotazioni. La libreria legge i metadati per importare/esportare le strutture c in stringhe json e viceversa. Uno script python si occupa di analizzare l'intestazione annotata e generare nuove intestazioni e inizializzatori di metadati. La libreria jstruct utilizza https://github.com/json-c/json-c internamente. Ho anche notato https://github.com/marel-keytech ... ma quello era dopo aver scritto l'intera cosa. (e le informazioni sulla pagina di quel progetto sono scarse)

Non esiste ancora alcun supporto per annotare una singola struttura da una lib di sistema esistente ma è possibile racchiudere la struttura tm in una struttura personalizzata annotata. (Credo?)

Sentiti libero di aggiungere richieste di funzionalità o anche di estrarre richieste se hai idee che potrebbero rendere la libreria più utile per te. Un'idea che avevo sarebbe quella di aggiungere un modo per incorporare questo tipo di sola lettura struct all'interno di un involucro annotato con un @inline della nota: per esempio (attualmente non supportato, ma potrebbe essere aggiunti)

//@json 
struct my_time { 
    //@inline 
    struct tm tm; 
} 

Codice:

struct my_time t; 

mktime(&t.tm); 
struct json_object *result = jstruct_export(t, my_time); 

Nel frattempo si potrebbe fare la stessa cosa, senza @inline (in quanto non è stato ancora scritto) e appena estrarre la proprietà tm a mano con json_object_to_json_string(json_object_object_get(result, "tm"))

Problemi correlati