2016-08-21 30 views
1

可変長文字列を印刷しようとするとセグメント違反が発生します。 printfまたはログファイルで文字列を出力すると、文字列が正常に印刷されます。別の問題は、同じテストが別のマシンでうまく動作するマシンでのみクラッシュが発生していることです。なぜ私は本当に混乱しています。文字列を印刷しようとするとSegフォールトが発生する

編集:クラッシュを引き起こしている完全な作業コード私は下に貼り付けています。 クラッシュはCentos 6.3とCentos 6.5で発生します。クラッシュは、次の

#include <stdio.h> 
#include <stdarg.h> 
#include <stdint.h> 
#include <sys/time.h> 
#include <time.h> 

typedef struct { 
    uint64_t total_bytes_sent; 
    uint64_t total_bytes_received; 
    uint64_t total_blocks_sent; 
    uint64_t total_blocks_received; 
    uint64_t total_commands_sent; 
    uint64_t total_commands_received; 
    uint64_t time_to_process_data; 
    char  mark_sent_time[64]; 
    char  mark_received_time[64]; 
} csperf_stats_t; 

void 
csperf_stats_printf(const char *format, ...) 
{ 
    /* Write to file */ 
    va_list args; 

    /* Write to stdout */ 
    va_start(args, format); 
    vfprintf(stdout, format, args); 
    va_end(args); 
} 

void 
ansperf_stats_display(csperf_stats_t *stats) 
{ 
    if (!stats) { 
     return; 
    } 

    stats->total_blocks_sent = 1000; 
    stats->total_blocks_received = 2000; 
    stats->time_to_process_data = 22; 

    csperf_stats_printf("%3d %15s %10s %10zu %10zu %10zu  %10s " 
      "%10s\n\n", 
       0, "hi", "testing.", 
      stats->total_blocks_sent, stats->total_blocks_received, 
      stats->time_to_process_data, 
      "crash", "test"); 
} 

/* Get time in millisecond */ 
uint64_t 
csperf_network_get_time(char *buf) 
{ 
    char   fmt[64]; 
    struct tm  *tm; 
    struct timeval tv; 
    uint64_t s; 

    gettimeofday(&tv, NULL); 

    if (buf) { 
     if((tm = localtime(&tv.tv_sec)) != NULL) { 
      strftime(fmt, sizeof(fmt), "%Y-%m-%d %H:%M:%S.%03u", tm); 
      snprintf(buf, sizeof(fmt), fmt, tv.tv_usec); 
     } 
    } 
    s = tv.tv_sec * 1000LL; 
    return(s + tv.tv_usec/1000LL); 
} 

int main() 
{ 
    csperf_stats_t stats = { 0 }; 
    csperf_network_get_time(stats.mark_sent_time); 
    csperf_network_get_time(stats.mark_received_time); 
    printf("%s%s\n", stats.mark_sent_time, stats.mark_received_time); 
    ansperf_stats_display(&stats); 
} 

CentOSに5では見られないことは、それがクラッシュしたコードスニペットです。

stats-> mark_sent_time、stats-> mark_received_timeを印刷しようとすると、vfprintf()がクラッシュします。文字列をgdbに表示すると、文句はありません。

command->echo_timestamp = csperf_network_get_time(
     client->stats.mark_sent_time); 

void 
csperf_stats_printf(FILE *fd, const char *format, ...) 
{ 
    /* Write to file */ 
    va_list args; 

    va_start(args, format); 
    if (fd) { 
     vfprintf(fd, format, args); 
    } 
    va_end(args); 

    /* Write to stdout */ 
    va_start(args, format); 
    vfprintf(stdout, format, args); 
    va_end(args); 
} 

void 
ansperf_stats_display(csperf_stats_t *stats, FILE *fd) 
{ 
    static int header_displayed = 0; 
    static int cycle = 0; 
    char total_bytes_sent_str[50]; 
    char total_bytes_recv_str[50]; 

    if (!stats) { 
     return; 
    } 

    if (!header_displayed) { 
     csperf_stats_printf(fd, "%s%s", header, seperator_line); 
     header_displayed = 1; 
    } 

    csperf_common_calculate_size(total_bytes_sent_str, 
      stats->total_bytes_sent); 
    csperf_common_calculate_size(total_bytes_recv_str, 
      stats->total_bytes_received); 

    csperf_stats_printf(fd, "%3d %15s %10s %10zu %10zu %10zu  %10s " 
      "%10s\n\n", ++cycle, 
      total_bytes_sent_str, total_bytes_recv_str, 
      stats->total_blocks_sent, stats->total_blocks_received, 
      stats->time_to_process_data, 
      stats->mark_sent_time, stats->mark_received_time); 
} 

