1 
2 /*
3  * Redistribution and use in source and binary forms, with or
4  * without modification, are permitted provided that the following
5  * conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above
8  *    copyright notice, this list of conditions and the
9  *    following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20  * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <stdbool.h>
34 #include <inttypes.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <assert.h>
40 
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 
47 #include <parser/queue.h>
48 #include <parser/utf8.h>
49 #include <parser/lex.h>
50 
51 #include "nosqlbench.h"
52 
53 extern struct nb nb;
54 
55 enum nb_config_index_type {
56 	NB_CONFIG_NONE,
57 	NB_CONFIG_INT,
58 	NB_CONFIG_STRING
59 };
60 
61 struct nb_config_index {
62 	int token;
63 	enum nb_config_index_type type;
64 	int *vi;
65 	char **vs;
66 };
67 
68 struct nb_config {
69 	struct tnt_lex lex;
70 	char *file;
71 };
72 
73 enum {
74 	NB_TK_CONFIGURATION = TNT_TK_CUSTOM,
75 	NB_TK_BENCHMARK,
76 	NB_TK_TIME_LIMIT,
77 	NB_TK_REQUEST_COUNT,
78 	NB_TK_REQUEST_BATCH_COUNT,
79 	NB_TK_REPORT_INTERVAL,
80 	NB_TK_REPORT_TYPE,
81 	NB_TK_CSV_FILE,
82 	NB_TK_CLIENT_HISTORY,
83 	NB_TK_CLIENT_CREATION_POLICY,
84 	NB_TK_CLIENT_CREATION_INTERVAL,
85 	NB_TK_CLIENT_CREATION_INCREMENT,
86 	NB_TK_CLIENT_START,
87 	NB_TK_CLIENT_MAX,
88 	NB_TK_DB_DRIVER,
89 	NB_TK_KEY_DISTRIBUTION,
90 	NB_TK_KEY_DISTRIBUTION_ITER,
91 	NB_TK_KEY_TYPE,
92 	NB_TK_VALUE_SIZE,
93 	NB_TK_REPLACE,
94 	NB_TK_UPDATE,
95 	NB_TK_DELETE,
96 	NB_TK_SELECT,
97 	NB_TK_SERVER,
98 	NB_TK_PORT,
99 	NB_TK_BUF_RECV,
100 	NB_TK_BUF_SEND
101 };
102 
103 #define NB_DECLARE_KEYWORD_DEF(NAME, ID) { NAME, sizeof(NAME) - 1, ID }
104 #define NB_DECLARE_KEYWORD(NAME, ID) { NAME, sizeof(NAME) - 1, ID }
105 #define NB_DECLARE_KEYWORD_END() { 0, 0, 0 }
106 
107 static struct tnt_lex_keyword nb_lex_keywords[] =
108 {
109 	NB_DECLARE_KEYWORD("configuration", NB_TK_CONFIGURATION),
110 	NB_DECLARE_KEYWORD("benchmark", NB_TK_BENCHMARK),
111 	NB_DECLARE_KEYWORD("time_limit", NB_TK_TIME_LIMIT),
112 	NB_DECLARE_KEYWORD("request_count", NB_TK_REQUEST_COUNT),
113 	NB_DECLARE_KEYWORD("request_batch_count", NB_TK_REQUEST_BATCH_COUNT),
114 	NB_DECLARE_KEYWORD("report_interval", NB_TK_REPORT_INTERVAL),
115 	NB_DECLARE_KEYWORD("report_type", NB_TK_REPORT_TYPE),
116 	NB_DECLARE_KEYWORD("csv_file", NB_TK_CSV_FILE),
117 	NB_DECLARE_KEYWORD("client_history", NB_TK_CLIENT_HISTORY),
118 	NB_DECLARE_KEYWORD("client_creation_policy", NB_TK_CLIENT_CREATION_POLICY),
119 	NB_DECLARE_KEYWORD("client_creation_interval", NB_TK_CLIENT_CREATION_INTERVAL),
120 	NB_DECLARE_KEYWORD("client_creation_increment", NB_TK_CLIENT_CREATION_INCREMENT),
121 	NB_DECLARE_KEYWORD("client_start", NB_TK_CLIENT_START),
122 	NB_DECLARE_KEYWORD("client_max", NB_TK_CLIENT_MAX),
123 	NB_DECLARE_KEYWORD("db_driver", NB_TK_DB_DRIVER),
124 	NB_DECLARE_KEYWORD("key_distribution", NB_TK_KEY_DISTRIBUTION),
125 	NB_DECLARE_KEYWORD("key_distribution_iter", NB_TK_KEY_DISTRIBUTION_ITER),
126 	NB_DECLARE_KEYWORD("key_type", NB_TK_KEY_TYPE),
127 	NB_DECLARE_KEYWORD("value_size", NB_TK_VALUE_SIZE),
128 	NB_DECLARE_KEYWORD("test_replace", NB_TK_REPLACE),
129 	NB_DECLARE_KEYWORD("test_update", NB_TK_UPDATE),
130 	NB_DECLARE_KEYWORD("test_delete", NB_TK_DELETE),
131 	NB_DECLARE_KEYWORD("test_select", NB_TK_SELECT),
132 	NB_DECLARE_KEYWORD("server", NB_TK_SERVER),
133 	NB_DECLARE_KEYWORD("port", NB_TK_PORT),
134 	NB_DECLARE_KEYWORD("buf_recv", NB_TK_BUF_RECV),
135 	NB_DECLARE_KEYWORD("buf_send", NB_TK_BUF_SEND),
136 	NB_DECLARE_KEYWORD_END()
137 };
138 
139 #define NB_DECLARE_OPT(ID, TYPE, I, S) { ID, TYPE, I, S }
140 #define NB_DECLARE_OPT_INT(ID, VALUE) \
141 	NB_DECLARE_OPT(ID, NB_CONFIG_INT, VALUE, NULL)
142 #define NB_DECLARE_OPT_STR(ID, VALUE) \
143 	NB_DECLARE_OPT(ID, NB_CONFIG_STRING, NULL, VALUE)
144 #define NB_DECLARE_OPT_END() \
145 	NB_DECLARE_OPT(0, NB_CONFIG_NONE, NULL, NULL)
146 
147 struct nb_config_index nb_config_index[] =
148 {
149 	NB_DECLARE_OPT_STR(NB_TK_BENCHMARK, &nb.opts.benchmark_policy_name),
150 	NB_DECLARE_OPT_INT(NB_TK_TIME_LIMIT, &nb.opts.time_limit),
151 	NB_DECLARE_OPT_INT(NB_TK_REQUEST_COUNT, &nb.opts.request_count),
152 	NB_DECLARE_OPT_INT(NB_TK_REQUEST_BATCH_COUNT, &nb.opts.request_batch_count),
153 	NB_DECLARE_OPT_INT(NB_TK_REPORT_INTERVAL, &nb.opts.report_interval),
154 	NB_DECLARE_OPT_STR(NB_TK_REPORT_TYPE, &nb.opts.report),
155 	NB_DECLARE_OPT_STR(NB_TK_CSV_FILE, &nb.opts.csv_file),
156 	NB_DECLARE_OPT_INT(NB_TK_CLIENT_HISTORY, &nb.opts.history_per_batch),
157 	NB_DECLARE_OPT_STR(NB_TK_CLIENT_CREATION_POLICY, &nb.opts.threads_policy_name),
158 	NB_DECLARE_OPT_INT(NB_TK_CLIENT_CREATION_INTERVAL, &nb.opts.threads_interval),
159 	NB_DECLARE_OPT_INT(NB_TK_CLIENT_CREATION_INCREMENT, &nb.opts.threads_increment),
160 	NB_DECLARE_OPT_INT(NB_TK_CLIENT_START, &nb.opts.threads_start),
161 	NB_DECLARE_OPT_INT(NB_TK_CLIENT_MAX, &nb.opts.threads_max),
162 	NB_DECLARE_OPT_STR(NB_TK_DB_DRIVER, &nb.opts.db),
163 	NB_DECLARE_OPT_STR(NB_TK_KEY_DISTRIBUTION, &nb.opts.key_dist),
164 	NB_DECLARE_OPT_INT(NB_TK_KEY_DISTRIBUTION_ITER, &nb.opts.key_dist_iter),
165 	NB_DECLARE_OPT_STR(NB_TK_KEY_TYPE, &nb.opts.key),
166 	NB_DECLARE_OPT_INT(NB_TK_VALUE_SIZE, &nb.opts.value_size),
167 	NB_DECLARE_OPT_INT(NB_TK_REPLACE, &nb.opts.dist_replace),
168 	NB_DECLARE_OPT_INT(NB_TK_UPDATE, &nb.opts.dist_update),
169 	NB_DECLARE_OPT_INT(NB_TK_DELETE, &nb.opts.dist_delete),
170 	NB_DECLARE_OPT_INT(NB_TK_SELECT, &nb.opts.dist_select),
171 	NB_DECLARE_OPT_STR(NB_TK_SERVER, &nb.opts.host),
172 	NB_DECLARE_OPT_INT(NB_TK_PORT, &nb.opts.port),
173 	NB_DECLARE_OPT_INT(NB_TK_BUF_RECV, &nb.opts.buf_recv),
174 	NB_DECLARE_OPT_INT(NB_TK_BUF_SEND, &nb.opts.buf_send),
175 	NB_DECLARE_OPT_END()
176 };
177 
178 static int
nb_config_error(struct nb_config * cfg,struct tnt_tk * last,char * fmt,...)179 nb_config_error(struct nb_config *cfg, struct tnt_tk *last, char *fmt, ...)
180 {
181 	char msgu[256];
182 	va_list args;
183 	va_start(args, fmt);
184 	vsnprintf(msgu, sizeof(msgu), fmt, args);
185 	va_end(args);
186 	int line = (last) ? last->line : cfg->lex.line;
187 	int col = (last) ? last->col : cfg->lex.col;
188 	printf("%s %d:%d %s\n", cfg->file, line, col, msgu);
189 	return TNT_TK_ERROR;
190 }
191 
192 static int
nb_config_expect(struct nb_config * cfg,int tk,struct tnt_tk ** tkp)193 nb_config_expect(struct nb_config *cfg, int tk, struct tnt_tk **tkp)
194 {
195 	struct tnt_tk *tkp_ = NULL;
196 	int tk_ = tnt_lex(&cfg->lex, &tkp_);
197 	if (tk_ == TNT_TK_ERROR)
198 		return nb_config_error(cfg, NULL, "%s", cfg->lex.error);
199 	if (tk_ != tk) {
200 		if (tk < 0xff && ispunct(tk))
201 			return nb_config_error(cfg, tkp_, "expected '%c'", tk);
202 		return nb_config_error(cfg, tkp_, "expected '%s'",
203 				       tnt_lex_nameof(&cfg->lex, tk));
204 	}
205 	if (tkp)
206 		*tkp = tkp_;
207 	return 0;
208 }
209 
nb_config_readint(struct nb_config * cfg,int * v)210 static int nb_config_readint(struct nb_config *cfg, int *v) {
211 	struct tnt_tk *tk = NULL;
212 	if (nb_config_expect(cfg, TNT_TK_NUM32, &tk) == -1)
213 		return -1;
214 	*v = TNT_TK_I32(tk);
215 	return 0;
216 }
217 
nb_config_readsz(struct nb_config * cfg,char ** v)218 static int nb_config_readsz(struct nb_config *cfg, char **v) {
219 	struct tnt_tk *tk = NULL;
220 	if (nb_config_expect(cfg, TNT_TK_STRING, &tk) == -1)
221 		return -1;
222 	if (*v)
223 		free(*v);
224 	*v = nb_malloc(TNT_TK_S(tk)->size + 1);
225 	memcpy(*v, (char*)TNT_TK_S(tk)->data, TNT_TK_S(tk)->size + 1);
226 	return 0;
227 }
228 
nb_config_read(struct nb_config * cfg,struct tnt_tk * tk)229 static int nb_config_read(struct nb_config *cfg, struct tnt_tk *tk)
230 {
231 	struct nb_config_index *it = &nb_config_index[0];
232 	for (; it->type != NB_CONFIG_NONE; it++) {
233 		if (it->token != tk->tk)
234 			continue;
235 		switch (it->type) {
236 		case NB_CONFIG_INT:
237 			if (!nb_config_readint(cfg, it->vi) == -1)
238 				return -1;
239 			break;
240 		case NB_CONFIG_STRING:
241 			if (!nb_config_readsz(cfg, it->vs) == -1)
242 				return -1;
243 			break;
244 		case NB_CONFIG_NONE:
245 			break;
246 		}
247 		return 0;
248 	}
249 	return -1;
250 }
251 
nb_config_process(struct nb_config * cfg)252 static int nb_config_process(struct nb_config *cfg)
253 {
254 	struct tnt_tk *tk;
255 	if (nb_config_expect(cfg, NB_TK_CONFIGURATION, NULL) == -1)
256 		return -1;
257 	if (nb_config_expect(cfg, '{', NULL) == -1)
258 		return -1;
259 	int eoc = 0;
260 	while (!eoc) {
261 		tnt_lex(&cfg->lex, &tk);
262 		if (nb_config_read(cfg, tk) == 0)
263 			continue;
264 		if (tk->tk == TNT_TK_PUNCT && tk->v.i32 == '}')
265 			eoc = 1;
266 		else
267 			return nb_config_error(cfg, tk, "unknown option");
268 	}
269 	return 0;
270 }
271 
nb_config_readfile(char * file,char ** buf,size_t * size)272 static int nb_config_readfile(char *file, char **buf, size_t *size)
273 {
274 	struct stat st;
275 	if (stat(file, &st) == -1) {
276 		printf("error: config file '%s': %s\n", file, strerror(errno));
277 		return -1;
278 	}
279 	int fd = open(file, O_RDONLY);
280 	if (fd == -1)
281 		return -1;
282 	*buf = nb_malloc(st.st_size);
283 	ssize_t off = 0;
284 	do {
285 		ssize_t r = read(fd, *buf + off, st.st_size - off);
286 		if (r == -1) {
287 			close(fd);
288 			free(*buf);
289 			printf("error: read(): %s\n", strerror(errno));
290 			return -1;
291 		}
292 		off += r;
293 	} while (off != st.st_size);
294 	*size = st.st_size;
295 	close(fd);
296 	return 0;
297 }
298 
nb_config_parse(char * file)299 int nb_config_parse(char *file)
300 {
301 	char *buf = NULL;
302 	size_t size;
303 
304 	if (nb_config_readfile(file, &buf, &size) == -1)
305 		return -1;
306 
307 	struct nb_config cfg;
308 	memset(&cfg, 0, sizeof(cfg));
309 
310 	if (tnt_lex_init(&cfg.lex, nb_lex_keywords, (unsigned char*)buf,
311 			 size) == -1) {
312 		free(buf);
313 		return -1;
314 	}
315 	free(buf);
316 
317 	cfg.file = file;
318 	int rc = nb_config_process(&cfg);
319 
320 	tnt_lex_free(&cfg.lex);
321 	return rc;
322 }
323