Fossil

Check-in [e28433a692]
Login

Check-in [e28433a692]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Added fossil_timer_xxx() family of functions and g.mainTimerId.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e28433a692fa606e3f12b47aa6d961cc409fd6a3
User & Date: stephan 2013-05-08 19:45:33.718
Context
2013-05-08
19:54
Added fossil_timer_is_active(timerId), removed fossil_has_timer(). ... (check-in: 590406df3c user: stephan tags: trunk)
19:45
Added fossil_timer_xxx() family of functions and g.mainTimerId. ... (check-in: e28433a692 user: stephan tags: trunk)
18:34
Update SQLite to the latest 3.7.17 preview from upstream, for the purpose of testing SQLite. ... (check-in: 21a6a8bc00 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/json.c.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  "requestId" /*requestId*/,
  "resultCode" /*resultCode*/,
  "resultText" /*resultText*/,
  "timestamp" /*timestamp*/
};


/* Timer code taken from sqlite3's shell.c, modified slightly.
   FIXME: move the timer into the fossil core API so that we can
   start the timer early on in the app init phase. Right now we're
   just timing the json ops themselves.
*/
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
#include <sys/time.h>
#include <sys/resource.h>

/* Saved resource information for the beginning of an operation */
static struct rusage sBegin;

/*
** Begin timing an operation
*/
static void beginTimer(void){
  getrusage(RUSAGE_SELF, &sBegin);
}

/* Return the difference of two time_structs in milliseconds */
static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
  return ((pEnd->tv_usec - pStart->tv_usec)*0.001 +
          (double)((pEnd->tv_sec - pStart->tv_sec)*1000.0));
}

/*
** Print the timing results.
*/
static double endTimer(void){
  struct rusage sEnd;
  getrusage(RUSAGE_SELF, &sEnd);
#if 0
  printf("CPU Time: user %f sys %f\n",
         timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
         timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
#endif
  return timeDiff(&sBegin.ru_utime, &sEnd.ru_utime)
    + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime);
}

#define BEGIN_TIMER beginTimer()
#define END_TIMER endTimer()
#define HAS_TIMER 1

#elif (defined(_WIN32) || defined(WIN32))

#include <windows.h>

/* Saved resource information for the beginning of an operation */
static HANDLE hProcess;
static FILETIME ftKernelBegin;
static FILETIME ftUserBegin;
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
static GETPROCTIMES getProcessTimesAddr = NULL;

/*
** Check to see if we have timer support.  Return 1 if necessary
** support found (or found previously).
*/
static int hasTimer(void){
  if( getProcessTimesAddr ){
    return 1;
  } else {
    /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions.
    ** See if the version we are running on has it, and if it does, save off
    ** a pointer to it and the current process handle.
    */
    hProcess = GetCurrentProcess();
    if( hProcess ){
      HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
      if( NULL != hinstLib ){
        getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
        if( NULL != getProcessTimesAddr ){
          return 1;
        }
        FreeLibrary(hinstLib);
      }
    }
  }
  return 0;
}

/*
** Begin timing an operation
*/
static void beginTimer(void){
  if( getProcessTimesAddr ){
    FILETIME ftCreation, ftExit;
    getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin);
  }
}

/* Return the difference of two FILETIME structs in milliseconds */
static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
  sqlite_int64 i64Start = *((sqlite_int64 *) pStart);
  sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
  return (double) ((i64End - i64Start) / 10000.0);
}

/*
** Print the timing results.
*/
static double endTimer(void){
  if(getProcessTimesAddr){
    FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
    getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd);
    return timeDiff(&ftUserBegin, &ftUserEnd) +
      timeDiff(&ftKernelBegin, &ftKernelEnd);
  }
  return 0.0;
}

#define BEGIN_TIMER beginTimer()
#define END_TIMER endTimer()
#define HAS_TIMER hasTimer()

#else
#define BEGIN_TIMER
#define END_TIMER 0.0
#define HAS_TIMER 0
#endif

