Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
groonga_benchmark.c
Go to the documentation of this file.
1 /* -*- c-basic-offset: 2 -*- */
2 /*
3  Copyright(C) 2010-2012 Brazil
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License version 2.1 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif /* HAVE_CONFIG_H */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 
30 #ifdef HAVE_SYS_WAIT_H
31 #include <sys/wait.h>
32 #endif /* HAVE_SYS_WAIT_H */
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif /* HAVE_SYS_SOCKET_H */
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif /* HAVE_NETINET_IN_H */
39 
40 #include "lib/str.h"
41 #include "lib/com.h"
42 #include "lib/db.h"
43 
44 #ifdef WIN32
45 #include <windows.h>
46 #include <stddef.h>
47 #else
48 #include <sys/param.h>
49 #include <sys/utsname.h>
50 #include <sys/statvfs.h>
51 #include <libgen.h>
52 #endif /* WIN32 */
53 
54 /*
55 #define DEBUG_FTP
56 #define DEBUG_HTTP
57 */
58 
59 #define FTPUSER "anonymous"
60 #define FTPPASSWD "grntest"
61 #define FTPSERVER "ftp.groonga.org"
62 #define FTPBUF 20000
63 #define DEFAULT_PORT 10041
64 #define DEFAULT_DEST "localhost"
65 
66 #define OUT_JSON 0
67 #define OUT_TSV 1
68 
69 static int grntest_outtype = OUT_JSON;
70 
71 static grn_critical_section grntest_cs;
72 
73 static int grntest_stop_flag = 0;
74 static int grntest_detail_on = 0;
75 static int grntest_remote_mode = 0;
76 static int grntest_localonly_mode = 0;
77 static int grntest_owndb_mode = 0;
78 static int grntest_onmemory_mode = 0;
79 static grn_bool grntest_ftp_mode = GRN_FALSE;
80 #define TMPFILE "_grntest.tmp"
81 
82 static grn_ctx grntest_server_context;
83 static FILE *grntest_log_file;
84 
85 #define OS_LINUX64 "LINUX64"
86 #define OS_LINUX32 "LINUX32"
87 #define OS_WINDOWS64 "WINDOWS64"
88 #define OS_WINDOWS32 "WINDOWS32"
89 
90 #ifdef WIN32
91 typedef SOCKET socket_t;
92 #define SOCKETERROR INVALID_SOCKET
93 #define socketclose closesocket
94 static const char *groonga_path = "groonga.exe";
95 static PROCESS_INFORMATION grntest_pi;
96 #else
97 static pid_t grntest_server_id = 0;
98 typedef int socket_t;
99 #define socketclose close
100 #define SOCKETERROR -1
101 static const char *groonga_path = "groonga";
102 #endif /* WIN32 */
103 
104 static const char *groonga_protocol = "gqtp";
105 static const char *grntest_osinfo;
106 static int grntest_sigint = 0;
107 
108 
109 
110 static grn_obj *grntest_db = NULL;
111 
112 #define MAX_CON_JOB 10
113 #define MAX_CON 64
114 
115 #define BUF_LEN 1024
116 #define MAX_PATH_LEN 256
117 
118 #define J_DO_LOCAL 1 /* do_local */
119 #define J_DO_GQTP 2 /* do_gqtp */
120 #define J_DO_HTTP 3 /* do_http */
121 #define J_REP_LOCAL 4 /* rep_local */
122 #define J_REP_GQTP 5 /* rep_gqtp */
123 #define J_REP_HTTP 6 /* rep_http */
124 #define J_OUT_LOCAL 7 /* out_local */
125 #define J_OUT_GQTP 8 /* out_gqtp */
126 #define J_OUT_HTTP 9 /* out_http */
127 #define J_TEST_LOCAL 10 /* test_local */
128 #define J_TEST_GQTP 11 /* test_gqtp */
129 #define J_TEST_HTTP 12 /* test_http */
130 
131 static char grntest_username[BUF_LEN];
132 static char grntest_scriptname[BUF_LEN];
133 static char grntest_date[BUF_LEN];
134 static char grntest_serverhost[BUF_LEN];
135 static int grntest_serverport;
136 static const char *grntest_dbpath;
137 
138 struct job {
141  int qnum;
142  int jobtype;
144  int ntimes;
145  int done;
146  long long int max;
147  long long int min;
148  FILE *outputlog;
149  FILE *inputlog;
151 };
152 
153 struct task {
154  char *file;
156  int jobtype;
157  int ntimes;
158  int qnum;
159  int job_id;
160  long long int max;
161  long long int min;
164 };
165 
166 static struct task grntest_task[MAX_CON];
167 static struct job grntest_job[MAX_CON];
168 static int grntest_jobdone;
169 static int grntest_jobnum;
170 static grn_ctx grntest_ctx[MAX_CON];
171 static grn_obj *grntest_owndb[MAX_CON];
172 
173 static grn_obj grntest_starttime, grntest_jobs_start;
174 
175 static int
176 grntest_atoi(const char *str, const char *end, const char **rest)
177 {
178  while (grn_isspace(str, GRN_ENC_UTF8) == 1) {
179  str++;
180  }
181  return grn_atoi(str, end, rest);
182 }
183 
184 static int
185 out_p(int jobtype)
186 {
187  if (jobtype == J_OUT_LOCAL) {
188  return 1;
189  }
190  if (jobtype == J_OUT_GQTP) {
191  return 1;
192  }
193  if (jobtype == J_OUT_HTTP) {
194  return 1;
195  }
196  return 0;
197 }
198 
199 static int
200 test_p(int jobtype)
201 {
202  if (jobtype == J_TEST_LOCAL) {
203  return 1;
204  }
205  if (jobtype == J_TEST_GQTP) {
206  return 1;
207  }
208  if (jobtype == J_TEST_HTTP) {
209  return 1;
210  }
211  return 0;
212 }
213 
214 static int
215 report_p(int jobtype)
216 {
217  if (jobtype == J_REP_LOCAL) {
218  return 1;
219  }
220  if (jobtype == J_REP_GQTP) {
221  return 1;
222  }
223  if (jobtype == J_REP_HTTP) {
224  return 1;
225  }
226  return 0;
227 }
228 
229 static int
230 gqtp_p(int jobtype)
231 {
232  if (jobtype == J_DO_GQTP) {
233  return 1;
234  }
235  if (jobtype == J_REP_GQTP) {
236  return 1;
237  }
238  if (jobtype == J_OUT_GQTP) {
239  return 1;
240  }
241  if (jobtype == J_TEST_GQTP) {
242  return 1;
243  }
244  return 0;
245 }
246 
247 static int
248 http_p(int jobtype)
249 {
250  if (jobtype == J_DO_HTTP) {
251  return 1;
252  }
253  if (jobtype == J_REP_HTTP) {
254  return 1;
255  }
256  if (jobtype == J_OUT_HTTP) {
257  return 1;
258  }
259  if (jobtype == J_TEST_HTTP) {
260  return 1;
261  }
262  return 0;
263 }
264 
265 static int
266 error_exit_in_thread(intptr_t code)
267 {
268  fprintf(stderr,
269  "Fatal error! Check script file or database!: %ld\n", (long)code);
270  fflush(stderr);
271  CRITICAL_SECTION_ENTER(grntest_cs);
272  grntest_stop_flag = 1;
273  CRITICAL_SECTION_LEAVE(grntest_cs);
274 #ifdef WIN32
275  _endthreadex(code);
276 #else
277  pthread_exit((void *)code);
278 #endif /* WIN32 */
279  return 0;
280 }
281 
282 
283 static void
284 escape_command(grn_ctx *ctx, const char *in, int ilen, grn_obj *escaped_command)
285 {
286  int i = 0;
287 
288  while (i < ilen) {
289  if ((in[i] == '\\') || (in[i] == '\"') || (in[i] == '/')) {
290  GRN_TEXT_PUTC(ctx, escaped_command, '\\');
291  GRN_TEXT_PUTC(ctx, escaped_command, in[i]);
292  i++;
293  } else {
294  switch (in[i]) {
295  case '\b':
296  GRN_TEXT_PUTS(ctx, escaped_command, "\\b");
297  i++;
298  break;
299  case '\f':
300  GRN_TEXT_PUTS(ctx, escaped_command, "\\f");
301  i++;
302  break;
303  case '\n':
304  GRN_TEXT_PUTS(ctx, escaped_command, "\\n");
305  i++;
306  break;
307  case '\r':
308  GRN_TEXT_PUTS(ctx, escaped_command, "\\r");
309  i++;
310  break;
311  case '\t':
312  GRN_TEXT_PUTS(ctx, escaped_command, "\\t");
313  i++;
314  break;
315  default:
316  GRN_TEXT_PUTC(ctx, escaped_command, in[i]);
317  i++;
318  break;
319  }
320  }
321  }
322  GRN_TEXT_PUTC(ctx, escaped_command, '\0');
323 }
324 
325 static int
326 report_command(grn_ctx *ctx, const char *command, const char *ret, int task_id,
327  grn_obj *start_time, grn_obj *end_time)
328 {
329  int i, len, clen;
330  long long int start, end;
331  grn_obj result, escaped_command;
332 
333  GRN_TEXT_INIT(&result, 0);
334  if (strncmp(ret, "[[", 2) == 0) {
335  i = 2;
336  len = 1;
337  while (ret[i] != ']') {
338  i++;
339  len++;
340  if (ret[i] == '\0') {
341  fprintf(stderr, "Error results:command=[%s]\n", command);
342  error_exit_in_thread(3);
343  }
344  }
345  len++;
346  grn_text_esc(ctx, &result, ret + 1, len);
347  } else {
348  grn_text_esc(ctx, &result, ret, strlen(ret));
349  }
350 
351  start = GRN_TIME_VALUE(start_time) - GRN_TIME_VALUE(&grntest_starttime);
352  end = GRN_TIME_VALUE(end_time) - GRN_TIME_VALUE(&grntest_starttime);
353  clen = strlen(command);
354  GRN_TEXT_INIT(&escaped_command, 0);
355  escape_command(ctx, command, clen, &escaped_command);
356  if (grntest_outtype == OUT_TSV) {
357  fprintf(grntest_log_file, "report\t%d\t%s\t%" GRN_FMT_LLD "\t%" GRN_FMT_LLD "\t%.*s\n",
358  task_id, GRN_TEXT_VALUE(&escaped_command), start, end,
359  (int)GRN_TEXT_LEN(&result), GRN_TEXT_VALUE(&result));
360  } else {
361  fprintf(grntest_log_file, "[%d, \"%s\", %" GRN_FMT_LLD ", %" GRN_FMT_LLD ", %.*s],\n",
362  task_id, GRN_TEXT_VALUE(&escaped_command), start, end,
363  (int)GRN_TEXT_LEN(&result), GRN_TEXT_VALUE(&result));
364  }
365  fflush(grntest_log_file);
366  GRN_OBJ_FIN(ctx, &escaped_command);
367  GRN_OBJ_FIN(ctx, &result);
368  return 0;
369 }
370 
371 static int
372 output_result_final(grn_ctx *ctx, int qnum)
373 {
374  grn_obj end_time;
375  long long int latency, self;
376  double sec, qps;
377 
378  GRN_TIME_INIT(&end_time, 0);
379  GRN_TIME_NOW(ctx, &end_time);
380 
381  latency = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_starttime);
382  self = latency;
383  sec = self / (double)1000000;
384  qps = (double)qnum / sec;
385  if (grntest_outtype == OUT_TSV) {
386  fprintf(grntest_log_file, "total\t%" GRN_FMT_LLD "\t%f\t%d\n", latency, qps, qnum);
387  } else {
388  fprintf(grntest_log_file,
389  "{\"total\": %" GRN_FMT_LLD ", \"qps\": %f, \"queries\": %d}]\n", latency, qps, qnum);
390  }
391  grn_obj_close(ctx, &end_time);
392  return 0;
393 }
394 
395 static int
396 output_sysinfo(char *sysinfo)
397 {
398  if (grntest_outtype == OUT_TSV) {
399  fprintf(grntest_log_file, "%s", sysinfo);
400  } else {
401  fprintf(grntest_log_file, "[%s\n", sysinfo);
402  }
403  return 0;
404 }
405 
406 /* #define ENABLE_ERROR_REPORT 1 */
407 #ifdef ENABLE_ERROR_REPORT
408 static int
409 error_command(grn_ctx *ctx, char *command, int task_id)
410 {
411  fprintf(stderr, "error!:command=[%s] task_id = %d\n", command, task_id);
412  fflush(stderr);
413  error_exit_in_thread(1);
414  return 0;
415 }
416 #endif
417 
418 static void
419 normalize_output(char *output, int length,
420  char **normalized_output, int *normalized_length)
421 {
422  int i;
423 
424  *normalized_output = NULL;
425  *normalized_length = length;
426  for (i = 0; i < length; i++) {
427  if (!strncmp(output + i, "],", 2)) {
428  *normalized_output = output + i + 2;
429  *normalized_length -= i + 2;
430  break;
431  }
432  }
433 
434  if (!*normalized_output) {
435  if (length > 2 && strncmp(output + length - 2, "]]", 2)) {
436  *normalized_output = output + length;
437  *normalized_length = 0;
438  } else {
439  *normalized_output = output;
440  }
441  }
442 }
443 
444 static grn_bool
445 same_result_p(char *expect, int expected_length, char *result, int result_length)
446 {
447  char *normalized_expected, *normalized_result;
448  int normalized_expected_length, normalized_result_length;
449 
450  normalize_output(expect, expected_length,
451  &normalized_expected, &normalized_expected_length);
452  normalize_output(result, result_length,
453  &normalized_result, &normalized_result_length);
454 
455  return((normalized_expected_length == normalized_result_length) &&
456  strncmp(normalized_expected, normalized_result,
457  normalized_expected_length) == 0);
458 }
459 
460 static socket_t
461 open_socket(const char *host, int port)
462 {
463  socket_t sock;
464  struct hostent *servhost;
465  struct sockaddr_in server;
466  u_long inaddr;
467  int ret;
468 
469  servhost = gethostbyname(host);
470  if (servhost == NULL){
471  fprintf(stderr, "Bad hostname [%s]\n", host);
472  return -1;
473  }
474  inaddr = *(u_long*)(servhost->h_addr_list[0]);
475 
476  memset(&server, 0, sizeof(struct sockaddr_in));
477  server.sin_family = AF_INET;
478  server.sin_port = htons(port);
479  server.sin_addr = *(struct in_addr*)&inaddr;
480 
481  sock = socket(AF_INET, SOCK_STREAM, 0);
482  if (sock == -1) {
483  fprintf(stderr, "socket error\n");
484  return -1;
485  }
486  ret = connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
487  if (ret == -1) {
488  fprintf(stderr, "connect error\n");
489  return -1;
490  }
491  return sock;
492 }
493 
494 static int
495 write_to_server(socket_t socket, const char *buf)
496 {
497 #ifdef DEBUG_FTP
498  fprintf(stderr, "send:%s", buf);
499 #endif
500  send(socket, buf, strlen(buf), 0);
501  return 0;
502 }
503 
504 #define OUTPUT_TYPE "output_type"
505 #define OUTPUT_TYPE_LEN (sizeof(OUTPUT_TYPE) - 1)
506 
507 static void
508 command_line_to_uri_path(grn_ctx *ctx, grn_obj *uri, const char *command)
509 {
510  char tok_type;
511  int offset = 0, have_key = 0;
512  const char *p, *e, *v;
513  grn_obj buf, *expr = NULL;
514  grn_expr_var *vars;
515  unsigned int nvars;
516 
517  GRN_TEXT_INIT(&buf, 0);
518  p = command;
519  e = command + strlen(command);
520  p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
521  if ((expr = grn_ctx_get(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)))) {
522  grn_obj params, output_type;
523 
524  GRN_TEXT_INIT(&params, 0);
525  GRN_TEXT_INIT(&output_type, 0);
526  vars = ((grn_proc *)expr)->vars;
527  nvars = ((grn_proc *)expr)->nvars;
528  GRN_TEXT_PUTS(ctx, uri, "/d/");
529  GRN_TEXT_PUT(ctx, uri, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
530  while (p < e) {
531  GRN_BULK_REWIND(&buf);
532  p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
533  v = GRN_TEXT_VALUE(&buf);
534  switch (tok_type) {
535  case GRN_TOK_VOID :
536  p = e;
537  break;
538  case GRN_TOK_SYMBOL :
539  if (GRN_TEXT_LEN(&buf) > 2 && v[0] == '-' && v[1] == '-') {
540  int l = GRN_TEXT_LEN(&buf) - 2;
541  v += 2;
542  if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
543  GRN_BULK_REWIND(&output_type);
544  p = grn_text_unesc_tok(ctx, &output_type, p, e, &tok_type);
545  break;
546  }
547  if (GRN_TEXT_LEN(&params)) {
548  GRN_TEXT_PUTS(ctx, &params, "&");
549  }
550  grn_text_urlenc(ctx, &params, v, l);
551  have_key = 1;
552  break;
553  }
554  /* fallthru */
555  case GRN_TOK_STRING :
556  case GRN_TOK_QUOTE :
557  if (!have_key) {
558  if (offset < nvars) {
559  if (GRN_TEXT_LEN(&params)) {
560  GRN_TEXT_PUTS(ctx, &params, "&");
561  }
562  grn_text_urlenc(ctx, &params,
563  vars[offset].name, vars[offset].name_size);
564  offset++;
565  }
566  }
567  GRN_TEXT_PUTS(ctx, &params, "=");
568  grn_text_urlenc(ctx, &params, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
569  have_key = 0;
570  break;
571  }
572  }
573  GRN_TEXT_PUTS(ctx, uri, ".");
574  if (GRN_TEXT_LEN(&output_type)) {
575  GRN_TEXT_PUT(ctx, uri,
576  GRN_TEXT_VALUE(&output_type), GRN_TEXT_LEN(&output_type));
577  } else {
578  GRN_TEXT_PUTS(ctx, uri, "json");
579  }
580  if (GRN_TEXT_LEN(&params) > 0) {
581  GRN_TEXT_PUTS(ctx, uri, "?");
582  GRN_TEXT_PUT(ctx, uri, GRN_TEXT_VALUE(&params), GRN_TEXT_LEN(&params));
583  }
584  GRN_OBJ_FIN(ctx, &params);
585  GRN_OBJ_FIN(ctx, &output_type);
586  }
587  GRN_OBJ_FIN(ctx, &buf);
588 }
589 
590 static void
591 command_send_http(grn_ctx *ctx, const char *command, int type, int task_id)
592 {
593  socket_t http_socket;
594  grn_obj buf;
595 
596  http_socket = open_socket(grntest_serverhost, grntest_serverport);
597  if (http_socket == SOCKETERROR) {
598  fprintf(stderr, "failed to connect to groonga at %s:%d via HTTP: ",
599  grntest_serverhost, grntest_serverport);
600 #ifdef WIN32
601  fprintf(stderr, "%d\n", GetLastError());
602 #else
603  fprintf(stderr, "%s\n", strerror(errno));
604 #endif
605  error_exit_in_thread(100);
606  }
607  grntest_task[task_id].http_socket = http_socket;
608  GRN_BULK_REWIND(&grntest_task[task_id].http_response);
609 
610  GRN_TEXT_INIT(&buf, 0);
611  GRN_TEXT_PUTS(ctx, &buf, "GET ");
612  if (strncmp(command, "/d/", 3) == 0) {
613  GRN_TEXT_PUTS(ctx, &buf, command);
614  } else {
615  command_line_to_uri_path(ctx, &buf, command);
616  }
617 #ifdef DEBUG_HTTP
618  fprintf(stderr, "command: <%s>\n", command);
619  fprintf(stderr, "path: <%.*s>\n",
620  (int)GRN_TEXT_LEN(&buf), GRN_TEXT_VALUE(&buf));
621 #endif
622  GRN_TEXT_PUTS(ctx, &buf, " HTTP/1.1\r\n");
623  GRN_TEXT_PUTS(ctx, &buf, "Host: ");
624  GRN_TEXT_PUTS(ctx, &buf, grntest_serverhost);
625  GRN_TEXT_PUTS(ctx, &buf, "\r\n");
626  GRN_TEXT_PUTS(ctx, &buf, "User-Agent: grntest/");
627  GRN_TEXT_PUTS(ctx, &buf, grn_get_version());
628  GRN_TEXT_PUTS(ctx, &buf, "\r\n");
629  GRN_TEXT_PUTS(ctx, &buf, "Connection: close\r\n");
630  GRN_TEXT_PUTS(ctx, &buf, "\r\n");
631  GRN_TEXT_PUTC(ctx, &buf, '\0');
632  write_to_server(http_socket, GRN_TEXT_VALUE(&buf));
633  GRN_OBJ_FIN(ctx, &buf);
634 }
635 
636 static void
637 command_send_ctx(grn_ctx *ctx, const char *command, int type, int task_id)
638 {
639  grn_ctx_send(ctx, command, strlen(command), 0);
640 /* fix me.
641  when command fails, ctx->rc is not 0 in local mode!
642  if (ctx->rc) {
643  fprintf(stderr, "ctx_send:rc=%d:command:%s\n", ctx->rc, command);
644  error_exit_in_thread(1);
645  }
646 */
647 }
648 
649 static void
650 command_send(grn_ctx *ctx, const char *command, int type, int task_id)
651 {
652  if (http_p(type)) {
653  command_send_http(ctx, command, type, task_id);
654  } else {
655  command_send_ctx(ctx, command, type, task_id);
656  }
657 }
658 
659 static void
660 command_recv_http(grn_ctx *ctx, int type, int task_id,
661  char **res, unsigned int *res_len, int *flags)
662 {
663  int len;
664  char buf[BUF_LEN];
665  char *p, *e;
666  socket_t http_socket;
667  grn_obj *http_response;
668 
669  http_socket = grntest_task[task_id].http_socket;
670  http_response = &grntest_task[task_id].http_response;
671  while ((len = recv(http_socket, buf, BUF_LEN - 1, 0))) {
672 #ifdef DEBUG_HTTP
673  fprintf(stderr, "receive: <%.*s>\n", len, buf);
674 #endif
675  GRN_TEXT_PUT(ctx, http_response, buf, len);
676  }
677 
678  p = GRN_TEXT_VALUE(http_response);
679  e = p + GRN_TEXT_LEN(http_response);
680  while (p < e) {
681  if (p[0] != '\r') {
682  p++;
683  continue;
684  }
685  if (e - p >= 4) {
686  if (!memcmp(p, "\r\n\r\n", 4)) {
687  *res = p + 4;
688  *res_len = e - *res;
689 #ifdef DEBUG_HTTP
690  fprintf(stderr, "body: <%.*s>\n", *res_len, *res);
691 #endif
692  break;
693  }
694  p += 4;
695  } else {
696  *res = NULL;
697  *res_len = 0;
698  break;
699  }
700  }
701 
702  socketclose(http_socket);
703  grntest_task[task_id].http_socket = 0;
704 }
705 
706 static void
707 command_recv_ctx(grn_ctx *ctx, int type, int task_id,
708  char **res, unsigned int *res_len, int *flags)
709 {
710  grn_ctx_recv(ctx, res, res_len, flags);
711  if (ctx->rc) {
712  fprintf(stderr, "ctx_recv:rc=%d\n", ctx->rc);
713  error_exit_in_thread(1);
714  }
715 }
716 
717 static void
718 command_recv(grn_ctx *ctx, int type, int task_id,
719  char **res, unsigned int *res_len, int *flags)
720 {
721  if (http_p(type)) {
722  command_recv_http(ctx, type, task_id, res, res_len, flags);
723  } else {
724  command_recv_ctx(ctx, type, task_id, res, res_len, flags);
725  }
726 }
727 
728 static int
729 shutdown_server(void)
730 {
731  char *res;
732  int flags;
733  unsigned int res_len;
734  int job_type;
735  int task_id = 0;
736 
737  if (grntest_remote_mode) {
738  return 0;
739  }
740  job_type = grntest_task[task_id].jobtype;
741  command_send(&grntest_server_context, "shutdown", job_type, task_id);
742  if (grntest_server_context.rc) {
743  fprintf(stderr, "ctx_send:rc=%d\n", grntest_server_context.rc);
744  exit(1);
745  }
746  command_recv(&grntest_server_context, job_type, task_id,
747  &res, &res_len, &flags);
748 
749  return 0;
750 }
751 
752 static int
753 do_load_command(grn_ctx *ctx, char *command, int type, int task_id,
754  long long int *load_start)
755 {
756  char *res;
757  unsigned int res_len;
758  int flags, ret;
759  grn_obj start_time, end_time;
760 
761  GRN_TIME_INIT(&start_time, 0);
762  if (*load_start == 0) {
763  GRN_TIME_NOW(ctx, &start_time);
764  *load_start = GRN_TIME_VALUE(&start_time);
765  } else {
766  GRN_TIME_SET(ctx, &start_time, *load_start);
767  }
768 
769  command_send(ctx, command, type, task_id);
770  do {
771  command_recv(ctx, type, task_id, &res, &res_len, &flags);
772  if (res_len) {
773  long long int self;
774  GRN_TIME_INIT(&end_time, 0);
775  GRN_TIME_NOW(ctx, &end_time);
776 
777  self = GRN_TIME_VALUE(&end_time) - *load_start;
778 
779  if (grntest_task[task_id].max < self) {
780  grntest_task[task_id].max = self;
781  }
782  if (grntest_task[task_id].min > self) {
783  grntest_task[task_id].min = self;
784  }
785 
786  if (report_p(grntest_task[task_id].jobtype)) {
787  char tmpbuf[BUF_LEN];
788 
789  if (res_len < BUF_LEN) {
790  strncpy(tmpbuf, res, res_len);
791  tmpbuf[res_len] = '\0';
792  } else {
793  strncpy(tmpbuf, res, BUF_LEN - 2);
794  tmpbuf[BUF_LEN -2] = '\0';
795  }
796  report_command(ctx, "load", tmpbuf, task_id, &start_time, &end_time);
797  }
798  if (out_p(grntest_task[task_id].jobtype)) {
799  fwrite(res, 1, res_len, grntest_job[grntest_task[task_id].job_id].outputlog);
800  fputc('\n', grntest_job[grntest_task[task_id].job_id].outputlog);
801  fflush(grntest_job[grntest_task[task_id].job_id].outputlog);
802  }
803  if (test_p(grntest_task[task_id].jobtype)) {
804  grn_obj log;
805  FILE *input;
806  FILE *output;
807  GRN_TEXT_INIT(&log, 0);
808  input = grntest_job[grntest_task[task_id].job_id].inputlog;
809  output = grntest_job[grntest_task[task_id].job_id].outputlog;
810  if (grn_text_fgets(ctx, &log, input) != GRN_SUCCESS) {
811  GRN_LOG(ctx, GRN_ERROR, "Cannot get input-log");
812  error_exit_in_thread(55);
813  }
814  if (GRN_TEXT_VALUE(&log)[GRN_TEXT_LEN(&log) - 1] == '\n') {
815  grn_bulk_truncate(ctx, &log, GRN_TEXT_LEN(&log) - 1);
816  }
817 
818  if (!same_result_p(GRN_TEXT_VALUE(&log), GRN_TEXT_LEN(&log),
819  res, res_len)) {
820  fprintf(output, "DIFF:command:%s\n", command);
821  fprintf(output, "DIFF:result:");
822  fwrite(res, 1, res_len, output);
823  fputc('\n', output);
824  fprintf(output, "DIFF:expect:%.*s\n",
825  (int)GRN_TEXT_LEN(&log), GRN_TEXT_VALUE(&log));
826  fflush(output);
827  }
828  GRN_OBJ_FIN(ctx, &log);
829  }
830  grn_obj_close(ctx, &end_time);
831  ret = 1;
832  break;
833  } else {
834  ret = 0;
835  break;
836  }
837  } while ((flags & GRN_CTX_MORE));
838  grn_obj_close(ctx, &start_time);
839 
840  return ret;
841 }
842 
843 
844 static int
845 do_command(grn_ctx *ctx, char *command, int type, int task_id)
846 {
847  char *res;
848  unsigned int res_len;
849  int flags;
850  grn_obj start_time, end_time;
851 
852  GRN_TIME_INIT(&start_time, 0);
853  GRN_TIME_NOW(ctx, &start_time);
854 
855  command_send(ctx, command, type, task_id);
856  do {
857  command_recv(ctx, type, task_id, &res, &res_len, &flags);
858  if (res_len) {
859  long long int self;
860  GRN_TIME_INIT(&end_time, 0);
861  GRN_TIME_NOW(ctx, &end_time);
862 
863  self = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&start_time);
864 
865  if (grntest_task[task_id].max < self) {
866  grntest_task[task_id].max = self;
867  }
868  if (grntest_task[task_id].min > self) {
869  grntest_task[task_id].min = self;
870  }
871 
872  if (report_p(grntest_task[task_id].jobtype)) {
873  char tmpbuf[BUF_LEN];
874 
875  if (res_len < BUF_LEN) {
876  strncpy(tmpbuf, res, res_len);
877  tmpbuf[res_len] = '\0';
878  } else {
879  strncpy(tmpbuf, res, BUF_LEN - 2);
880  tmpbuf[BUF_LEN -2] = '\0';
881  }
882  report_command(ctx, command, tmpbuf, task_id, &start_time, &end_time);
883  }
884  if (out_p(grntest_task[task_id].jobtype)) {
885  fwrite(res, 1, res_len, grntest_job[grntest_task[task_id].job_id].outputlog);
886  fputc('\n', grntest_job[grntest_task[task_id].job_id].outputlog);
887  fflush(grntest_job[grntest_task[task_id].job_id].outputlog);
888  }
889  if (test_p(grntest_task[task_id].jobtype)) {
890  grn_obj log;
891  FILE *input;
892  FILE *output;
893  GRN_TEXT_INIT(&log, 0);
894  input = grntest_job[grntest_task[task_id].job_id].inputlog;
895  output = grntest_job[grntest_task[task_id].job_id].outputlog;
896  if (grn_text_fgets(ctx, &log, input) != GRN_SUCCESS) {
897  GRN_LOG(ctx, GRN_ERROR, "Cannot get input-log");
898  error_exit_in_thread(55);
899  }
900  if (GRN_TEXT_VALUE(&log)[GRN_TEXT_LEN(&log) - 1] == '\n') {
901  grn_bulk_truncate(ctx, &log, GRN_TEXT_LEN(&log) - 1);
902  }
903 
904  if (!same_result_p(GRN_TEXT_VALUE(&log), GRN_TEXT_LEN(&log),
905  res, res_len)) {
906  fprintf(output, "DIFF:command:%s\n", command);
907  fprintf(output, "DIFF:result:");
908  fwrite(res, 1, res_len, output);
909  fputc('\n', output);
910  fprintf(output, "DIFF:expect:%.*s\n",
911  (int)GRN_TEXT_LEN(&log), GRN_TEXT_VALUE(&log));
912  fflush(output);
913  }
914  GRN_OBJ_FIN(ctx, &log);
915  }
916  grn_obj_close(ctx, &end_time);
917  break;
918  } else {
919 #ifdef ENABLE_ERROR_REPORT
920  error_command(ctx, command, task_id);
921 #endif
922  }
923  } while ((flags & GRN_CTX_MORE));
924 
925  grn_obj_close(ctx, &start_time);
926 
927  return 0;
928 }
929 
930 static int
931 comment_p(char *command)
932 {
933  if (command[0] == '#') {
934  return 1;
935  }
936  return 0;
937 }
938 
939 static int
940 load_command_p(char *command)
941 {
942  int i = 0;
943 
944  while (grn_isspace(&command[i], GRN_ENC_UTF8) == 1) {
945  i++;
946  }
947  if (command[i] == '\0') {
948  return 0;
949  }
950  if (!strncmp(&command[i], "load", 4)) {
951  return 1;
952  }
953  return 0;
954 }
955 
956 static int
957 worker_sub(grn_ctx *ctx, grn_obj *log, int task_id)
958 {
959  int i, load_mode, load_count;
960  grn_obj end_time;
961  long long int total_elapsed_time, job_elapsed_time;
962  double sec, qps;
963  long long int load_start;
964  struct task *task;
965  struct job *job;
966 
967  task = &(grntest_task[task_id]);
968  task->max = 0LL;
969  task->min = 9223372036854775807LL;
970  task->qnum = 0;
971 
972  for (i = 0; i < task->ntimes; i++) {
973  if (task->file != NULL) {
974  FILE *fp;
975  grn_obj line;
976  fp = fopen(task->file, "r");
977  if (!fp) {
978  fprintf(stderr, "Cannot open %s\n",grntest_task[task_id].file);
979  error_exit_in_thread(1);
980  }
981  load_mode = 0;
982  load_count = 0;
983  load_start = 0LL;
984  GRN_TEXT_INIT(&line, 0);
985  while (grn_text_fgets(ctx, &line, fp) == GRN_SUCCESS) {
986  if (GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] == '\n') {
987  grn_bulk_truncate(ctx, &line, GRN_TEXT_LEN(&line) - 1);
988  }
989  if (GRN_TEXT_LEN(&line) == 0) {
990  GRN_BULK_REWIND(&line);
991  continue;
992  }
993  GRN_TEXT_PUTC(ctx, &line, '\0');
994  if (comment_p(GRN_TEXT_VALUE(&line))) {
995  GRN_BULK_REWIND(&line);
996  continue;
997  }
998  if (load_command_p(GRN_TEXT_VALUE(&line))) {
999  load_mode = 1;
1000  load_count = 1;
1001  }
1002  if (load_mode == 1) {
1003  if (do_load_command(&grntest_ctx[task_id], GRN_TEXT_VALUE(&line),
1004  task->jobtype,
1005  task_id, &load_start)) {
1006  task->qnum += load_count;
1007  load_mode = 0;
1008  load_count = 0;
1009  load_start = 0LL;
1010  }
1011  load_count++;
1012  GRN_BULK_REWIND(&line);
1013  continue;
1014  }
1015  do_command(&grntest_ctx[task_id], GRN_TEXT_VALUE(&line),
1016  task->jobtype,
1017  task_id);
1018  task->qnum++;
1019  GRN_BULK_REWIND(&line);
1020  if (grntest_sigint) {
1021  goto exit;
1022  }
1023  }
1024  GRN_OBJ_FIN(ctx, &line);
1025  fclose(fp);
1026  } else {
1027  int i, n_commands;
1028  grn_obj *commands;
1029  commands = task->commands;
1030  if (!commands) {
1031  error_exit_in_thread(1);
1032  }
1033  load_mode = 0;
1034  n_commands = GRN_BULK_VSIZE(commands) / sizeof(grn_obj *);
1035  for (i = 0; i < n_commands; i++) {
1036  grn_obj *command;
1037  command = GRN_PTR_VALUE_AT(commands, i);
1038  if (load_command_p(GRN_TEXT_VALUE(command))) {
1039  load_mode = 1;
1040  }
1041  if (load_mode == 1) {
1042  if (do_load_command(&grntest_ctx[task_id],
1043  GRN_TEXT_VALUE(command),
1044  task->jobtype, task_id, &load_start)) {
1045  load_mode = 0;
1046  load_start = 0LL;
1047  task->qnum++;
1048  }
1049  continue;
1050  }
1051  do_command(&grntest_ctx[task_id],
1052  GRN_TEXT_VALUE(command),
1053  task->jobtype, task_id);
1054  task->qnum++;
1055  if (grntest_sigint) {
1056  goto exit;
1057  }
1058  }
1059  }
1060  }
1061 
1062 exit:
1063  GRN_TIME_INIT(&end_time, 0);
1064  GRN_TIME_NOW(&grntest_ctx[task_id], &end_time);
1065  total_elapsed_time = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_starttime);
1066  job_elapsed_time = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_jobs_start);
1067 
1068  CRITICAL_SECTION_ENTER(grntest_cs);
1069  job = &(grntest_job[task->job_id]);
1070  if (job->max < task->max) {
1071  job->max = task->max;
1072  }
1073  if (job->min > task->min) {
1074  job->min = task->min;
1075  }
1076 
1077  job->qnum += task->qnum;
1078  job->done++;
1079  if (job->done == job->concurrency) {
1080  char tmpbuf[BUF_LEN];
1081  sec = job_elapsed_time / (double)1000000;
1082  qps = (double)job->qnum/ sec;
1083  grntest_jobdone++;
1084  if (grntest_outtype == OUT_TSV) {
1085  sprintf(tmpbuf,
1086  "job\t"
1087  "%s\t"
1088  "%" GRN_FMT_LLD "\t"
1089  "%" GRN_FMT_LLD "\t"
1090  "%f\t"
1091  "%" GRN_FMT_LLD "\t"
1092  "%" GRN_FMT_LLD "\t"
1093  "%d\n",
1094  job->jobname,
1095  total_elapsed_time,
1096  job_elapsed_time,
1097  qps,
1098  job->min,
1099  job->max,
1100  job->qnum);
1101  } else {
1102  sprintf(tmpbuf,
1103  "{\"job\": \"%s\", "
1104  "\"total_elapsed_time\": %" GRN_FMT_LLD ", "
1105  "\"job_elapsed_time\": %" GRN_FMT_LLD ", "
1106  "\"qps\": %f, "
1107  "\"min\": %" GRN_FMT_LLD ", "
1108  "\"max\": %" GRN_FMT_LLD ", "
1109  "\"queries\": %d}",
1110  job->jobname,
1111  total_elapsed_time,
1112  job_elapsed_time,
1113  qps,
1114  job->min,
1115  job->max,
1116  job->qnum);
1117  if (grntest_jobdone < grntest_jobnum) {
1118  strcat(tmpbuf, ",");
1119  }
1120  }
1121  GRN_TEXT_PUTS(ctx, log, tmpbuf);
1122  if (grntest_jobdone == grntest_jobnum) {
1123  if (grntest_outtype == OUT_TSV) {
1124  fprintf(grntest_log_file, "%.*s",
1125  (int)GRN_TEXT_LEN(log), GRN_TEXT_VALUE(log));
1126  } else {
1127  if (grntest_detail_on) {
1128  fseek(grntest_log_file, -2, SEEK_CUR);
1129  fprintf(grntest_log_file, "],\n");
1130  }
1131  fprintf(grntest_log_file, "\"summary\": [");
1132  fprintf(grntest_log_file, "%.*s",
1133  (int)GRN_TEXT_LEN(log), GRN_TEXT_VALUE(log));
1134  fprintf(grntest_log_file, "]");
1135  }
1136  fflush(grntest_log_file);
1137  }
1138  }
1139  grn_obj_close(&grntest_ctx[task_id], &end_time);
1140  CRITICAL_SECTION_LEAVE(grntest_cs);
1141 
1142  return 0;
1143 }
1144 
1145 typedef struct _grntest_worker {
1148  int task_id;
1149 } grntest_worker;
1150 
1151 #ifdef WIN32
1152 static int
1153 __stdcall
1154 worker(void *val)
1155 {
1156  grntest_worker *worker = val;
1157  worker_sub(worker->ctx, &worker->log, worker->task_id);
1158  return 0;
1159 }
1160 #else
1161 static void *
1162 worker(void *val)
1163 {
1164  grntest_worker *worker = val;
1165  worker_sub(worker->ctx, &worker->log, worker->task_id);
1166  return NULL;
1167 }
1168 #endif /* WIN32 */
1169 
1170 #ifdef WIN32
1171 static int
1172 thread_main(grn_ctx *ctx, int num)
1173 {
1174  int i;
1175  int ret;
1176  HANDLE pthread[MAX_CON];
1177  grntest_worker *workers[MAX_CON];
1178 
1179  for (i = 0; i < num; i++) {
1180  workers[i] = GRN_MALLOC(sizeof(grntest_worker));
1181  workers[i]->ctx = &grntest_ctx[i];
1182  GRN_TEXT_INIT(&workers[i]->log, 0);
1183  workers[i]->task_id = i;
1184  pthread[i] = (HANDLE)_beginthreadex(NULL, 0, worker, (void *)workers[i],
1185  0, NULL);
1186  if (pthread[i]== (HANDLE)0) {
1187  fprintf(stderr, "thread failed:%d\n", i);
1188  error_exit_in_thread(1);
1189  }
1190  }
1191 
1192  ret = WaitForMultipleObjects(num, pthread, TRUE, INFINITE);
1193  if (ret == WAIT_TIMEOUT) {
1194  fprintf(stderr, "timeout\n");
1195  error_exit_in_thread(1);
1196  }
1197 
1198  for (i = 0; i < num; i++) {
1199  CloseHandle(pthread[i]);
1200  GRN_OBJ_FIN(workers[i]->ctx, &workers[i]->log);
1201  GRN_FREE(workers[i]);
1202  }
1203  return 0;
1204 }
1205 #else
1206 static int
1207 thread_main(grn_ctx *ctx, int num)
1208 {
1209  intptr_t i;
1210  int ret;
1211  pthread_t pthread[MAX_CON];
1212  grntest_worker *workers[MAX_CON];
1213 
1214  for (i = 0; i < num; i++) {
1215  workers[i] = GRN_MALLOC(sizeof(grntest_worker));
1216  workers[i]->ctx = &grntest_ctx[i];
1217  GRN_TEXT_INIT(&workers[i]->log, 0);
1218  workers[i]->task_id = i;
1219  ret = pthread_create(&pthread[i], NULL, worker, (void *)workers[i]);
1220  if (ret) {
1221  fprintf(stderr, "Cannot create thread:ret=%d\n", ret);
1222  error_exit_in_thread(1);
1223  }
1224  }
1225 
1226  for (i = 0; i < num; i++) {
1227  ret = pthread_join(pthread[i], NULL);
1228  GRN_OBJ_FIN(workers[i]->ctx, &workers[i]->log);
1229  GRN_FREE(workers[i]);
1230  if (ret) {
1231  fprintf(stderr, "Cannot join thread:ret=%d\n", ret);
1232  error_exit_in_thread(1);
1233  }
1234  }
1235  return 0;
1236 }
1237 #endif
1238 
1239 static int
1240 error_exit(grn_ctx *ctx, int ret)
1241 {
1242  fflush(stderr);
1243  shutdown_server();
1244  grn_ctx_fin(ctx);
1245  grn_fin();
1246  exit(ret);
1247 }
1248 
1249 static int
1250 get_sysinfo(const char *path, char *result, int olen)
1251 {
1252  char tmpbuf[256];
1253 
1254 #ifdef WIN32
1255  int cinfo[4];
1256  ULARGE_INTEGER dinfo;
1257  char cpustring[64];
1258  SYSTEM_INFO sinfo;
1259  MEMORYSTATUSEX minfo;
1260  OSVERSIONINFO osinfo;
1261 
1262  if (grntest_outtype == OUT_TSV) {
1263  result[0] = '\0';
1264  sprintf(tmpbuf, "script\t%s\n", grntest_scriptname);
1265  strcat(result, tmpbuf);
1266  sprintf(tmpbuf, "user\t%s\n", grntest_username);
1267  strcat(result, tmpbuf);
1268  sprintf(tmpbuf, "date\t%s\n", grntest_date);
1269  strcat(result, tmpbuf);
1270  } else {
1271  strcpy(result, "{");
1272  sprintf(tmpbuf, "\"script\": \"%s.scr\",\n", grntest_scriptname);
1273  strcat(result, tmpbuf);
1274  sprintf(tmpbuf, " \"user\": \"%s\",\n", grntest_username);
1275  strcat(result, tmpbuf);
1276  sprintf(tmpbuf, " \"date\": \"%s\",\n", grntest_date);
1277  strcat(result, tmpbuf);
1278  }
1279 
1280  memset(cpustring, 0, 64);
1281 #ifndef __GNUC__
1282  __cpuid(cinfo, 0x80000002);
1283  memcpy(cpustring, cinfo, 16);
1284  __cpuid(cinfo, 0x80000003);
1285  memcpy(cpustring+16, cinfo, 16);
1286  __cpuid(cinfo, 0x80000004);
1287  memcpy(cpustring+32, cinfo, 16);
1288 #endif
1289 
1290  if (grntest_outtype == OUT_TSV) {
1291  sprintf(tmpbuf, "%s\n", cpustring);
1292  } else {
1293  sprintf(tmpbuf, " \"CPU\": \"%s\",\n", cpustring);
1294  }
1295  strcat(result, tmpbuf);
1296 
1297  if (sizeof(int *) == 8) {
1298  grntest_osinfo = OS_WINDOWS64;
1299  if (grntest_outtype == OUT_TSV) {
1300  sprintf(tmpbuf, "64BIT\n");
1301  } else {
1302  sprintf(tmpbuf, " \"BIT\": 64,\n");
1303  }
1304  } else {
1305  grntest_osinfo = OS_WINDOWS32;
1306  if (grntest_outtype == OUT_TSV) {
1307  sprintf(tmpbuf, "32BIT\n");
1308  } else {
1309  sprintf(tmpbuf, " \"BIT\": 32,\n");
1310  }
1311  }
1312  strcat(result, tmpbuf);
1313 
1314  GetSystemInfo(&sinfo);
1315  if (grntest_outtype == OUT_TSV) {
1316  sprintf(tmpbuf, "CORE\t%d\n", sinfo.dwNumberOfProcessors);
1317  } else {
1318  sprintf(tmpbuf, " \"CORE\": %d,\n", sinfo.dwNumberOfProcessors);
1319  }
1320  strcat(result, tmpbuf);
1321 
1322  minfo.dwLength = sizeof(MEMORYSTATUSEX);
1323  GlobalMemoryStatusEx(&minfo);
1324  if (grntest_outtype == OUT_TSV) {
1325  sprintf(tmpbuf, "RAM\t%I64dMByte\n", minfo.ullTotalPhys/(1024*1024));
1326  } else {
1327  sprintf(tmpbuf, " \"RAM\": \"%I64dMByte\",\n", minfo.ullTotalPhys/(1024*1024));
1328  }
1329  strcat(result, tmpbuf);
1330 
1331  GetDiskFreeSpaceEx(NULL, NULL, &dinfo, NULL);
1332  if (grntest_outtype == OUT_TSV) {
1333  sprintf(tmpbuf, "HDD\t%I64dKBytes\n", dinfo.QuadPart/1024 );
1334  } else {
1335  sprintf(tmpbuf, " \"HDD\": \"%I64dKBytes\",\n", dinfo.QuadPart/1024 );
1336  }
1337  strcat(result, tmpbuf);
1338 
1339  osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osinfo);
1340  if (grntest_outtype == OUT_TSV) {
1341  sprintf(tmpbuf, "Windows %d.%d\n", osinfo.dwMajorVersion, osinfo.dwMinorVersion);
1342  } else {
1343  sprintf(tmpbuf, " \"OS\": \"Windows %d.%d\",\n", osinfo.dwMajorVersion,
1344  osinfo.dwMinorVersion);
1345  }
1346  strcat(result, tmpbuf);
1347 
1348  if (grntest_outtype == OUT_TSV) {
1349  sprintf(tmpbuf, "%s\n", grntest_serverhost);
1350  } else {
1351  sprintf(tmpbuf, " \"HOST\": \"%s\",\n", grntest_serverhost);
1352  }
1353  strcat(result, tmpbuf);
1354 
1355  if (grntest_outtype == OUT_TSV) {
1356  sprintf(tmpbuf, "%d\n", grntest_serverport);
1357  } else {
1358  sprintf(tmpbuf, " \"PORT\": \"%d\",\n", grntest_serverport);
1359  }
1360  strcat(result, tmpbuf);
1361 
1362  if (grntest_outtype == OUT_TSV) {
1363  sprintf(tmpbuf, "%s\"\n", grn_get_version());
1364  } else {
1365  sprintf(tmpbuf, " \"VERSION\": \"%s\"\n", grn_get_version());
1366  }
1367 
1368  strcat(result, tmpbuf);
1369  if (grntest_outtype != OUT_TSV) {
1370  strcat(result, "}");
1371  }
1372 
1373 #else /* linux only */
1374  FILE *fp;
1375  int ret;
1376  int cpunum = 0;
1377  int minfo = 0;
1378  int unevictable = 0;
1379  int mlocked = 0;
1380  char cpustring[256];
1381  struct utsname ubuf;
1382  struct statvfs vfsbuf;
1383 
1384  if (grntest_outtype == OUT_TSV) {
1385  result[0] = '\0';
1386  sprintf(tmpbuf, "sctipt\t%s\n", grntest_scriptname);
1387  strcat(result, tmpbuf);
1388  sprintf(tmpbuf, "user\t%s\n", grntest_username);
1389  strcat(result, tmpbuf);
1390  sprintf(tmpbuf, "date\t%s\n", grntest_date);
1391  strcat(result, tmpbuf);
1392  } else {
1393  strcpy(result, "{");
1394  sprintf(tmpbuf, "\"script\": \"%s.scr\",\n", grntest_scriptname);
1395  strcat(result, tmpbuf);
1396  sprintf(tmpbuf, " \"user\": \"%s\",\n", grntest_username);
1397  strcat(result, tmpbuf);
1398  sprintf(tmpbuf, " \"date\": \"%s\",\n", grntest_date);
1399  strcat(result, tmpbuf);
1400  }
1401 
1402  fp = fopen("/proc/cpuinfo", "r");
1403  if (!fp) {
1404  fprintf(stderr, "Cannot open cpuinfo\n");
1405  exit(1);
1406  }
1407  while (fgets(tmpbuf, 256, fp) != NULL) {
1408  tmpbuf[strlen(tmpbuf)-1] = '\0';
1409  if (!strncmp(tmpbuf, "model name\t: ", 13)) {
1410  strcpy(cpustring, &tmpbuf[13]);
1411  }
1412  }
1413  fclose(fp);
1414  cpunum = sysconf(_SC_NPROCESSORS_CONF);
1415 
1416  if (grntest_outtype == OUT_TSV) {
1417  sprintf(tmpbuf, "%s\n", cpustring);
1418  } else {
1419  sprintf(tmpbuf, " \"CPU\": \"%s\",\n", cpustring);
1420  }
1421  strcat(result, tmpbuf);
1422 
1423  if (sizeof(int *) == 8) {
1424  grntest_osinfo = OS_LINUX64;
1425  if (grntest_outtype == OUT_TSV) {
1426  sprintf(tmpbuf, "64BIT\n");
1427  } else {
1428  sprintf(tmpbuf, " \"BIT\": 64,\n");
1429  }
1430  } else {
1431  grntest_osinfo = OS_LINUX32;
1432  if (grntest_outtype == OUT_TSV) {
1433  sprintf(tmpbuf, "32BIT\n");
1434  } else {
1435  sprintf(tmpbuf, " \"BIT\": 32,\n");
1436  }
1437  }
1438  strcat(result, tmpbuf);
1439 
1440  if (grntest_outtype == OUT_TSV) {
1441  sprintf(tmpbuf, "CORE\t%d\n", cpunum);
1442  } else {
1443  sprintf(tmpbuf, " \"CORE\": %d,\n", cpunum);
1444  }
1445  strcat(result, tmpbuf);
1446 
1447  fp = fopen("/proc/meminfo", "r");
1448  if (!fp) {
1449  fprintf(stderr, "Cannot open meminfo\n");
1450  exit(1);
1451  }
1452  while (fgets(tmpbuf, 256, fp) != NULL) {
1453  tmpbuf[strlen(tmpbuf)-1] = '\0';
1454  if (!strncmp(tmpbuf, "MemTotal:", 9)) {
1455  minfo = grntest_atoi(&tmpbuf[10], &tmpbuf[10] + 40, NULL);
1456  }
1457  if (!strncmp(tmpbuf, "Unevictable:", 12)) {
1458  unevictable = grntest_atoi(&tmpbuf[13], &tmpbuf[13] + 40, NULL);
1459  }
1460  if (!strncmp(tmpbuf, "Mlocked:", 8)) {
1461  mlocked = grntest_atoi(&tmpbuf[9], &tmpbuf[9] + 40, NULL);
1462  }
1463  }
1464  fclose(fp);
1465  if (grntest_outtype == OUT_TSV) {
1466  sprintf(tmpbuf, "%dMBytes\n", minfo/1024);
1467  strcat(result, tmpbuf);
1468  sprintf(tmpbuf, "%dMBytes_Unevictable\n", unevictable/1024);
1469  strcat(result, tmpbuf);
1470  sprintf(tmpbuf, "%dMBytes_Mlocked\n", mlocked/1024);
1471  strcat(result, tmpbuf);
1472  } else {
1473  sprintf(tmpbuf, " \"RAM\": \"%dMBytes\",\n", minfo/1024);
1474  strcat(result, tmpbuf);
1475  sprintf(tmpbuf, " \"Unevictable\": \"%dMBytes\",\n", unevictable/1024);
1476  strcat(result, tmpbuf);
1477  sprintf(tmpbuf, " \"Mlocked\": \"%dMBytes\",\n", mlocked/1024);
1478  strcat(result, tmpbuf);
1479  }
1480 
1481  ret = statvfs(path, &vfsbuf);
1482  if (ret) {
1483  fprintf(stderr, "Cannot access %s\n", path);
1484  exit(1);
1485  }
1486 
1487  if (grntest_outtype == OUT_TSV) {
1488  sprintf(tmpbuf, "%" GRN_FMT_INT64U "KBytes\n", vfsbuf.f_blocks * 4);
1489  } else {
1490  sprintf(tmpbuf,
1491  " \"HDD\": \"%" GRN_FMT_INT64U "KBytes\",\n",
1492  vfsbuf.f_blocks * 4);
1493  }
1494  strcat(result, tmpbuf);
1495 
1496  uname(&ubuf);
1497  if (grntest_outtype == OUT_TSV) {
1498  sprintf(tmpbuf, "%s %s\n", ubuf.sysname, ubuf.release);
1499  } else {
1500  sprintf(tmpbuf, " \"OS\": \"%s %s\",\n", ubuf.sysname, ubuf.release);
1501  }
1502  strcat(result, tmpbuf);
1503 
1504  if (grntest_outtype == OUT_TSV) {
1505  sprintf(tmpbuf, "%s\n", grntest_serverhost);
1506  } else {
1507  sprintf(tmpbuf, " \"HOST\": \"%s\",\n", grntest_serverhost);
1508  }
1509  strcat(result, tmpbuf);
1510 
1511  if (grntest_outtype == OUT_TSV) {
1512  sprintf(tmpbuf, "%d\n", grntest_serverport);
1513  } else {
1514  sprintf(tmpbuf, " \"PORT\": \"%d\",\n", grntest_serverport);
1515  }
1516  strcat(result, tmpbuf);
1517 
1518  if (grntest_outtype == OUT_TSV) {
1519  sprintf(tmpbuf, "%s\n", grn_get_version());
1520  } else {
1521  sprintf(tmpbuf, " \"VERSION\": \"%s\"\n", grn_get_version());
1522  }
1523  strcat(result, tmpbuf);
1524 
1525  if (grntest_outtype != OUT_TSV) {
1526  strcat(result, "},");
1527  }
1528 #endif /* WIN32 */
1529  if (strlen(result) >= olen) {
1530  fprintf(stderr, "buffer overrun in get_sysinfo!\n");
1531  exit(1);
1532  }
1533  return 0;
1534 }
1535 
1536 static int
1537 start_server(const char *dbpath, int r)
1538 {
1539  int ret;
1540  char optbuf[BUF_LEN];
1541 #ifdef WIN32
1542  char tmpbuf[BUF_LEN];
1543 
1544  STARTUPINFO si;
1545 
1546  if (strlen(dbpath) > BUF_LEN - 100) {
1547  fprintf(stderr, "too long dbpath!\n");
1548  exit(1);
1549  }
1550 
1551  strcpy(tmpbuf, groonga_path);
1552  strcat(tmpbuf, " -s --protocol ");
1553  strcat(tmpbuf, groonga_protocol);
1554  strcat(tmpbuf, " -p ");
1555  sprintf(optbuf, "%d ", grntest_serverport);
1556  strcat(tmpbuf, optbuf);
1557  strcat(tmpbuf, dbpath);
1558  memset(&si, 0, sizeof(STARTUPINFO));
1559  si.cb=sizeof(STARTUPINFO);
1560  ret = CreateProcess(NULL, tmpbuf, NULL, NULL, FALSE,
1561  0, NULL, NULL, &si, &grntest_pi);
1562 
1563  if (ret == 0) {
1564  fprintf(stderr, "Cannot start groonga server: <%s>: error=%d\n",
1565  groonga_path, GetLastError());
1566  exit(1);
1567  }
1568 
1569 #else
1570  pid_t pid;
1571  pid = fork();
1572  if (pid < 0) {
1573  fprintf(stderr, "Cannot start groonga server:Cannot fork\n");
1574  exit(1);
1575  }
1576  sprintf(optbuf, "%d", grntest_serverport);
1577  if (pid == 0) {
1578  ret = execlp(groonga_path, groonga_path,
1579  "-s",
1580  "--protocol", groonga_protocol,
1581  "-p", optbuf,
1582  dbpath, (char*)NULL);
1583  if (ret == -1) {
1584  fprintf(stderr, "Cannot start groonga server: <%s>: errno=%d\n",
1585  groonga_path, errno);
1586  exit(1);
1587  }
1588  }
1589  else {
1590  grntest_server_id = pid;
1591  }
1592 
1593 #endif /* WIN32 */
1594 
1595  return 0;
1596 }
1597 
1598 static int
1599 parse_line(char *buf, int start, int end, int num)
1600 {
1601  int i, j, error_flag = 0, out_or_test = 0;
1602  char tmpbuf[BUF_LEN];
1603 
1604  grntest_job[num].concurrency = 1;
1605  grntest_job[num].ntimes = 1;
1606  grntest_job[num].done = 0;
1607  grntest_job[num].qnum = 0;
1608  grntest_job[num].max = 0LL;
1609  grntest_job[num].min = 9223372036854775807LL;
1610  grntest_job[num].outputlog = NULL;
1611  grntest_job[num].inputlog = NULL;
1612 
1613  strncpy(grntest_job[num].jobname, &buf[start], end - start);
1614  grntest_job[num].jobname[end - start] = '\0';
1615  i = start;
1616  while (i < end) {
1617  if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1618  i++;
1619  continue;
1620  }
1621  if (!strncmp(&buf[i], "do_local", 8)) {
1622  grntest_job[num].jobtype = J_DO_LOCAL;
1623  i = i + 8;
1624  break;
1625  }
1626  if (!strncmp(&buf[i], "do_gqtp", 7)) {
1627  grntest_job[num].jobtype = J_DO_GQTP;
1628  i = i + 7;
1629  break;
1630  }
1631  if (!strncmp(&buf[i], "do_http", 7)) {
1632  grntest_job[num].jobtype = J_DO_HTTP;
1633  i = i + 7;
1634  break;
1635  }
1636  if (!strncmp(&buf[i], "rep_local", 9)) {
1637  grntest_job[num].jobtype = J_REP_LOCAL;
1638  i = i + 9;
1639  break;
1640  }
1641  if (!strncmp(&buf[i], "rep_gqtp", 8)) {
1642  grntest_job[num].jobtype = J_REP_GQTP;
1643  i = i + 8;
1644  break;
1645  }
1646  if (!strncmp(&buf[i], "rep_http", 8)) {
1647  grntest_job[num].jobtype = J_REP_HTTP;
1648  i = i + 8;
1649  break;
1650  }
1651  if (!strncmp(&buf[i], "out_local", 9)) {
1652  grntest_job[num].jobtype = J_OUT_LOCAL;
1653  i = i + 9;
1654  out_or_test = 1;
1655  break;
1656  }
1657  if (!strncmp(&buf[i], "out_gqtp", 8)) {
1658  grntest_job[num].jobtype = J_OUT_GQTP;
1659  i = i + 8;
1660  out_or_test = 1;
1661  break;
1662  }
1663  if (!strncmp(&buf[i], "out_http", 8)) {
1664  grntest_job[num].jobtype = J_OUT_HTTP;
1665  i = i + 8;
1666  out_or_test = 1;
1667  break;
1668  }
1669  if (!strncmp(&buf[i], "test_local", 10)) {
1670  grntest_job[num].jobtype = J_TEST_LOCAL;
1671  i = i + 10;
1672  out_or_test = 1;
1673  break;
1674  }
1675  if (!strncmp(&buf[i], "test_gqtp", 9)) {
1676  grntest_job[num].jobtype = J_TEST_GQTP;
1677  i = i + 9;
1678  out_or_test = 1;
1679  break;
1680  }
1681  if (!strncmp(&buf[i], "test_http", 9)) {
1682  grntest_job[num].jobtype = J_TEST_HTTP;
1683  i = i + 9;
1684  out_or_test = 1;
1685  break;
1686  }
1687  error_flag = 1;
1688  i++;
1689  }
1690 
1691  if (error_flag) {
1692  return 3;
1693  }
1694  if (i == end) {
1695  return 1;
1696  }
1697 
1698  if (grn_isspace(&buf[i], GRN_ENC_UTF8) != 1) {
1699  return 4;
1700  }
1701  i++;
1702 
1703  while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1704  i++;
1705  continue;
1706  }
1707  j = 0;
1708  while (i < end) {
1709  if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1710  break;
1711  }
1712  grntest_job[num].commandfile[j] = buf[i];
1713  i++;
1714  j++;
1715  if (j > 255) {
1716  return 5;
1717  }
1718  }
1719  grntest_job[num].commandfile[j] = '\0';
1720 
1721  while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1722  i++;
1723  }
1724 
1725  if (i == end) {
1726  if (out_or_test) {
1727  fprintf(stderr, "log(test)_local(gqtp|http) needs log(test)_filename\n");
1728  return 11;
1729  }
1730  return 0;
1731  }
1732 
1733  j = 0;
1734  while (i < end) {
1735  if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1736  break;
1737  }
1738  tmpbuf[j] = buf[i];
1739  i++;
1740  j++;
1741  if (j >= BUF_LEN) {
1742  return 6;
1743  }
1744  }
1745  tmpbuf[j] ='\0';
1746  if (out_or_test) {
1747  if (out_p(grntest_job[num].jobtype)) {
1748  grntest_job[num].outputlog = fopen(tmpbuf, "wb");
1749  if (grntest_job[num].outputlog == NULL) {
1750  fprintf(stderr, "Cannot open %s\n", tmpbuf);
1751  return 13;
1752  }
1753  } else {
1754  char outlog[BUF_LEN];
1755  grntest_job[num].inputlog = fopen(tmpbuf, "rb");
1756  if (grntest_job[num].inputlog == NULL) {
1757  fprintf(stderr, "Cannot open %s\n", tmpbuf);
1758  return 14;
1759  }
1760  sprintf(outlog, "%s.diff", tmpbuf);
1761  grntest_job[num].outputlog = fopen(outlog, "wb");
1762  if (grntest_job[num].outputlog == NULL) {
1763  fprintf(stderr, "Cannot open %s\n", outlog);
1764  return 15;
1765  }
1766  }
1767  strcpy(grntest_job[num].logfile, tmpbuf);
1768  return 0;
1769  } else {
1770  grntest_job[num].concurrency = grntest_atoi(tmpbuf, tmpbuf + j, NULL);
1771  if (grntest_job[num].concurrency == 0) {
1772  return 7;
1773  }
1774  }
1775 
1776  while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1777  i++;
1778  }
1779 
1780  if (i == end) {
1781  return 0;
1782  }
1783 
1784  j = 0;
1785  while (i < end) {
1786  if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1787  break;
1788  }
1789  tmpbuf[j] = buf[i];
1790  i++;
1791  j++;
1792  if (j > 16) {
1793  return 8;
1794  }
1795  }
1796  tmpbuf[j] ='\0';
1797  grntest_job[num].ntimes = grntest_atoi(tmpbuf, tmpbuf + j, NULL);
1798  if (grntest_job[num].ntimes == 0) {
1799  return 9;
1800  }
1801  if (i == end) {
1802  return 0;
1803  }
1804 
1805  while (i < end) {
1806  if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
1807  i++;
1808  continue;
1809  }
1810  return 10;
1811  }
1812  return 0;
1813 }
1814 
1815 static int
1816 get_jobs(grn_ctx *ctx, char *buf, int line)
1817 {
1818  int i, len, start, end, ret;
1819  int jnum = 0;
1820 
1821  len = strlen(buf);
1822  i = 0;
1823  while (i < len) {
1824  if ((buf[i] == '#') || (buf[i] == '\r') || (buf[i] == '\n')) {
1825  buf[i] = '\0';
1826  len = i;
1827  break;
1828  }
1829  i++;
1830  }
1831 
1832  i = 0;
1833  start = 0;
1834  while (i < len) {
1835  if (buf[i] == ';') {
1836  end = i;
1837  ret = parse_line(buf, start, end, jnum);
1838  if (ret) {
1839  if (ret > 1) {
1840  fprintf(stderr, "Syntax error:line=%d:ret=%d:%s\n", line, ret, buf);
1841  error_exit(ctx, 1);
1842  }
1843  } else {
1844  jnum++;
1845  }
1846  start = end + 1;
1847  }
1848  i++;
1849  }
1850  end = len;
1851  ret = parse_line(buf, start, end, jnum);
1852  if (ret) {
1853  if (ret > 1) {
1854  fprintf(stderr, "Syntax error:line=%d:ret=%d:%s\n", line, ret, buf);
1855  error_exit(ctx, 1);
1856  }
1857  } else {
1858  jnum++;
1859  }
1860  return jnum;
1861 }
1862 
1863 static int
1864 make_task_table(grn_ctx *ctx, int jobnum)
1865 {
1866  int i, j;
1867  int tid = 0;
1868  FILE *fp;
1869  grn_obj *commands = NULL;
1870 
1871  for (i = 0; i < jobnum; i++) {
1872  if ((grntest_job[i].concurrency == 1) && (!grntest_onmemory_mode)) {
1873  grntest_task[tid].file = grntest_job[i].commandfile;
1874  grntest_task[tid].commands = NULL;
1875  grntest_task[tid].ntimes = grntest_job[i].ntimes;
1876  grntest_task[tid].jobtype = grntest_job[i].jobtype;
1877  grntest_task[tid].job_id = i;
1878  tid++;
1879  continue;
1880  }
1881  for (j = 0; j < grntest_job[i].concurrency; j++) {
1882  if (j == 0) {
1883  grn_obj line;
1884  GRN_TEXT_INIT(&line, 0);
1885  commands = grn_obj_open(ctx, GRN_PVECTOR, 0, GRN_VOID);
1886  if (!commands) {
1887  fprintf(stderr, "Cannot alloc commands\n");
1888  error_exit(ctx, 1);
1889  }
1890  fp = fopen(grntest_job[i].commandfile, "r");
1891  if (!fp) {
1892  fprintf(stderr, "Cannot alloc commandfile:%s\n",
1893  grntest_job[i].commandfile);
1894  error_exit(ctx, 1);
1895  }
1896  while (grn_text_fgets(ctx, &line, fp) == GRN_SUCCESS) {
1897  grn_obj *command;
1898  if (GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] == '\n') {
1899  grn_bulk_truncate(ctx, &line, GRN_TEXT_LEN(&line) - 1);
1900  }
1901  if (GRN_TEXT_LEN(&line) == 0) {
1902  GRN_BULK_REWIND(&line);
1903  continue;
1904  }
1905  GRN_TEXT_PUTC(ctx, &line, '\0');
1906  if (comment_p(GRN_TEXT_VALUE(&line))) {
1907  GRN_BULK_REWIND(&line);
1908  continue;
1909  }
1910  command = grn_obj_open(ctx, GRN_BULK, 0, GRN_VOID);
1911  if (!command) {
1912  fprintf(stderr, "Cannot alloc command: %s: %s\n",
1913  grntest_job[i].commandfile, GRN_TEXT_VALUE(&line));
1914  GRN_OBJ_FIN(ctx, &line);
1915  error_exit(ctx, 1);
1916  }
1917  GRN_TEXT_SET(ctx, command, GRN_TEXT_VALUE(&line), GRN_TEXT_LEN(&line));
1918  GRN_PTR_PUT(ctx, commands, command);
1919  GRN_BULK_REWIND(&line);
1920  }
1921  GRN_OBJ_FIN(ctx, &line);
1922  }
1923  grntest_task[tid].file = NULL;
1924  grntest_task[tid].commands = commands;
1925  grntest_task[tid].ntimes = grntest_job[i].ntimes;
1926  grntest_task[tid].jobtype = grntest_job[i].jobtype;
1927  grntest_task[tid].job_id = i;
1928  tid++;
1929  }
1930  }
1931  return tid;
1932 }
1933 
1934 /*
1935 static int
1936 print_commandlist(int task_id)
1937 {
1938  int i;
1939 
1940  for (i = 0; i < GRN_TEXT_LEN(grntest_task[task_id].commands); i++) {
1941  grn_obj *command;
1942  command = GRN_PTR_VALUE_AT(grntest_task[task_id].commands, i);
1943  printf("%s\n", GRN_TEXT_VALUE(command));
1944  }
1945  return 0;
1946 }
1947 */
1948 
1949 /* return num of query */
1950 static int
1951 do_jobs(grn_ctx *ctx, int jobnum, int line)
1952 {
1953  int i, task_num, ret, qnum = 0, thread_num = 0;
1954 
1955  for (i = 0; i < jobnum; i++) {
1956 /*
1957 printf("%d:type =%d:file=%s:con=%d:ntimes=%d\n", i, grntest_job[i].jobtype,
1958  grntest_job[i].commandfile, JobTable[i].concurrency, JobTable[i].ntimes);
1959 
1960 */
1961  thread_num = thread_num + grntest_job[i].concurrency;
1962  }
1963 
1964  if (thread_num >= MAX_CON) {
1965  fprintf(stderr, "Too many threads requested(MAX=64):line=%d\n", line);
1966  error_exit(ctx, 1);
1967  }
1968 
1969  task_num = make_task_table(ctx, jobnum);
1970  if (task_num != thread_num) {
1971  fprintf(stderr, "Logical error\n");
1972  error_exit(ctx, 9);
1973  }
1974 
1975  grntest_detail_on = 0;
1976  for (i = 0; i < task_num; i++) {
1977  grn_ctx_init(&grntest_ctx[i], 0);
1978  grntest_owndb[i] = NULL;
1979  if (gqtp_p(grntest_task[i].jobtype)) {
1980  ret = grn_ctx_connect(&grntest_ctx[i], grntest_serverhost, grntest_serverport, 0);
1981  if (ret) {
1982  fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
1983  grntest_serverhost, grntest_serverport, ret);
1984  error_exit(ctx, 1);
1985  }
1986  } else if (http_p(grntest_task[i].jobtype)) {
1987  grntest_task[i].http_socket = 0;
1988  GRN_TEXT_INIT(&grntest_task[i].http_response, 0);
1989  if (grntest_owndb_mode) {
1990  grntest_owndb[i] = grn_db_open(&grntest_ctx[i], grntest_dbpath);
1991  if (grntest_owndb[i] == NULL) {
1992  fprintf(stderr, "Cannot open db:%s\n", grntest_dbpath);
1993  exit(1);
1994  }
1995  } else {
1996  grntest_owndb[i] = grn_db_create(&grntest_ctx[i], NULL, NULL);
1997  }
1998  } else {
1999  if (grntest_owndb_mode) {
2000  grntest_owndb[i] = grn_db_open(&grntest_ctx[i], grntest_dbpath);
2001  if (grntest_owndb[i] == NULL) {
2002  fprintf(stderr, "Cannot open db:%s\n", grntest_dbpath);
2003  exit(1);
2004  }
2005  }
2006  else {
2007  grn_ctx_use(&grntest_ctx[i], grntest_db);
2008  }
2009  }
2010  if (report_p(grntest_task[i].jobtype)) {
2011  grntest_detail_on++;
2012  }
2013  }
2014  if (grntest_detail_on) {
2015  if (grntest_outtype == OUT_TSV) {
2016  ;
2017  }
2018  else {
2019  fprintf(grntest_log_file, "\"detail\": [\n");
2020  }
2021  fflush(grntest_log_file);
2022  }
2023 
2024  thread_main(ctx, task_num);
2025 
2026  for (i = 0; i < task_num; i++) {
2027  if (grntest_owndb[i]) {
2028  grn_obj_close(&grntest_ctx[i], grntest_owndb[i]);
2029  }
2030  if (http_p(grntest_task[i].jobtype)) {
2031  GRN_OBJ_FIN(&grntest_ctx[i], &grntest_task[i].http_response);
2032  }
2033  grn_ctx_fin(&grntest_ctx[i]);
2034  qnum = qnum + grntest_task[i].qnum;
2035  }
2036 
2037  i = 0;
2038  while (i < task_num) {
2039  int job_id;
2040  if (grntest_task[i].commands) {
2041  job_id = grntest_task[i].job_id;
2042  GRN_OBJ_FIN(ctx, grntest_task[i].commands);
2043  while (job_id == grntest_task[i].job_id) {
2044  i++;
2045  }
2046  } else {
2047  i++;
2048  }
2049  }
2050  for (i = 0; i < jobnum; i++) {
2051  if (grntest_job[i].outputlog) {
2052  int ret;
2053  ret = fclose(grntest_job[i].outputlog);
2054  if (ret) {
2055  fprintf(stderr, "Cannot close %s\n", grntest_job[i].logfile);
2056  exit(1);
2057  }
2058  }
2059  if (grntest_job[i].inputlog) {
2060  int ret;
2061  ret = fclose(grntest_job[i].inputlog);
2062  if (ret) {
2063  fprintf(stderr, "Cannot close %s\n", grntest_job[i].logfile);
2064  exit(1);
2065  }
2066  }
2067  }
2068  return qnum;
2069 }
2070 
2071 /* return num of query */
2072 static int
2073 do_script(grn_ctx *ctx, const char *script_file_path)
2074 {
2075  int n_lines = 0;
2076  int n_jobs;
2077  int n_queries, total_n_queries = 0;
2078  FILE *script_file;
2079  grn_obj line;
2080 
2081  script_file = fopen(script_file_path, "r");
2082  if (script_file == NULL) {
2083  fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
2084  error_exit(ctx, 1);
2085  }
2086 
2087  GRN_TEXT_INIT(&line, 0);
2088  while (grn_text_fgets(ctx, &line, script_file) == GRN_SUCCESS) {
2089  if (grntest_sigint) {
2090  break;
2091  }
2092  n_lines++;
2093  grntest_jobdone = 0;
2094  n_jobs = get_jobs(ctx, GRN_TEXT_VALUE(&line), n_lines);
2095  grntest_jobnum = n_jobs;
2096 
2097  if (n_jobs > 0) {
2098  GRN_TIME_INIT(&grntest_jobs_start, 0);
2099  GRN_TIME_NOW(ctx, &grntest_jobs_start);
2100  if (grntest_outtype == OUT_TSV) {
2101  fprintf(grntest_log_file, "jobs-start\t%s\n", GRN_TEXT_VALUE(&line));
2102  } else {
2103  fprintf(grntest_log_file, "{\"jobs\": \"%s\",\n", GRN_TEXT_VALUE(&line));
2104  }
2105  n_queries = do_jobs(ctx, n_jobs, n_lines);
2106  if (grntest_outtype == OUT_TSV) {
2107  fprintf(grntest_log_file, "jobs-end\t%s\n", GRN_TEXT_VALUE(&line));
2108  } else {
2109  fprintf(grntest_log_file, "},\n");
2110  }
2111  total_n_queries += n_queries;
2112 
2113  grn_obj_close(ctx, &grntest_jobs_start);
2114  }
2115  if (grntest_stop_flag) {
2116  fprintf(stderr, "Error:Quit\n");
2117  break;
2118  }
2119  GRN_BULK_REWIND(&line);
2120  }
2121  grn_obj_unlink(ctx, &line);
2122 
2123  fclose(script_file);
2124 
2125  return total_n_queries;
2126 }
2127 
2128 static int
2129 start_local(grn_ctx *ctx, const char *dbpath)
2130 {
2131  grntest_db = grn_db_open(ctx, dbpath);
2132  if (!grntest_db) {
2133  grntest_db = grn_db_create(ctx, dbpath, NULL);
2134  }
2135  if (!grntest_db) {
2136  fprintf(stderr, "Cannot open db:%s\n", dbpath);
2137  exit(1);
2138  }
2139  return 0;
2140 }
2141 
2142 static int
2143 check_server(grn_ctx *ctx)
2144 {
2145  int ret, retry = 0;
2146  while (1) {
2147  ret = grn_ctx_connect(ctx, grntest_serverhost, grntest_serverport, 0);
2148  if (ret == GRN_CONNECTION_REFUSED) {
2149  grn_sleep(1);
2150  retry++;
2151  if (retry > 5) {
2152  fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
2153  grntest_serverhost, grntest_serverport, ret);
2154  return 1;
2155  }
2156  continue;
2157  }
2158  if (ret) {
2159  fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
2160  grntest_serverhost, grntest_serverport, ret);
2161  return 1;
2162  }
2163  break;
2164  }
2165  return 0;
2166 }
2167 
2168 #define MODE_LIST 1
2169 #define MODE_GET 2
2170 #define MODE_PUT 3
2171 #define MODE_TIME 4
2172 
2173 static int
2174 check_response(char *buf)
2175 {
2176  if (buf[0] == '1') {
2177  return 1;
2178  }
2179  if (buf[0] == '2') {
2180  return 1;
2181  }
2182  if (buf[0] == '3') {
2183  return 1;
2184  }
2185  return 0;
2186 }
2187 
2188 static int
2189 read_response(socket_t socket, char *buf)
2190 {
2191  int ret;
2192  ret = recv(socket, buf, BUF_LEN - 1, 0);
2193  if (ret == -1) {
2194  fprintf(stderr, "recv error:3\n");
2195  exit(1);
2196  }
2197  buf[ret] ='\0';
2198 #ifdef DEBUG_FTP
2199  fprintf(stderr, "recv:%s", buf);
2200 #endif
2201  return ret;
2202 }
2203 
2204 static int
2205 put_file(socket_t socket, const char *filename)
2206 {
2207  FILE *fp;
2208  int c, ret, size = 0;
2209  char buf[1];
2210 
2211  fp = fopen(filename, "rb");
2212  if (!fp) {
2213  fprintf(stderr, "LOCAL:no such file:%s\n", filename);
2214  return 0;
2215  }
2216 
2217  while ((c = fgetc(fp)) != EOF) {
2218  buf[0] = c;
2219  ret = send(socket, buf, 1, 0);
2220  if (ret == -1) {
2221  fprintf(stderr, "send error\n");
2222  exit(1);
2223  }
2224  size++;
2225  }
2226  fclose(fp);
2227  return size;
2228 }
2229 
2230 static int
2231 ftp_list(socket_t data_socket)
2232 {
2233  int ret;
2234  char buf[BUF_LEN];
2235 
2236  while (1) {
2237  ret = recv(data_socket, buf, BUF_LEN - 2, 0);
2238  if (ret == 0) {
2239  fflush(stdout);
2240  return 0;
2241  }
2242  buf[ret] = '\0';
2243  fprintf(stdout, "%s", buf);
2244  }
2245 
2246  return 0;
2247 }
2248 
2249 static int
2250 get_file(socket_t socket, const char *filename, int size)
2251 {
2252  FILE *fp;
2253  int ret, total;
2254  char buf[FTPBUF];
2255 
2256  fp = fopen(filename, "wb");
2257  if (!fp) {
2258  fprintf(stderr, "Cannot open %s\n", filename);
2259  return -1;
2260  }
2261 
2262  total = 0;
2263  while (total != size) {
2264  ret = recv(socket, buf, FTPBUF, 0);
2265  if (ret == -1) {
2266  fprintf(stderr, "recv error:2:ret=%d:size=%d:total\n", ret, size);
2267  return -1;
2268  }
2269  if (ret == 0) {
2270  break;
2271  }
2272  fwrite(buf, ret, 1, fp);
2273  total = total + ret;
2274  }
2275 
2276  fclose(fp);
2277  return size;
2278 }
2279 
2280 static int
2281 get_port(char *buf, char *host, int *port)
2282 {
2283  int ret,d1,d2,d3,d4,d5,d6;
2284  ret = sscanf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
2285  &d1, &d2, &d3, &d4, &d5, &d6);
2286  if (ret != 6) {
2287  fprintf(stderr, "Cannot enter passsive mode\n");
2288  return 0;
2289  }
2290 
2291  *port = d5 * 256 + d6;
2292  sprintf(host, "%d.%d.%d.%d", d1, d2, d3, d4);
2293  return 1;
2294 }
2295 
2296 static char *
2297 get_ftp_date(char *buf)
2298 {
2299  while (*buf !=' ') {
2300  buf++;
2301  if (*buf == '\0') {
2302  return NULL;
2303  }
2304  }
2305  buf++;
2306 
2307  return buf;
2308 }
2309 
2310 static int
2311 get_size(char *buf)
2312 {
2313  int size;
2314 
2315  while (*buf !='(') {
2316  buf++;
2317  if (*buf == '\0') {
2318  return 0;
2319  }
2320  }
2321  buf++;
2322  size = grntest_atoi(buf, buf + strlen(buf), NULL);
2323 
2324  return size;
2325 }
2326 
2327 int
2328 ftp_sub(const char *user, const char *passwd, const char *host,
2329  const char *filename, int mode,
2330  const char *cd_dirname, char *retval)
2331 {
2332  int size = 0;
2333  int status = 0;
2334  socket_t command_socket, data_socket;
2335  int data_port;
2336  char data_host[BUF_LEN];
2337  char send_mesg[BUF_LEN];
2338  char buf[BUF_LEN];
2339 #ifdef WIN32
2340  char base[BUF_LEN];
2341  char fname[BUF_LEN];
2342  char ext[BUF_LEN];
2343 #else
2344  char *base;
2345 #endif /* WIN32 */
2346 
2347 #ifdef WIN32
2348  WSADATA ws;
2349 
2350  WSAStartup(MAKEWORD(2,0), &ws);
2351 #endif /* WIN32 */
2352 
2353  if ((filename != NULL) && (strlen(filename) >= MAX_PATH_LEN)) {
2354  fprintf(stderr, "too long filename\n");
2355  exit(1);
2356  }
2357 
2358  if ((cd_dirname != NULL) && (strlen(cd_dirname) >= MAX_PATH_LEN)) {
2359  fprintf(stderr, "too long dirname\n");
2360  exit(1);
2361  }
2362 
2363  command_socket = open_socket(host, 21);
2364  if (command_socket == SOCKETERROR) {
2365  return 0;
2366  }
2367 
2368  read_response(command_socket, buf);
2369  if (!check_response(buf)) {
2370  goto exit;
2371  }
2372 
2373  /* send username */
2374  sprintf(send_mesg, "USER %s\r\n", user);
2375  write_to_server(command_socket, send_mesg);
2376  read_response(command_socket, buf);
2377  if (!check_response(buf)) {
2378  goto exit;
2379  }
2380 
2381  /* send passwd */
2382  sprintf(send_mesg, "PASS %s\r\n", passwd);
2383  write_to_server(command_socket, send_mesg);
2384  read_response(command_socket, buf);
2385  if (!check_response(buf)) {
2386  goto exit;
2387  }
2388 
2389  /* send TYPE I */
2390  sprintf(send_mesg, "TYPE I\r\n");
2391  write_to_server(command_socket, send_mesg);
2392  read_response(command_socket, buf);
2393  if (!check_response(buf)) {
2394  goto exit;
2395  }
2396 
2397  /* send PASV */
2398  sprintf(send_mesg, "PASV\r\n");
2399  write_to_server(command_socket, send_mesg);
2400  read_response(command_socket, buf);
2401  if (!check_response(buf)) {
2402  goto exit;
2403  }
2404 
2405  if (!get_port(buf, data_host, &data_port)) {
2406  goto exit;
2407  }
2408 
2409  data_socket = open_socket(data_host, data_port);
2410  if (data_socket == SOCKETERROR) {
2411  goto exit;
2412  }
2413 
2414  if (cd_dirname) {
2415  sprintf(send_mesg, "CWD %s\r\n", cd_dirname);
2416  write_to_server(command_socket, send_mesg);
2417  }
2418 
2419  read_response(command_socket, buf);
2420  if (!check_response(buf)) {
2421  socketclose(data_socket);
2422  goto exit;
2423  }
2424 
2425 #ifdef WIN32
2426  _splitpath(filename, NULL, NULL, fname, ext);
2427  strcpy(base, fname);
2428  strcat(base, ext);
2429 #else
2430  strcpy(buf, filename);
2431  base = basename(buf);
2432 #endif /* WIN32 */
2433 
2434  switch (mode) {
2435  case MODE_LIST:
2436  if (filename) {
2437  sprintf(send_mesg, "LIST %s\r\n", filename);
2438  } else {
2439  sprintf(send_mesg, "LIST \r\n");
2440  }
2441  write_to_server(command_socket, send_mesg);
2442  break;
2443  case MODE_PUT:
2444  sprintf(send_mesg, "STOR %s\r\n", base);
2445  write_to_server(command_socket, send_mesg);
2446  break;
2447  case MODE_GET:
2448  sprintf(send_mesg, "RETR %s\r\n", base);
2449  write_to_server(command_socket, send_mesg);
2450  break;
2451  case MODE_TIME:
2452  sprintf(send_mesg, "MDTM %s\r\n", base);
2453  write_to_server(command_socket, send_mesg);
2454  break;
2455  default:
2456  fprintf(stderr, "invalid mode\n");
2457  socketclose(data_socket);
2458  goto exit;
2459  }
2460 
2461  read_response(command_socket, buf);
2462  if (!check_response(buf)) {
2463  socketclose(data_socket);
2464  goto exit;
2465  }
2466  if (!strncmp(buf, "150", 3)) {
2467  size = get_size(buf);
2468  }
2469  if (!strncmp(buf, "213", 3)) {
2470  retval[BUF_LEN-2] = '\0';
2471  strcpy(retval, get_ftp_date(buf));
2472  if (retval[BUF_LEN-2] != '\0' ) {
2473  fprintf(stderr, "buffer over run in ftp\n");
2474  exit(1);
2475  }
2476  }
2477 
2478  switch (mode) {
2479  case MODE_LIST:
2480  ftp_list(data_socket);
2481  break;
2482  case MODE_GET:
2483  if (get_file(data_socket, filename, size) == -1) {
2484  socketclose(data_socket);
2485  goto exit;
2486  }
2487  fprintf(stderr, "get:%s\n", filename);
2488  break;
2489  case MODE_PUT:
2490  if (put_file(data_socket, filename) == -1) {
2491  socketclose(data_socket);
2492  goto exit;
2493  }
2494  fprintf(stderr, "put:%s\n", filename);
2495  break;
2496  default:
2497  break;
2498  }
2499 
2500  socketclose(data_socket);
2501  if ((mode == MODE_GET) || (mode == MODE_PUT)) {
2502  read_response(command_socket, buf);
2503  }
2504  write_to_server(command_socket, "QUIT\n");
2505  status = 1;
2506 exit:
2507  socketclose(command_socket);
2508 
2509 #ifdef WIN32
2510  WSACleanup();
2511 #endif
2512  return status;
2513 }
2514 
2515 /*
2516 static int
2517 ftp_main(int argc, char **argv)
2518 {
2519  char val[BUF_LEN];
2520  val[0] = '\0';
2521  ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, argv[2],
2522  grntest_atoi(argv[3], argv[3] + strlen(argv[3]), NULL), argv[4], val);
2523  if (val[0] != '\0') {
2524  printf("val=%s\n", val);
2525  }
2526  return 0;
2527 }
2528 */
2529 
2530 static int
2531 get_username(char *name, int maxlen)
2532 {
2533  char *env=NULL;
2534  strcpy(name, "nobody");
2535 #ifdef WIN32
2536  env = getenv("USERNAME");
2537 #else
2538  env = getenv("USER");
2539 #endif /* WIN32 */
2540  if (strlen(env) > maxlen) {
2541  fprintf(stderr, "too long username:%s\n", env);
2542  exit(1);
2543  }
2544  if (env) {
2545  strcpy(name, env);
2546  }
2547  return 0;
2548 }
2549 
2550 static int
2551 get_date(char *date, time_t *sec)
2552 {
2553 #if defined(WIN32) && !defined(__GNUC__)
2554  struct tm tmbuf;
2555  struct tm *tm = &tmbuf;
2556  localtime_s(tm, sec);
2557 #else /* defined(WIN32) && !defined(__GNUC__) */
2558 # ifdef HAVE_LOCALTIME_R
2559  struct tm result;
2560  struct tm *tm = &result;
2561  localtime_r(sec, tm);
2562 # else /* HAVE_LOCALTIME_R */
2563  struct tm *tm = localtime(sec);
2564 # endif /* HAVE_LOCALTIME_R */
2565 #endif /* defined(WIN32) && !defined(__GNUC__) */
2566 
2567 #ifdef WIN32
2568  strftime(date, 128, "%Y-%m-%d %H:%M:%S", tm);
2569 #else
2570  strftime(date, 128, "%F %T", tm);
2571 #endif /* WIN32 */
2572 
2573  return 1;
2574 }
2575 
2576 static int
2577 get_scriptname(const char *path, char *name, const char *suffix)
2578 {
2579  int slen = strlen(suffix);
2580  int len = strlen(path);
2581 
2582  if (len >= BUF_LEN) {
2583  fprintf(stderr, "too long script name\n");
2584  exit(1);
2585  }
2586  if (slen > len) {
2587  fprintf(stderr, "too long suffux\n");
2588  exit(1);
2589  }
2590 
2591  strcpy(name, path);
2592  if (strncmp(&name[len-slen], suffix, slen)) {
2593  name[0] = '\0';
2594  return 0;
2595  }
2596  name[len-slen] = '\0';
2597  return 1;
2598 }
2599 
2600 #ifdef WIN32
2601 static int
2602 get_tm_from_serverdate(char *serverdate, struct tm *tm)
2603 {
2604  int res;
2605  int year, month, day, hour, minute, second;
2606 
2607  res = sscanf(serverdate, "%4d%2d%2d%2d%2d%2d",
2608  &year, &month, &day, &hour, &minute, &second);
2609 
2610 /*
2611  printf("%d %d %d %d %d %d\n", year, month, day, hour, minute, second);
2612 */
2613 
2614  tm->tm_sec = second;
2615  tm->tm_min = minute;
2616  tm->tm_hour = hour;
2617  tm->tm_mday = day;
2618  tm->tm_mon = month - 1;
2619  tm->tm_year = year - 1900;
2620  tm->tm_isdst = -1;
2621 
2622  return 0;
2623 }
2624 #endif /* WIN32 */
2625 
2626 
2627 
2628 static int
2629 sync_sub(grn_ctx *ctx, const char *filename)
2630 {
2631  int ret;
2632  char serverdate[BUF_LEN];
2633 #ifdef WIN32
2634  struct _stat statbuf;
2635 #else
2636  struct stat statbuf;
2637 #endif /* WIN32 */
2638  time_t st, lt;
2639  struct tm stm;
2640 
2641  ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_TIME, "data",
2642  serverdate);
2643  if (ret == 0) {
2644  fprintf(stderr, "[%s] does not exist in server\n", filename);
2645  return 0;
2646  }
2647 #ifdef WIN32
2648  get_tm_from_serverdate(serverdate, &stm);
2649 #else
2650  strptime(serverdate, "%Y%m%d %H%M%S", &stm);
2651 #endif /* WIN32 */
2652 
2653  /* fixme! needs timezone info */
2654  st = mktime(&stm) + 3600 * 9;
2655  lt = st;
2656 
2657 #ifdef WIN32
2658  ret = _stat(filename, &statbuf);
2659 #else
2660  ret = stat(filename, &statbuf);
2661 #endif /* WIN32 */
2662 
2663  if (!ret) {
2664  lt = statbuf.st_mtime;
2665  if (lt < st) {
2666  fprintf(stderr, "newer [%s] exists in server\n", filename);
2667  fflush(stderr);
2668  ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_GET, "data",
2669  NULL);
2670  return ret;
2671  }
2672  } else {
2673  fprintf(stderr, "[%s] does not exist in local\n", filename);
2674  fflush(stderr);
2675  ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_GET, "data",
2676  NULL);
2677  return ret;
2678  }
2679  return 0;
2680 }
2681 
2682 static int
2683 cache_file(grn_ctx *ctx, char **flist, char *file, int fnum)
2684 {
2685  int i;
2686 
2687  for (i = 0; i < fnum; i++) {
2688  if (!strcmp(flist[i], file) ) {
2689  return fnum;
2690  }
2691  }
2692  flist[fnum] = GRN_STRDUP(file);
2693  fnum++;
2694  if (fnum >= BUF_LEN) {
2695  fprintf(stderr, "too many uniq commands file!\n");
2696  exit(1);
2697  }
2698  return fnum;
2699 }
2700 
2701 static int
2702 sync_datafile(grn_ctx *ctx, const char *script_file_path)
2703 {
2704  int line = 0;
2705  int fnum = 0;
2706  int i, job_num;
2707  FILE *fp;
2708  char buf[BUF_LEN];
2709  char *filelist[BUF_LEN];
2710 
2711  fp = fopen(script_file_path, "r");
2712  if (fp == NULL) {
2713  fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
2714  error_exit(ctx, 1);
2715  }
2716  buf[BUF_LEN-2] = '\0';
2717  while (fgets(buf, BUF_LEN, fp) != NULL) {
2718  line++;
2719  if (buf[BUF_LEN-2] != '\0') {
2720  fprintf(stderr, "Too long line in script file:%d\n", line);
2721  error_exit(ctx, 1);
2722  }
2723  job_num = get_jobs(ctx, buf, line);
2724 
2725  if (job_num > 0) {
2726  for (i = 0; i < job_num; i++) {
2727 /*
2728 printf("commandfile=[%s]:buf=%s\n", grntest_job[i].commandfile, buf);
2729 */
2730  fnum = cache_file(ctx, filelist, grntest_job[i].commandfile, fnum);
2731  }
2732  }
2733  }
2734  for (i = 0; i < fnum; i++) {
2735  if (sync_sub(ctx, filelist[i])) {
2736  fprintf(stderr, "updated!:%s\n", filelist[i]);
2737  fflush(stderr);
2738  }
2739  GRN_FREE(filelist[i]);
2740  }
2741  fclose(fp);
2742  return fnum;
2743 }
2744 
2745 static int
2746 sync_script(grn_ctx *ctx, const char *filename)
2747 {
2748  int ret, filenum;
2749 
2750  ret = sync_sub(ctx, filename);
2751  if (!ret) {
2752  return 0;
2753  }
2754 
2755  fprintf(stderr, "updated!:%s\n", filename);
2756  fflush(stderr);
2757  filenum = sync_datafile(ctx, filename);
2758  return 1;
2759 }
2760 
2761 static void
2762 usage(void)
2763 {
2764  fprintf(stderr,
2765  "Usage: grntest [options...] [script] [db]\n"
2766  "options:\n"
2767  " --dir: show script files on ftp server\n"
2768  " -i, --host <ip/hostname>: server address to listen (default: %s)\n"
2769  " --localonly: omit server connection\n"
2770  " --log-output-dir: specify output dir (default: current)\n"
2771  " --ftp: connect to ftp server\n"
2772  " --onmemory: load all commands into memory\n"
2773  " --output-type <tsv/json>: specify output-type (default: json)\n"
2774  " --owndb: open dbs for each ctx\n"
2775  " -p, --port <port number>: server port number (default: %d)\n"
2776  " --groonga <groonga_path>: groonga command path (default: %s)\n"
2777  " --protocol <gqtp|http>: groonga server protocol (default: %s)\n"
2778  " --log-path <path>: specify log file path\n"
2779  " --pid-path <path>: specify file path to store PID file\n",
2781  groonga_path, groonga_protocol);
2782  exit(1);
2783 }
2784 
2785 enum {
2789 };
2790 
2791 #define MODE_MASK 0x007f
2792 #define MODE_FTP 0x0080
2793 #define MODE_LOCALONLY 0x0100
2794 #define MODE_OWNDB 0x0800
2795 #define MODE_ONMEMORY 0x1000
2796 
2797 
2798 static int
2799 get_token(char *line, char *token, int maxlen, char **next)
2800 {
2801  int i = 0;
2802 
2803  *next = NULL;
2804  token[i] = '\0';
2805 
2806  while (*line) {
2807  if (grn_isspace(line, GRN_ENC_UTF8) == 1) {
2808  line++;
2809  continue;
2810  }
2811  if (*line == ';') {
2812  token[0] = ';';
2813  token[1] = '\0';
2814  *next = line + 1;
2815  return 1;
2816  }
2817  if (*line == '#') {
2818  token[0] = ';';
2819  token[1] = '\0';
2820  *next = line + 1;
2821  return 1;
2822  }
2823  break;
2824  }
2825 
2826  while (*line) {
2827  token[i] = *line;
2828  i++;
2829  if (grn_isspace(line + 1, GRN_ENC_UTF8) == 1) {
2830  token[i] = '\0';
2831  *next = line + 1;
2832  return 1;
2833  }
2834  if (*(line + 1) == ';') {
2835  token[i] = '\0';
2836  *next = line + 1;
2837  return 1;
2838  }
2839  if (*(line + 1) == '#') {
2840  token[i] = '\0';
2841  *next = line + 1;
2842  return 1;
2843  }
2844  if (*(line + 1) == '\0') {
2845  token[i] = '\0';
2846  return 1;
2847  }
2848 
2849  line++;
2850  }
2851  return 0;
2852 }
2853 
2854 /* SET_PORT and SET_HOST */
2855 static grn_bool
2856 check_script(grn_ctx *ctx, const char *script_file_path)
2857 {
2858  FILE *script_file;
2859  grn_obj line;
2860  char token[BUF_LEN];
2861  char prev[BUF_LEN];
2862  char *next = NULL;
2863 
2864  script_file = fopen(script_file_path, "r");
2865  if (!script_file) {
2866  fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
2867  return GRN_FALSE;
2868  }
2869 
2870  GRN_TEXT_INIT(&line, 0);
2871  while (grn_text_fgets(ctx, &line, script_file) == GRN_SUCCESS) {
2872  GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] = '\0';
2873  get_token(GRN_TEXT_VALUE(&line), token, BUF_LEN, &next);
2874  strcpy(prev, token);
2875 
2876  while (next) {
2877  get_token(next, token, BUF_LEN, &next);
2878  if (!strncmp(prev, "SET_PORT", 8)) {
2879  grntest_serverport = grn_atoi(token, token + strlen(token), NULL);
2880  }
2881  if (!strncmp(prev, "SET_HOST", 8)) {
2882  strcpy(grntest_serverhost, token);
2883  grntest_remote_mode = 1;
2884  }
2885  strcpy(prev, token);
2886  }
2887  }
2888  grn_obj_unlink(ctx, &line);
2889 
2890  fclose(script_file);
2891  return GRN_TRUE;
2892 }
2893 
2894 #ifndef WIN32
2895 static void
2896 timeout(int sig)
2897 {
2898  fprintf(stderr, "timeout:groonga server cannot shutdown!!\n");
2899  fprintf(stderr, "Use \"kill -9 %d\"\n", grntest_server_id);
2900  alarm(0);
2901 }
2902 
2903 static void
2904 setexit(int sig)
2905 {
2906  grntest_sigint = 1;
2907 }
2908 
2909 static int
2910 setsigalarm(int sec)
2911 {
2912  int ret;
2913  struct sigaction sig;
2914 
2915  alarm(sec);
2916  sig.sa_handler = timeout;
2917  sig.sa_flags = 0;
2918  sigemptyset(&sig.sa_mask);
2919  ret = sigaction(SIGALRM, &sig, NULL);
2920  if (ret == -1) {
2921  fprintf(stderr, "setsigalarm:errno= %d\n", errno);
2922  }
2923  return ret;
2924 }
2925 
2926 static int
2927 setsigint(void)
2928 {
2929  int ret;
2930  struct sigaction sig;
2931 
2932  sig.sa_handler = setexit;
2933  sig.sa_flags = 0;
2934  sigemptyset(&sig.sa_mask);
2935  ret = sigaction(SIGINT, &sig, NULL);
2936  if (ret == -1) {
2937  fprintf(stderr, "setsigint:errno= %d\n", errno);
2938  }
2939  return ret;
2940 }
2941 #endif /* WIN32 */
2942 
2943 int
2944 main(int argc, char **argv)
2945 {
2946  int qnum, i, mode = 0;
2947  int exit_code = EXIT_SUCCESS;
2948  grn_ctx context;
2949  char sysinfo[BUF_LEN];
2950  char log_path_buffer[BUF_LEN];
2951  const char *log_path = NULL;
2952  const char *pid_path = NULL;
2953  const char *portstr = NULL, *hoststr = NULL, *dbname = NULL, *scrname = NULL, *outdir = NULL, *outtype = NULL;
2954  time_t sec;
2955 
2956  static grn_str_getopt_opt opts[] = {
2957  {'i', "host", NULL, 0, GETOPT_OP_NONE},
2958  {'p', "port", NULL, 0, GETOPT_OP_NONE},
2959  {'\0', "log-output-dir", NULL, 0, GETOPT_OP_NONE},
2960  {'\0', "output-type", NULL, 0, GETOPT_OP_NONE},
2961  {'\0', "dir", NULL, mode_list, GETOPT_OP_UPDATE},
2962  {'\0', "ftp", NULL, MODE_FTP, GETOPT_OP_ON},
2963  {'h', "help", NULL, mode_usage, GETOPT_OP_UPDATE},
2964  {'\0', "localonly", NULL, MODE_LOCALONLY, GETOPT_OP_ON},
2965  {'\0', "onmemory", NULL, MODE_ONMEMORY, GETOPT_OP_ON},
2966  {'\0', "owndb", NULL, MODE_OWNDB, GETOPT_OP_ON},
2967  {'\0', "groonga", NULL, 0, GETOPT_OP_NONE},
2968  {'\0', "protocol", NULL, 0, GETOPT_OP_NONE},
2969  {'\0', "log-path", NULL, 0, GETOPT_OP_NONE},
2970  {'\0', "pid-path", NULL, 0, GETOPT_OP_NONE},
2971  {'\0', NULL, NULL, 0, 0}
2972  };
2973 
2974  opts[0].arg = &hoststr;
2975  opts[1].arg = &portstr;
2976  opts[2].arg = &outdir;
2977  opts[3].arg = &outtype;
2978  opts[10].arg = &groonga_path;
2979  opts[11].arg = &groonga_protocol;
2980  opts[12].arg = &log_path;
2981  opts[13].arg = &pid_path;
2982 
2983  i = grn_str_getopt(argc, argv, opts, &mode);
2984  if (i < 0) {
2985  usage();
2986  }
2987 
2988  switch (mode & MODE_MASK) {
2989  case mode_list :
2990  ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, "*.scr", 1, "data",
2991  NULL);
2992  return 0;
2993  break;
2994  case mode_usage :
2995  usage();
2996  break;
2997  default :
2998  break;
2999  }
3000 
3001  if (pid_path) {
3002  FILE *pid_file;
3003  pid_file = fopen(pid_path, "w");
3004  if (pid_file) {
3005  fprintf(pid_file, "%d", getpid());
3006  fclose(pid_file);
3007  } else {
3008  fprintf(stderr,
3009  "failed to open PID file: <%s>: %s\n",
3010  pid_path, strerror(errno));
3011  }
3012  }
3013 
3014  if (i < argc) {
3015  scrname = argv[i];
3016  }
3017  if (i < argc - 1) {
3018  dbname = argv[i+1];
3019  }
3020  grntest_dbpath = dbname;
3021 
3022  if (mode & MODE_LOCALONLY) {
3023  grntest_localonly_mode = 1;
3024  grntest_remote_mode = 1;
3025  }
3026 
3027  if (mode & MODE_OWNDB) {
3028  grntest_localonly_mode = 1;
3029  grntest_remote_mode = 1;
3030  grntest_owndb_mode = 1;
3031  }
3032 
3033  if (mode & MODE_ONMEMORY) {
3034  grntest_onmemory_mode= 1;
3035  }
3036 
3037  if (mode & MODE_FTP) {
3038  grntest_ftp_mode = GRN_TRUE;
3039  }
3040 
3041  if ((scrname == NULL) || (dbname == NULL)) {
3042  usage();
3043  }
3044 
3045  strcpy(grntest_serverhost, DEFAULT_DEST);
3046  if (hoststr) {
3047  grntest_remote_mode = 1;
3048  strcpy(grntest_serverhost, hoststr);
3049  }
3050  grntest_serverport = DEFAULT_PORT;
3051  if (portstr) {
3052  grntest_serverport = grn_atoi(portstr, portstr + strlen(portstr), NULL);
3053  }
3054 
3055  if (outtype && !strcmp(outtype, "tsv")) {
3056  grntest_outtype = OUT_TSV;
3057  }
3058 
3059  grn_init();
3060  CRITICAL_SECTION_INIT(grntest_cs);
3061 
3062  grn_ctx_init(&context, 0);
3063  grn_ctx_init(&grntest_server_context, 0);
3064  grn_db_create(&grntest_server_context, NULL, NULL);
3066 
3067  if (grntest_ftp_mode) {
3068  sync_script(&context, scrname);
3069  }
3070  if (!check_script(&context, scrname)) {
3071  exit_code = EXIT_FAILURE;
3072  goto exit;
3073  }
3074 
3075  start_local(&context, dbname);
3076  if (!grntest_remote_mode) {
3077  start_server(dbname, 0);
3078  }
3079 
3080  if (!grntest_localonly_mode) {
3081  if (check_server(&grntest_server_context)) {
3082  goto exit;
3083  }
3084  }
3085 
3086  get_scriptname(scrname, grntest_scriptname, ".scr");
3087  get_username(grntest_username, 256);
3088 
3089  GRN_TIME_INIT(&grntest_starttime, 0);
3090  GRN_TIME_NOW(&context, &grntest_starttime);
3091  sec = (time_t)(GRN_TIME_VALUE(&grntest_starttime)/1000000);
3092  get_date(grntest_date, &sec);
3093 
3094  if (!log_path) {
3095  if (outdir) {
3096  sprintf(log_path_buffer,
3097  "%s/%s-%s-%" GRN_FMT_LLD "-%s.log", outdir, grntest_scriptname,
3098  grntest_username,
3099  GRN_TIME_VALUE(&grntest_starttime), grn_get_version());
3100  } else {
3101  sprintf(log_path_buffer,
3102  "%s-%s-%" GRN_FMT_LLD "-%s.log", grntest_scriptname,
3103  grntest_username,
3104  GRN_TIME_VALUE(&grntest_starttime), grn_get_version());
3105  }
3106  log_path = log_path_buffer;
3107  }
3108 
3109  grntest_log_file = fopen(log_path, "w+b");
3110  if (!grntest_log_file) {
3111  fprintf(stderr, "Cannot open log file: <%s>\n", log_path);
3112  goto exit;
3113  }
3114 
3115  get_sysinfo(dbname, sysinfo, BUF_LEN);
3116  output_sysinfo(sysinfo);
3117 
3118 #ifndef WIN32
3119  setsigint();
3120 #endif /* WIN32 */
3121  qnum = do_script(&context, scrname);
3122  output_result_final(&context, qnum);
3123  fclose(grntest_log_file);
3124 
3125  if (grntest_ftp_mode) {
3126  ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, log_path, 3,
3127  "report", NULL);
3128  }
3129  fprintf(stderr, "grntest done. logfile=%s\n", log_path);
3130 
3131 exit:
3132  if (pid_path) {
3133  remove(pid_path);
3134  }
3135 
3136  shutdown_server();
3137 #ifdef WIN32
3138  if (!grntest_remote_mode) {
3139  int ret;
3140  ret = WaitForSingleObject(grntest_pi.hProcess, 20000);
3141  if (ret == WAIT_TIMEOUT) {
3142  fprintf(stderr, "timeout:groonga server cannot shutdown!!\n");
3143  fprintf(stderr, "Cannot wait\n");
3144  exit(1);
3145  }
3146  }
3147 #else
3148  if (grntest_server_id) {
3149  int ret, pstatus;
3150  setsigalarm(20);
3151  ret = waitpid(grntest_server_id, &pstatus, 0);
3152  if (ret < 0) {
3153  fprintf(stderr, "Cannot wait\n");
3154  exit(1);
3155  }
3156 /*
3157  else {
3158  fprintf(stderr, "pstatus = %d\n", pstatus);
3159  }
3160 */
3161  alarm(0);
3162  }
3163 #endif /* WIN32 */
3164  CRITICAL_SECTION_FIN(grntest_cs);
3165  grn_obj_close(&context, &grntest_starttime);
3166  grn_obj_close(&context, grntest_db);
3167  grn_ctx_fin(&context);
3168  grn_obj_close(&grntest_server_context, grn_ctx_db(&grntest_server_context));
3169  grn_ctx_fin(&grntest_server_context);
3170  grn_fin();
3171  return exit_code;
3172 }