これは

char  mark_sent_time[100]; 
char  mark_received_time[100]; 

それは次のように設定され、次のように文字列は100バイトの配列であるGDBが

(gdb) p stats->mark_sent_time 
No symbol "stats" in current context. 
(gdb) f 2 
#2 0x08051f56 in ansperf_stats_display (stats=0x892ded4, fd=0x892e888) at /home/nikhil/csperf/src/csperf_stats.c:55 
55   csperf_stats_printf(fd, "%3d %15s %10s %10zu %10zu %10zu  %100s " 
(gdb) p stats->mark_sent_time 
$1 = "20160821 21325800007", '\000' <repeats 79 times> 
(gdb) p stats->mark_recei9ved_time 
There is no member named mark_recei9ved_time. 
(gdb) p stats->mark_received_time 
$2 = "20160821 21325800007", '\000' <repeats 79 times> 

を示すものですこの関数はタイムスタンプをstrinにコピーします。グラム "mark_sent_time"

uint64_t 
csperf_network_get_time(char *buf) 
{ 
    char   fmt[64]; 
    struct tm  *tm; 
    struct timeval tv; 
    uint64_t s; 

    gettimeofday(&tv, NULL); 

    if (buf) { 
     if((tm = localtime(&tv.tv_sec)) != NULL) { 
      strftime(fmt, sizeof(fmt), "%Y-%m-%d %H:%M:%S.%03u", tm); 
      snprintf(buf, sizeof(fmt), fmt, tv.tv_usec); 
     } 
    } 
    s = tv.tv_sec * 1000LL; 
    return(s + tv.tv_usec/1000LL); 
} 

バックトレース:

(gdb) bt 
#0 0x002b535e in _IO_vfprintf_internal (s=Cannot access memory at address 0xffffffff 
) at vfprintf.c:1603 
#1 0x08051de7 in csperf_stats_printf (fd=0x892e888, format=0x8079a6c "%3d %15s %10s %10zu %10zu %10zu  %100s %100s \n\n") at /home/nikhil/csperf/src/csperf_stats.c:23 
#2 0x08051f56 in ansperf_stats_display (stats=0x892ded4, fd=0x892e888) at /home/nikhil/csperf/src/csperf_stats.c:55 

#3 0x08050ad3 in csperf_client_shutdown (client=0x892deb0) at /home/nikhil/csperf/src/csperf_client.c:67 

私は何をしないのですか?

+1

問題を再現するのに十分なコードを教えていただけますか?おそらく 'FILE * 'が無効です。 –

+0

ファイルに印刷するコードを削除しましたが、次のvprintfでstdoutに印刷しようとするとクラッシュします – Nikhil

+0

そのテストを自分で複製できれば、問題を見つけ出すことができます。 –

答えて

2

問題は%zuを使用して、フォーマット文字列にuint64_t変数を表示することです。

これは、%zusize_tであり、(少なくとも私の64ビットシステムでは)これは64ビットです。しかし、32ビットシステムでは(私にとっては少なくとも)size_tは32ビットですが、uint64_t変数はまだva_list構造体に64ビットを置きます。 %zuは32ビットのみを消費するため、次のパラメータの代わりに使用されるva_listの32ビット値が残ります。

"%zu"の使用を"%" PRIu64に置き換えるときは、uint64_t変数(例では少なくとも3桁)を印刷しているとき。

PRIu64にアクセスするには、<inttypes.h>ヘッダーを含める必要があります。

....そして、私はコメントで述べたように、私はこのことを考えていない:

strftime(fmt, sizeof(fmt), "%Y-%m-%d %H:%M:%S.%03u", tm); 
snprintf(buf, sizeof(fmt), fmt, tv.tv_usec); 

は、あなたが期待する何をしています。私はあなたがおそらくstrftime呼び出しで%%03uを使用する必要があります、しかしstrftimeため%uが数字として曜日である、あなたはおそらくstrftimeラインで%03utv.tv_usecを消費するsnprintfラインに持ち越さしたいと思って、その後、あなたのfmtsnprintfには、ちょうど%03uが含まれます。

関連する問題