/*
** Returns true (non-0) if fossil appears to be running in JSON mode.
*/
char fossil_has_json(){
  return g.json.isJsonMode && (g.isHTTP || g.json.post.o);
}







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







51
52
53
54
55
56
57

























































































































58
59
60
61
62
63
64
  "requestId" /*requestId*/,
  "resultCode" /*resultCode*/,
  "resultText" /*resultText*/,
  "timestamp" /*timestamp*/
};




























































































































/*
** Returns true (non-0) if fossil appears to be running in JSON mode.
*/
char fossil_has_json(){
  return g.json.isJsonMode && (g.isHTTP || g.json.post.o);
}
812
813
814
815
816
817
818

819
820
821
822
823
824
825
** Initializes g.json.gc and g.json.param. This code does not (and
** must not) rely on any of the fossil environment having been set
** up. e.g. it must not use cgi_parameter() and friends because this
** must be called before those data are initialized.
*/
void json_main_bootstrap(){
  cson_value * v;

  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in.
  */
  v = cson_value_new_array();







>







691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
** Initializes g.json.gc and g.json.param. This code does not (and
** must not) rely on any of the fossil environment having been set
** up. e.g. it must not use cgi_parameter() and friends because this
** must be called before those data are initialized.
*/
void json_main_bootstrap(){
  cson_value * v;
  g.json.timerId = fossil_timer_start();
  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in.
  */
  v = cson_value_new_array();
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559


1560
1561
1562
1563
1564
1565
1566
1567
    if(0){/*Only for debugging, add some info to the response.*/
      tmp = cson_value_new_integer( g.json.cmd.offset );
      cson_object_set( o, "cmd.offset", tmp );
      cson_object_set( o, "isCGI", cson_value_new_bool( g.isHTTP ) );
    }
  }

  if(HAS_TIMER){
    /* This is, philosophically speaking, not quite the right place
       for ending the timer, but this is the one function which all of
       the JSON exit paths use (and they call it after processing,
       just before they end).
    */
    double span;
    span = END_TIMER;
    /* I'm actually seeing sub-ms runtimes in some tests, but a time of
       0 is "just wrong", so we'll bump that up to 1ms.
    */


    cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)((span>1.0)?span:1)));
  }
  if(g.json.warnings){
    tmp = cson_array_value(g.json.warnings);
    SET("warnings");
  }

  /* Only add the payload to SUCCESS responses. Else delete it. */







|





<
|
|
|

>
>
|







1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434

