Mercurial > hg > dmlib
comparison tools/fanalyze.c @ 2034:45ad06bb60c4
Implement offset dump mode in fanalyze.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 29 Nov 2018 13:16:02 +0200 |
parents | 5fe25336a474 |
children | 472ca1dbc2d3 |
comparison
equal
deleted
inserted
replaced
2033:cf966e66c9af | 2034:45ad06bb60c4 |
---|---|
10 #include "dmargs.h" | 10 #include "dmargs.h" |
11 #include "dmfile.h" | 11 #include "dmfile.h" |
12 | 12 |
13 #define SET_MAX_FILES 64 | 13 #define SET_MAX_FILES 64 |
14 #define SET_MAX_ELEMS 256 | 14 #define SET_MAX_ELEMS 256 |
15 #define SET_MAX_VALUES 32 | 15 #define SET_MAX_VALUES 64 |
16 | 16 |
17 | 17 |
18 /* Typedefs | 18 /* Typedefs |
19 */ | 19 */ |
20 typedef struct | 20 typedef struct |
60 }; | 60 }; |
61 | 61 |
62 | 62 |
63 typedef struct | 63 typedef struct |
64 { | 64 { |
65 int type; | |
66 Uint32 value; | |
67 } DMGrepValue; | |
68 | |
69 | |
70 typedef struct | |
71 { | |
65 char *name; | 72 char *name; |
66 uint32_t nmax; | 73 Uint32 nmax; |
67 unsigned int bsize; | 74 unsigned int bsize; |
68 } DMGrepDef; | 75 } DMGrepDef; |
69 | 76 |
70 | 77 |
71 static const DMGrepDef dmGrepTypes[DMGV_last] = | 78 static const DMGrepDef dmGrepTypes[DMGV_last] = |
76 { "32bit (word) LE" , (1UL << 32) - 1, 4 }, | 83 { "32bit (word) LE" , (1UL << 32) - 1, 4 }, |
77 { "32bit (word) BE" , (1UL << 32) - 1, 4 }, | 84 { "32bit (word) BE" , (1UL << 32) - 1, 4 }, |
78 }; | 85 }; |
79 | 86 |
80 | 87 |
81 typedef struct | 88 enum |
82 { | 89 { |
83 int type; | 90 FA_ANALYZE, |
84 uint32_t value; | 91 FA_GREP, |
85 } DMGrepValue; | 92 FA_OFFSET, |
93 }; | |
86 | 94 |
87 | 95 |
88 /* Global variables | 96 /* Global variables |
89 */ | 97 */ |
98 int setMode = FA_ANALYZE; | |
90 int nsrcFiles = 0; // Number of source files | 99 int nsrcFiles = 0; // Number of source files |
91 DMSourceFile srcFiles[SET_MAX_FILES]; // Source file names | 100 DMSourceFile srcFiles[SET_MAX_FILES]; // Source file names |
92 DMStats totalStats; | 101 DMStats totalStats; |
93 int nsetGrepValues = 0; | 102 int nsetGrepValues = 0; |
94 DMGrepValue setGrepValues[SET_MAX_VALUES]; | 103 DMGrepValue setGrepValues[SET_MAX_VALUES]; |
98 */ | 107 */ |
99 static const DMOptArg optList[] = | 108 static const DMOptArg optList[] = |
100 { | 109 { |
101 { 0, '?', "help", "Show this help", OPT_NONE }, | 110 { 0, '?', "help", "Show this help", OPT_NONE }, |
102 { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, | 111 { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, |
103 { 2, 'g', "grep", "Binary grep mode <val>[,<le|be>[8|16|32]]", OPT_ARGREQ }, | 112 { 2, 'g', "grep", "Binary grep <val>[,<le|be>[8|16|32]]", OPT_ARGREQ }, |
113 { 3, 'o', "offset", "Show data in offset <offset>,<le|be>[8|16|32]]", OPT_ARGREQ }, | |
104 }; | 114 }; |
105 | 115 |
106 static const int optListN = sizeof(optList) / sizeof(optList[0]); | 116 static const int optListN = sizeof(optList) / sizeof(optList[0]); |
107 | 117 |
108 | 118 |
111 dmPrintBanner(stdout, dmProgName, "[options] <input file #1> <input file #2> [...]"); | 121 dmPrintBanner(stdout, dmProgName, "[options] <input file #1> <input file #2> [...]"); |
112 dmArgsPrintHelp(stdout, optList, optListN, 0); | 122 dmArgsPrintHelp(stdout, optList, optListN, 0); |
113 } | 123 } |
114 | 124 |
115 | 125 |
116 int argParseGrepValue(const char *arg) | 126 int argParseGrepValue(const char *arg, const int mode) |
117 { | 127 { |
118 const char *sep = strchr(arg, ','); | 128 const char *sep = strchr(arg, ','); |
119 char *vspec, *vstr; | 129 char *vspec, *vstr; |
120 int vtype = -1, ret = DMERR_OK; | 130 int vtype = -1, ret = DMERR_OK; |
121 uint32_t vval; | 131 Uint32 vval; |
132 | |
133 if (setMode != FA_ANALYZE && setMode != mode) | |
134 { | |
135 dmErrorMsg("Options specifying multiple operating modes can't be used.\n"); | |
136 return DMERR_INVALID_ARGS; | |
137 } | |
138 setMode = mode; | |
122 | 139 |
123 // Do we have spec? | 140 // Do we have spec? |
124 if (sep != NULL) | 141 if (sep != NULL) |
125 { | 142 { |
126 vspec = dm_strdup_trim(sep + 1, DM_TRIM_BOTH); | 143 vspec = dm_strdup_trim(sep + 1, DM_TRIM_BOTH); |
176 "Not a valid integer value '%s'.\n", | 193 "Not a valid integer value '%s'.\n", |
177 vstr); | 194 vstr); |
178 goto out; | 195 goto out; |
179 } | 196 } |
180 | 197 |
181 // Check if we need to guess size | 198 if (mode == FA_GREP) |
182 if (vtype < 0) | 199 { |
183 { | 200 // Check if we need to guess size |
184 for (int n = DMGV_last; n >= 0; n--) | 201 if (vtype < 0) |
185 { | 202 { |
186 const DMGrepDef *def = &dmGrepTypes[n]; | 203 for (int n = DMGV_last; n >= 0; n--) |
187 if (vval <= def->nmax) | 204 { |
188 vtype = n; | 205 const DMGrepDef *def = &dmGrepTypes[n]; |
189 } | 206 if (vval <= def->nmax) |
190 } | 207 vtype = n; |
191 | 208 } |
192 if (vtype < 0) | 209 } |
193 { | 210 |
194 ret = dmError(DMERR_INVALID_ARGS, | 211 if (vtype < 0) |
195 "Could not guess value type for '%s'.\n", | 212 { |
196 arg); | 213 ret = dmError(DMERR_INVALID_ARGS, |
197 goto out; | 214 "Could not guess value type for '%s'.\n", |
198 } | 215 arg); |
199 | 216 goto out; |
200 // Check range | 217 } |
201 if (vval > dmGrepTypes[vtype].nmax) | 218 |
202 { | 219 // Check range |
203 ret = dmError(DMERR_INVALID_ARGS, | 220 if (vval > dmGrepTypes[vtype].nmax) |
204 "Integer value %d <= %d <= %d out of range for type %s.\n", | 221 { |
205 vval, 0, dmGrepTypes[vtype].nmax, dmGrepTypes[vtype].name); | 222 ret = dmError(DMERR_INVALID_ARGS, |
206 | 223 "Integer value %d <= %d <= %d out of range for type %s.\n", |
207 goto out; | 224 vval, 0, dmGrepTypes[vtype].nmax, dmGrepTypes[vtype].name); |
225 | |
226 goto out; | |
227 } | |
228 } | |
229 else | |
230 if (mode == FA_OFFSET) | |
231 { | |
232 if (vtype < 0) | |
233 vtype = DMGV_uint8; | |
208 } | 234 } |
209 | 235 |
210 if (nsetGrepValues < SET_MAX_VALUES) | 236 if (nsetGrepValues < SET_MAX_VALUES) |
211 { | 237 { |
212 DMGrepValue *node = &setGrepValues[nsetGrepValues++]; | 238 DMGrepValue *node = &setGrepValues[nsetGrepValues++]; |
218 vval, vval); | 244 vval, vval); |
219 } | 245 } |
220 else | 246 else |
221 { | 247 { |
222 ret = dmError(DMERR_BOUNDS, | 248 ret = dmError(DMERR_BOUNDS, |
223 "Too many greps specified (max %d).", | 249 "Too many values specified (max %d).", |
224 SET_MAX_VALUES); | 250 SET_MAX_VALUES); |
225 } | 251 } |
226 | 252 |
227 out: | 253 out: |
228 dmFree(vspec); | 254 dmFree(vspec); |
245 case 1: | 271 case 1: |
246 dmVerbosity++; | 272 dmVerbosity++; |
247 break; | 273 break; |
248 | 274 |
249 case 2: | 275 case 2: |
250 return argParseGrepValue(optArg) == DMERR_OK; | 276 return argParseGrepValue(optArg, FA_GREP) == DMERR_OK; |
277 | |
278 case 3: | |
279 return argParseGrepValue(optArg, FA_OFFSET) == DMERR_OK; | |
251 | 280 |
252 default: | 281 default: |
253 dmErrorMsg("Unknown argument '%s'.\n", currArg); | 282 dmErrorMsg("Unknown argument '%s'.\n", currArg); |
254 return FALSE; | 283 return FALSE; |
255 } | 284 } |
305 } | 334 } |
306 printf("\n\n"); | 335 printf("\n\n"); |
307 } | 336 } |
308 | 337 |
309 | 338 |
339 BOOL dmGetData(const int type, const DMSourceFile *file, const size_t offs, Uint32 *mval) | |
340 { | |
341 Uint8 *data = file->data + offs; | |
342 if (offs + dmGrepTypes[type].bsize >= file->size) | |
343 return FALSE; | |
344 | |
345 switch (type) | |
346 { | |
347 case DMGV_uint8: | |
348 *mval = *((uint8_t *) data); | |
349 break; | |
350 | |
351 case DMGV_uint16_le: | |
352 *mval = DM_LE16_TO_NATIVE(*((Uint16 *) data)); | |
353 break; | |
354 | |
355 case DMGV_uint16_be: | |
356 *mval = DM_BE16_TO_NATIVE(*((Uint16 *) data)); | |
357 break; | |
358 | |
359 case DMGV_uint32_le: | |
360 *mval = DM_LE32_TO_NATIVE(*((Uint32 *) data)); | |
361 break; | |
362 | |
363 case DMGV_uint32_be: | |
364 *mval = DM_BE32_TO_NATIVE(*((Uint32 *) data)); | |
365 break; | |
366 | |
367 default: | |
368 return FALSE; | |
369 } | |
370 return TRUE; | |
371 } | |
372 | |
373 | |
310 int main(int argc, char *argv[]) | 374 int main(int argc, char *argv[]) |
311 { | 375 { |
312 DMCompElem *compBuf = NULL; | 376 DMCompElem *compBuf = NULL; |
313 size_t compBufSize = 0, totalSize = 0; | 377 size_t compBufSize = 0, totalSize = 0; |
314 int res; | 378 int res; |
349 totalSize += file->size; | 413 totalSize += file->size; |
350 dmInitStats(&file->stats); | 414 dmInitStats(&file->stats); |
351 } | 415 } |
352 | 416 |
353 | 417 |
354 // Check if we are in grep mode | 418 // |
355 if (nsetGrepValues > 0) | 419 // Check what operating mode we are in |
420 // | |
421 if (setMode == FA_GREP) | |
356 { | 422 { |
357 for (int nfile = 0; nfile < nsrcFiles; nfile++) | 423 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
358 { | 424 { |
359 DMSourceFile *file = &srcFiles[nfile]; | 425 DMSourceFile *file = &srcFiles[nfile]; |
360 dmPrint(0, "\n%s:\n", file->filename); | 426 dmPrint(0, "\n%s\n", file->filename); |
361 | 427 |
362 for (int n = 0; n < nsetGrepValues; n++) | 428 for (int n = 0; n < nsetGrepValues; n++) |
363 { | 429 { |
364 DMGrepValue *node = &setGrepValues[n]; | 430 DMGrepValue *node = &setGrepValues[n]; |
365 const DMGrepDef *def = &dmGrepTypes[node->type]; | 431 const DMGrepDef *def = &dmGrepTypes[node->type]; |
366 | 432 |
367 for (size_t offs = 0; offs + def->bsize < file->size; offs++) | 433 for (size_t offs = 0; offs + def->bsize < file->size; offs++) |
368 { | 434 { |
369 uint32_t mval = -1; | 435 Uint32 mval; |
370 switch (node->type) | 436 dmGetData(node->type, file, offs, &mval); |
371 { | |
372 case DMGV_uint8: | |
373 mval = *((uint8_t *) (file->data + offs)); | |
374 break; | |
375 | |
376 case DMGV_uint16_le: | |
377 mval = DM_LE16_TO_NATIVE(*((uint16_t *) (file->data + offs))); | |
378 break; | |
379 | |
380 case DMGV_uint16_be: | |
381 mval = DM_BE16_TO_NATIVE(*((uint16_t *) (file->data + offs))); | |
382 break; | |
383 | |
384 case DMGV_uint32_le: | |
385 mval = DM_LE32_TO_NATIVE(*((uint32_t *) (file->data + offs))); | |
386 break; | |
387 | |
388 case DMGV_uint32_be: | |
389 mval = DM_BE32_TO_NATIVE(*((uint32_t *) (file->data + offs))); | |
390 break; | |
391 } | |
392 | 437 |
393 if (mval == node->value) | 438 if (mval == node->value) |
394 { | 439 { |
395 dmPrint(0, "%08x : %s match %d / 0x%x\n", | 440 dmPrint(0, "%08x : %s match %d / 0x%x\n", |
396 offs, def->name, mval, mval); | 441 offs, def->name, mval, mval); |
397 | |
398 } | 442 } |
399 } | 443 } |
400 } | 444 } |
401 } | 445 } |
402 goto out; | 446 } |
403 } | 447 else |
404 | 448 if (setMode == FA_OFFSET) |
405 // Allocate comparision buffer | 449 { |
406 // XXX: integer overflow? | 450 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
407 dmPrint(2, "Allocating %d element (%d bytes) comparision buffer.\n", | 451 { |
408 compBufSize, compBufSize * sizeof(DMCompElem)); | 452 DMSourceFile *file = &srcFiles[nfile]; |
409 | 453 dmPrint(1, "#%03d: %s\n", nfile + 1, file->filename); |
410 if ((compBuf = dmCalloc(compBufSize, sizeof(DMCompElem))) == NULL) | 454 } |
411 { | 455 |
412 dmErrorMsg("Out of memory. Could not allocate comparision buffer!\n"); | 456 printf(" offset :"); |
413 goto out; | 457 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
414 } | 458 printf(" %03d ", nfile + 1); |
415 | 459 printf("\n"); |
416 // Begin analyzing .. | 460 |
417 dmPrint(2, "Analyzing ..\n"); | 461 printf("=========="); |
418 for (int nfile = 0; nfile < nsrcFiles; nfile++) | 462 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
419 { | 463 printf("==========="); |
420 DMSourceFile *file = &srcFiles[nfile]; | 464 printf("\n"); |
421 | 465 |
422 for (size_t offs = 0; offs < file->size; offs++) | 466 for (int n = 0; n < nsetGrepValues; n++) |
423 { | 467 { |
424 Uint8 bv = file->data[offs]; | 468 DMGrepValue *node = &setGrepValues[n]; |
425 totalStats.cv[bv].count++; | 469 const DMGrepDef *def = &dmGrepTypes[node->type]; |
426 file->stats.cv[bv].count++; | 470 printf("%08x : ", node->value); |
471 | |
472 for (int nfile = 0; nfile < nsrcFiles; nfile++) | |
473 { | |
474 DMSourceFile *file = &srcFiles[nfile]; | |
475 Uint32 mval; | |
476 char mfmt[32]; | |
477 int npad, nwidth; | |
478 | |
479 if (dmGetData(node->type, file, node->value, &mval)) | |
480 { | |
481 snprintf(mfmt, sizeof(mfmt), "%%0%dx", def->bsize * 2); | |
482 nwidth = def->bsize * 2; | |
483 } | |
484 else | |
485 { | |
486 strcpy(mfmt, "----"); | |
487 nwidth = 4; | |
488 } | |
489 | |
490 npad = (10 - nwidth) / 2; | |
491 for (int q = 0; q < npad; q++) | |
492 putchar(' '); | |
493 | |
494 printf(mfmt, mval); | |
495 | |
496 for (int q = 0; q < npad; q++) | |
497 putchar(' '); | |
498 } | |
499 printf("\n"); | |
500 } | |
501 } | |
502 else | |
503 if (setMode == FA_ANALYZE) | |
504 { | |
505 // Allocate comparision buffer | |
506 // XXX: integer overflow? | |
507 dmPrint(2, "Allocating %d element (%d bytes) comparision buffer.\n", | |
508 compBufSize, compBufSize * sizeof(DMCompElem)); | |
509 | |
510 if ((compBuf = dmCalloc(compBufSize, sizeof(DMCompElem))) == NULL) | |
511 { | |
512 dmErrorMsg("Out of memory. Could not allocate comparision buffer!\n"); | |
513 goto out; | |
514 } | |
515 | |
516 // Begin analyzing .. | |
517 dmPrint(2, "Analyzing ..\n"); | |
518 for (int nfile = 0; nfile < nsrcFiles; nfile++) | |
519 { | |
520 DMSourceFile *file = &srcFiles[nfile]; | |
521 | |
522 for (size_t offs = 0; offs < file->size; offs++) | |
523 { | |
524 Uint8 bv = file->data[offs]; | |
525 totalStats.cv[bv].count++; | |
526 file->stats.cv[bv].count++; | |
527 } | |
528 | |
529 for (size_t offs = 0; offs < compBufSize; offs++) | |
530 { | |
531 Uint8 data = offs < file->size ? file->data[offs] : 0; | |
532 compBuf[offs].stats[data]++; | |
533 } | |
427 } | 534 } |
428 | 535 |
429 for (size_t offs = 0; offs < compBufSize; offs++) | 536 for (size_t offs = 0; offs < compBufSize; offs++) |
430 { | 537 { |
431 Uint8 data = offs < file->size ? file->data[offs] : 0; | 538 DMCompElem *el = &compBuf[offs]; |
432 compBuf[offs].stats[data]++; | 539 for (int n = 0; n < SET_MAX_ELEMS; n++) |
433 } | 540 { |
434 } | 541 if (el->stats[n] > 0) |
435 | 542 { |
436 for (size_t offs = 0; offs < compBufSize; offs++) | 543 el->variants++; |
437 { | 544 el->data = n; |
438 DMCompElem *el = &compBuf[offs]; | 545 } |
439 for (int n = 0; n < SET_MAX_ELEMS; n++) | 546 } |
440 { | 547 } |
441 if (el->stats[n] > 0) | 548 |
442 { | 549 // Display results |
443 el->variants++; | 550 for (size_t offs = 0, n = 0; offs < compBufSize; offs++) |
444 el->data = n; | 551 { |
445 } | 552 DMCompElem *el = &compBuf[offs]; |
446 } | 553 BOOL var = el->variants > 1; |
447 } | 554 |
448 | 555 if (n == 0) |
449 // Display results | 556 printf("%08" DM_PRIx_SIZE_T " | ", offs); |
450 for (size_t offs = 0, n = 0; offs < compBufSize; offs++) | 557 |
451 { | 558 if (var) |
452 DMCompElem *el = &compBuf[offs]; | 559 printf("[%2d] ", el->variants); |
453 BOOL var = el->variants > 1; | 560 else |
454 | 561 printf(" %02x ", el->data); |
455 if (n == 0) | 562 |
456 printf("%08" DM_PRIx_SIZE_T " | ", offs); | 563 if (++n >= 16) |
457 | 564 { |
458 if (var) | 565 printf("\n"); |
459 printf("[%2d] ", el->variants); | 566 n = 0; |
460 else | 567 } |
461 printf(" %02x ", el->data); | 568 } |
462 | 569 |
463 if (++n >= 16) | 570 printf("\n"); |
464 { | 571 |
465 printf("\n"); | 572 // Attempt further analysis |
466 n = 0; | 573 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
467 } | 574 { |
468 } | 575 DMSourceFile *file = &srcFiles[nfile]; |
469 | 576 size_t len = file->size > compBufSize ? compBufSize : file->size; |
470 printf("\n"); | 577 for (size_t offs = 0; offs + 4 < len; offs++) |
471 | 578 { |
472 // Attempt further analysis | 579 DMCompElem *elem = &compBuf[offs]; |
473 for (int nfile = 0; nfile < nsrcFiles; nfile++) | 580 |
474 { | 581 for (int variant = 3; variant >= 0; variant--) |
475 DMSourceFile *file = &srcFiles[nfile]; | 582 { |
476 size_t len = file->size > compBufSize ? compBufSize : file->size; | 583 size_t nmax = (variant < 2) ? sizeof(Uint16) : sizeof(Uint32); |
477 for (size_t offs = 0; offs + 4 < len; offs++) | 584 Uint32 tmp = 0; |
585 | |
586 for (size_t n = 0; n < nmax; n++) | |
587 { | |
588 size_t boffs = (variant & 1) ? n : nmax - n; | |
589 | |
590 tmp <<= 8; | |
591 tmp |= file->data[offs + boffs]; | |
592 } | |
593 | |
594 if (file->size - tmp < 32) | |
595 { | |
596 elem->interest[variant] += 32 - (file->size - tmp); | |
597 elem->interestF[variant]++; | |
598 } | |
599 } | |
600 } | |
601 } | |
602 | |
603 printf("\nMore findings:\n"); | |
604 for (size_t offs = 0; offs + 4 < compBufSize; offs++) | |
478 { | 605 { |
479 DMCompElem *elem = &compBuf[offs]; | 606 DMCompElem *elem = &compBuf[offs]; |
480 | 607 |
481 for (int variant = 3; variant >= 0; variant--) | 608 for (int variant = 0; variant < 4; variant++) |
482 { | 609 if (elem->interestF[variant] > 0) |
483 size_t nmax = (variant < 2) ? sizeof(Uint16) : sizeof(Uint32); | 610 { |
484 Uint32 tmp = 0; | 611 printf("%08" DM_PRIx_SIZE_T " | V%d : %d / %d\n", |
485 | 612 offs, variant, |
486 for (size_t n = 0; n < nmax; n++) | 613 elem->interestF[variant], elem->interest[variant]); |
487 { | 614 } |
488 size_t boffs = (variant & 1) ? n : nmax - n; | 615 } |
489 | 616 |
490 tmp <<= 8; | 617 printf("\nGlobal most used bytes:\n"); |
491 tmp |= file->data[offs + boffs]; | 618 dmPrintStats(&totalStats, 16, totalSize); |
492 } | 619 |
493 | 620 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
494 if (file->size - tmp < 32) | 621 { |
495 { | 622 DMSourceFile *file = &srcFiles[nfile]; |
496 elem->interest[variant] += 32 - (file->size - tmp); | 623 printf("Most used bytes for '%s':\n", file->filename); |
497 elem->interestF[variant]++; | 624 dmPrintStats(&file->stats, 16, file->size); |
498 } | 625 } |
499 } | 626 } |
500 } | 627 else |
501 } | 628 { |
502 | 629 dmErrorMsg("Invalid operating mode?\n"); |
503 printf("\nMore findings:\n"); | |
504 for (size_t offs = 0; offs + 4 < compBufSize; offs++) | |
505 { | |
506 DMCompElem *elem = &compBuf[offs]; | |
507 | |
508 for (int variant = 0; variant < 4; variant++) | |
509 if (elem->interestF[variant] > 0) | |
510 { | |
511 printf("%08" DM_PRIx_SIZE_T " | V%d : %d / %d\n", | |
512 offs, variant, | |
513 elem->interestF[variant], elem->interest[variant]); | |
514 } | |
515 } | |
516 | |
517 printf("\nGlobal most used bytes:\n"); | |
518 dmPrintStats(&totalStats, 16, totalSize); | |
519 | |
520 for (int nfile = 0; nfile < nsrcFiles; nfile++) | |
521 { | |
522 DMSourceFile *file = &srcFiles[nfile]; | |
523 printf("Most used bytes for '%s':\n", file->filename); | |
524 dmPrintStats(&file->stats, 16, file->size); | |
525 } | 630 } |
526 | 631 |
527 out: | 632 out: |
528 for (int nfile = 0; nfile < nsrcFiles; nfile++) | 633 for (int nfile = 0; nfile < nsrcFiles; nfile++) |
529 { | 634 { |