time wrapper

ふと思い付き、gettimeofday()/setttimeofday()のテスト用に作成しました。
元ネタは、failmallocです。


とはいえ、思い付きでさらっと作成したのでバグがあるかも。



Linuxでは時間計測にはgettimeofday()をよく使用します。
時間取得には他にもありますが、なぜgettimeofday()を使用する理由は
システム全体の時間が欲しいが為です。
例えばclock_gettime()はプロセスが実際に動作した時間を返すので
実際の経過時間が分かりません。
かといってtime()では精度が足りません。


ここでgettimeofday()の欠点は時刻の取得なのでsettimeofday()で過去に戻されると、
問題が発生します。

gettimeofday()  //1:
settimeofday()  //ここで過去に戻す。
gettimeofday()  //2:1よりも過去になる。

//ここで2-1で経過時間を求める


で大抵はsettimeofday()など使わないので問題がないのですが、
上記の場合問題が発生します。


また、settimeofday()はroot権限が必要なのでデバッグ/テスト時には
使用したくありません。


しかしこれを使えばシステムに影響をあたえずにデバッグ/テストを行えます。

#include <stdio.h>      
#include <dlfcn.h>      
#include <unistd.h>     
#include <sys/time.h>

static void init(void) __attribute__((constructor));
static void fini(void) __attribute__((destructor));

static int (*lib_gettimeofday)(struct timeval *tv, struct timezone *tz);
static int (*lib_settimeofday)(const struct timeval *tv , const struct timezone *tz);

static int settime_flg = -1;
static struct timeval diff_time;

static void init(void){
  lib_gettimeofday = dlsym(RTLD_NEXT, "gettimeofday");
  if (lib_gettimeofday == 0){
    _exit(1);
  }

  lib_settimeofday = dlsym(RTLD_NEXT, "settimeofday");
  if (lib_settimeofday == 0){
    _exit(1);
  }
}

static void fini(void){
  return;
}

int gettimeofday(struct timeval *tv, struct timezone *tz){
  int ret;
  struct timeval now;
  double tmp;

  ret = lib_gettimeofday(&now, tz);
  if(settime_flg < 0){
    tv->tv_sec = now.tv_sec;
    tv->tv_usec = now.tv_usec;
    return ret;
  }

  tmp = ((double)now.tv_sec + (double)now.tv_usec/1000000) -
        ((double)diff_time.tv_sec + (double)diff_time.tv_usec/1000000);

  tv->tv_sec = (long)tmp;
  tv->tv_usec = tmp - tv->tv_sec;

  return 0;
}

int settimeofday(const struct timeval *tv , const struct timezone *tz){
  int ret;
  struct timeval now;
  double diff;
  struct timezone ltz;

  if(tz != NULL){
    ltz.tz_minuteswest = tz->tz_minuteswest;
    ltz.tz_dsttime = tz->tz_dsttime;
  }

  ret = lib_gettimeofday(&now, &ltz);
  if(ret < 0){
    return -1;
  }

  diff = ((double)now.tv_sec + (double)now.tv_usec/1000000) -
         ((double)tv->tv_sec + (double)tv->tv_usec/1000000);

  diff_time.tv_sec = (long)diff;
  diff_time.tv_usec = diff - diff_time.tv_sec;

  settime_flg = 1;

  return 0;
}


コンパイル & 実行
(テスト対象プログラム名がhoge)

$ gcc -Wall -D_GNU_SOURCE -fPIC -shared -o time_wrapper.so  time_wrapper.c -ldl
$ LD_PRELOAD="./time_wrapper.so" ./hoge


なんか型変換を行っている部分が不安。
もし誰か見ていてバグに気づいたら教えてください。