1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
    if(0){/*Only for debugging, add some info to the response.*/
      tmp = cson_value_new_integer( g.json.cmd.offset );
      cson_object_set( o, "cmd.offset", tmp );
      cson_object_set( o, "isCGI", cson_value_new_bool( g.isHTTP ) );
    }
  }

  if(fossil_has_timer()){
    /* This is, philosophically speaking, not quite the right place
       for ending the timer, but this is the one function which all of
       the JSON exit paths use (and they call it after processing,
       just before they end).
    */

    sqlite3_uint64 span = fossil_timer_fetch(g.json.timerId);
    /* I'm actually seeing sub-uSec runtimes in some tests, but a time of
       0 is "just kinda wrong".
    */
    cson_object_set(o,"procTimeUs", cson_value_new_integer((cson_int_t)span));
    span /= 1000/*for milliseconds */;
    cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)span));
  }
  if(g.json.warnings){
    tmp = cson_array_value(g.json.warnings);
    SET("warnings");
  }

  /* Only add the payload to SUCCESS responses. Else delete it. */
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
**
** Pages under /json/... must be entered into JsonPageDefs.
** This function dispatches them, and is the HTTP equivalent of
** json_cmd_top().
*/
void json_page_top(void){
  char const * zCommand;
  BEGIN_TIMER;
  json_mode_bootstrap();
  zCommand = json_command_arg(1);
  if(!zCommand || !*zCommand){
    json_dispatch_missing_args_err( JsonPageDefs,
                                    "No command (sub-path) specified."
                                    " Try one of: ");
    return;







<







2213
2214
2215
2216
2217
2218
2219

2220
2221
2222
2223
2224
2225
2226
**
** Pages under /json/... must be entered into JsonPageDefs.
** This function dispatches them, and is the HTTP equivalent of
** json_cmd_top().
*/
void json_page_top(void){
  char const * zCommand;

  json_mode_bootstrap();
  zCommand = json_command_arg(1);
  if(!zCommand || !*zCommand){
    json_dispatch_missing_args_err( JsonPageDefs,
                                    "No command (sub-path) specified."
                                    " Try one of: ");
    return;
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
** Run '%fossil json' without any subcommand to see the full list (but be
** aware that some listed might not yet be fully implemented).
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 0;
  BEGIN_TIMER;
  memset( &g.perm, 0xff, sizeof(g.perm) )
    /* In CLI mode fossil does not use permissions
       and they all default to false. We enable them
       here because (A) fossil doesn't use them in local
       mode but (B) having them set gives us one less
       difference in the CLI/CGI/Server-mode JSON
       handling.







<







2270
2271
2272
2273
2274
2275
2276

2277
2278
2279
2280
2281
2282
2283
** Run '%fossil json' without any subcommand to see the full list (but be
** aware that some listed might not yet be fully implemented).
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 0;

  memset( &g.perm, 0xff, sizeof(g.perm) )
    /* In CLI mode fossil does not use permissions
       and they all default to false. We enable them
       here because (A) fossil doesn't use them in local
       mode but (B) having them set gives us one less
       difference in the CLI/CGI/Server-mode JSON
       handling.
Changes to src/main.c.
216
217
218
219
220
221
222

223
224
225
226
227
228
229
  char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
  const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
  const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
  int anAuxCols[MX_AUX];         /* Number of columns for option() values */

  int allowSymlinks;             /* Cached "allow-symlinks" option */


#ifdef FOSSIL_ENABLE_JSON
  struct FossilJsonBits {
    int isJsonMode;            /* True if running in JSON mode, else
                                  false. This changes how errors are
                                  reported. In JSON mode we try to
                                  always output JSON-form error
                                  responses and always exit() with







>







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
  const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
  const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
  int anAuxCols[MX_AUX];         /* Number of columns for option() values */

  int allowSymlinks;             /* Cached "allow-symlinks" option */

  int mainTimerId;               /* Set to fossil_timer_start() */
#ifdef FOSSIL_ENABLE_JSON
  struct FossilJsonBits {
    int isJsonMode;            /* True if running in JSON mode, else
                                  false. This changes how errors are
                                  reported. In JSON mode we try to
                                  always output JSON-form error
                                  responses and always exit() with
264
265
266
267
268
269
270

271
272
273
274
275
276
277
      cson_object * o;
    } param;
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */
    cson_array * warnings;     /* response warnings */

  } json;
#endif /* FOSSIL_ENABLE_JSON */
};

/*
** Macro for debugging:
*/







>







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
      cson_object * o;
    } param;
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */
    cson_array * warnings;     /* response warnings */
    int timerId;               /* fetched from fossil_timer_start() */
  } json;
#endif /* FOSSIL_ENABLE_JSON */
};

/*
** Macro for debugging:
*/
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
#else
int main(int argc, char **argv)
#endif
{
  const char *zCmdName = "unknown";
  int idx;
  int rc;

  sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
  memset(&g, 0, sizeof(g));
  g.now = time(0);
#ifdef FOSSIL_ENABLE_JSON
#if defined(NDEBUG)
  g.json.errorDetailParanoia = 2 /* FIXME: make configurable
                                    One problem we have here is that this







|







511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
#else
int main(int argc, char **argv)
#endif
{
  const char *zCmdName = "unknown";
  int idx;
  int rc;
  int timerId;
  sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
  memset(&g, 0, sizeof(g));
  g.now = time(0);
#ifdef FOSSIL_ENABLE_JSON
#if defined(NDEBUG)
  g.json.errorDetailParanoia = 2 /* FIXME: make configurable
                                    One problem we have here is that this
532
533
534
535
536
537
538



539
540
541
542
543
544
545
#endif /* FOSSIL_ENABLE_JSON */
  expand_args_option(argc, argv);
#ifdef FOSSIL_ENABLE_TCL
  memset(&g.tcl, 0, sizeof(TclContext));
  g.tcl.argc = g.argc;
  g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
#endif



  if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
    zCmdName = "cgi";
    g.isHTTP = 1;
  }else if( g.argc<2 ){
    fossil_print(
       "Usage: %s COMMAND ...\n"
       "   or: %s help           -- for a list of common commands\n"







>
>
>







534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
#endif /* FOSSIL_ENABLE_JSON */
  expand_args_option(argc, argv);
#ifdef FOSSIL_ENABLE_TCL
  memset(&g.tcl, 0, sizeof(TclContext));
  g.tcl.argc = g.argc;
  g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
#endif

  g.mainTimerId = fossil_timer_start();
  
  if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
    zCmdName = "cgi";
    g.isHTTP = 1;
  }else if( g.argc<2 ){
    fossil_print(
       "Usage: %s COMMAND ...\n"
       "   or: %s help           -- for a list of common commands\n"
Changes to src/util.c.
15
16
17
18
19
20
21











22
23
24
25
26
27
28
**
*******************************************************************************
**
** This file contains code for miscellaneous utility routines.
*/
#include "config.h"
#include "util.h"












/*
** Exit.  Take care to close the database first.
*/
NORETURN void fossil_exit(int rc){
  db_close(1);
  exit(rc);







>
>
>
>
>
>
>
>
>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
**
*******************************************************************************
**
** This file contains code for miscellaneous utility routines.
*/
#include "config.h"
#include "util.h"

/*
** For the fossil_timer_xxx() family of functions...
*/
#ifdef _WIN32
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/resource.h>
#endif


/*
** Exit.  Take care to close the database first.
*/
NORETURN void fossil_exit(int rc){
  db_close(1);
  exit(rc);
132
133
134
135
136
137
138

















































































































































    return +1;
  }
  nByte = strlen(zB);
  rc = sqlite3_strnicmp(zA, zB, nByte);
  if( rc==0 && zA[nByte] ) rc = 1;
  return rc;
}
























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
    return +1;
  }
  nByte = strlen(zB);
  rc = sqlite3_strnicmp(zA, zB, nByte);
  if( rc==0 && zA[nByte] ) rc = 1;
  return rc;
}

/*
** Get user and kernel times in microseconds.
*/
void fossil_cpu_times(sqlite3_uint64 *piUser, sqlite3_uint64 *piKernel){
#ifdef _WIN32
  FILETIME not_used;
  FILETIME kernel_time;
  FILETIME user_time;
  GetProcessTimes(GetCurrentProcess(), &not_used, &not_used,
                  &kernel_time, &user_time);
  if( piUser ){
     *piUser = ((((sqlite3_uint64)user_time.dwHighDateTime)<<32) +
                         (sqlite3_uint64)user_time.dwLowDateTime + 5)/10;
  }
  if( piKernel ){
     *piKernel = ((((sqlite3_uint64)kernel_time.dwHighDateTime)<<32) +
                         (sqlite3_uint64)kernel_time.dwLowDateTime + 5)/10;
  }
#else
  struct rusage s;
  getrusage(RUSAGE_SELF, &s);
  if( piUser ){
    *piUser = ((sqlite3_uint64)s.ru_utime.tv_sec)*1000000 + s.ru_utime.tv_usec;
  }
  if( piKernel ){
    *piKernel = 
              ((sqlite3_uint64)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec;
  }
#endif
}

/*
** Internal helper type for fossil_timer_xxx().
 */
static struct FossilTimer {
  sqlite3_uint64 u; /* "User" CPU times */
  sqlite3_uint64 s; /* "System" CPU times */
  char used; /* 1 if timer is allocated, else 0. */
} fossilTimer = { 0U, 0U, 0 };
enum FossilTimerEnum {
  FOSSIL_TIMER_COUNT = 10 /* Number of timers we can stack. */
};
static struct FossilTimer fossilTimerList[FOSSIL_TIMER_COUNT] = {{0,0,0}};

/*
** Stores the current CPU times into the shared timer list
** and returns that timer's internal ID. Pass that ID to
** fossil_timer_fetch() to get the elapsed time for that
** timer.
**
** The system has a fixed number of timers, and they can be
** "deallocated" by passing this function's return value to
** fossil_timer_stop() Adjust FOSSIL_TIMER_COUNT to set the number of
** available timers.
**
** Returns -1 on error (no more timers available), with 0 or greater
** being valid timer IDs.
*/
int fossil_timer_start(){
  int i;
  static char once = 0;
  if(!once){
    memset(&fossilTimerList, 0,
           sizeof(fossilTimerList)/sizeof(fossilTimerList[0]));
  }
  for( i = 0; i < FOSSIL_TIMER_COUNT; ++i ){
    struct FossilTimer * ft = &fossilTimerList[i];
    if(ft->used) continue;
    ft->used = 1;
    fossil_cpu_times( &ft->u, &ft->s );
    break;
  }
  return (i<FOSSIL_TIMER_COUNT) ? i : -1;
}

/*
** Returns the difference in CPU times in microseconds since
** fossil_timer_start() was called and returned the given timer ID (or
** since it was last reset). Returns 0 if timerId is out of range.
*/
sqlite3_uint64 fossil_timer_fetch(int timerId){
  if(timerId<0 || timerId>=FOSSIL_TIMER_COUNT){
    return 0;
  }else{
    struct FossilTimer * start = &fossilTimerList[timerId];
    if( ! start->used ){
      fossil_fatal("Invalid call to reset a non-allocated "
                   "timer (#%d)", timerId);
      /*NOTREACHED*/
    }else{
      sqlite3_uint64 eu = 0, es = 0;
      fossil_cpu_times( &eu, &es );
      return (eu - start->u) + (es - start->s);
    }
  }
}

/*
** Resets the timer associated with the given ID, as obtained via
** fossil_timer_start(), to the current CPU time values.
*/
sqlite3_uint64 fossil_timer_reset(int timerId){
  if(timerId<0 || timerId>=FOSSIL_TIMER_COUNT){
    return 0;
  }else{
    struct FossilTimer * start = &fossilTimerList[timerId];
    if( ! start->used ){
      fossil_fatal("Invalid call to reset a non-allocated "
                   "timer (#%d)", timerId);
      /*NOTREACHED*/
    }else{
      sqlite3_uint64 const rc = fossil_timer_fetch(timerId);
      fossil_cpu_times( &start->u, &start->s );
      return rc;
    }
  }
}

/**
   "Deallocates" the fossil timer identified by the given timer ID.
   returns the difference (in uSec) between the last time that timer
   was started or reset. Returns 0 if timerId is out of range.  It is
   not legal to re-use the passed-in timerId after calling this
   until/unless it is re-initialized using fossil_timer_start() (NOT
   fossil_timer_reset()).
*/
sqlite3_uint64 fossil_timer_stop(int timerId){
  if(timerId<0 || timerId>=FOSSIL_TIMER_COUNT){
    return 0;
  }else{
    sqlite3_uint64 const rc = fossil_timer_fetch(timerId);
    struct FossilTimer * t = &fossilTimerList[timerId];
    t->used = 0;
    t->u = t->s = 0U;
    return rc;
  }
}

/*
** Returns 1 if fossil has timer capabilities.
*/
int fossil_has_timer(){
  return 1;
}