segfault/memleak on incorrect data fixed
[elliptics.git] / bindings / cpp / test.cpp
blob1bae062c3c2e9f999739f2d16b036c9cd7c7834c
1 /*
2 * 2008+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
3 * All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <sys/time.h>
17 #include <sys/resource.h>
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <string.h>
23 #include <sstream>
24 #include <fstream>
26 #include <elliptics/cppdef.h>
28 using namespace ioremap::elliptics;
30 static void test_log_raw(logger *l, int level, const char *format, ...)
32 va_list args;
33 char buf[1024];
34 int buflen = sizeof(buf);
36 if (l->get_log_level() < level)
37 return;
39 va_start(args, format);
40 vsnprintf(buf, buflen, format, args);
41 buf[buflen-1] = '\0';
42 l->log(level, buf);
43 va_end(args);
46 class callback_io : public callback {
47 public:
48 callback_io(logger *l) { log = l; };
49 virtual ~callback_io() {};
51 virtual int handle(struct dnet_net_state *state, struct dnet_cmd *cmd);
53 private:
54 logger *log;
57 int callback_io::handle(struct dnet_net_state *state, struct dnet_cmd *cmd)
59 int err;
60 struct dnet_io_attr *io;
62 if (is_trans_destroyed(state, cmd)) {
63 err = -EINVAL;
64 goto err_out_exit;
67 if (cmd->status || !cmd->size) {
68 err = cmd->status;
69 goto err_out_exit;
72 if (cmd->size <= sizeof(struct dnet_io_attr)) {
73 test_log_raw(log, DNET_LOG_ERROR, "%s: read completion error: wrong size: "
74 "cmd_size: %llu, must be more than %zu.\n",
75 dnet_dump_id(&cmd->id), (unsigned long long)cmd->size,
76 sizeof(struct dnet_io_attr));
77 err = -EINVAL;
78 goto err_out_exit;
81 if (!cmd->size) {
82 test_log_raw(log, DNET_LOG_ERROR, "%s: no attributes but command size is not null.\n",
83 dnet_dump_id(&cmd->id));
84 err = -EINVAL;
85 goto err_out_exit;
88 io = (struct dnet_io_attr *)(cmd + 1);
90 dnet_convert_io_attr(io);
91 err = 0;
93 test_log_raw(log, DNET_LOG_INFO, "%s: io completion: offset: %llu, size: %llu.\n",
94 dnet_dump_id(&cmd->id), (unsigned long long)io->offset, (unsigned long long)io->size);
96 err_out_exit:
97 if (!cmd || !(cmd->flags & DNET_FLAGS_MORE))
98 test_log_raw(log, DNET_LOG_INFO, "%s: io completed: %d.\n", cmd ? dnet_dump_id(&cmd->id) : "nil", err);
99 return err;
102 static void test_prepare_commit(session &s, int psize, int csize)
104 std::string written, ret;
105 try {
106 std::string key = "prepare-commit-test";
108 std::string prepare_data = "prepare data|";
109 std::string commit_data = "commit data";
110 std::string plain_data[3] = {"plain data0|", "plain data1|", "plain data2|"};
112 if (psize)
113 prepare_data.clear();
114 if (csize)
115 commit_data.clear();
117 uint64_t offset = 0;
118 uint64_t total_size_to_reserve = 1024;
120 uint64_t cflags = 0;
121 unsigned int ioflags = 0;
123 int column = 0;
125 s.write_prepare(key, prepare_data, offset, total_size_to_reserve, cflags, ioflags, column);
126 offset += prepare_data.size();
128 written += prepare_data;
130 for (int i = 0; i < 3; ++i) {
131 s.write_plain(key, plain_data[i], offset, cflags, ioflags, column);
132 offset += plain_data[i].size();
134 written += plain_data[i];
137 /* append data first so that subsequent written.size() call returned real size of the written data */
138 written += commit_data;
140 s.write_commit(key, commit_data, offset, written.size(), cflags, ioflags, column);
142 ret = s.read_data_wait(key, 0, 0, cflags, ioflags, column);
143 std::cout << "prepare/commit write: '" << written << "', read: '" << ret << "'" << std::endl;
144 } catch (const std::exception &e) {
145 std::cerr << "PREPARE/COMMIT test failed: " << e.what() << std::endl;
146 throw;
149 if (ret != written) {
150 std::cerr << "PREPARE/COMMIT test failed: read mismatch" << std::endl;
151 throw std::runtime_error("PREPARE/COMMIT test failed: read mismatch");
155 static void test_range_request(session &s, int limit_start, int limit_num, uint64_t cflags, int group_id)
157 struct dnet_io_attr io;
159 memset(&io, 0, sizeof(io));
161 #if 0
162 dnet_parse_numeric_id("76a046fcd25ebeaaa65a0fa692faf8b8701695c6ba67008b5922ae9f134fc1da7ffffed191edf767000000000000000000000000000000000000000000000000", &io.id);
163 dnet_parse_numeric_id("76a046fcd25ebeaaa65a0fa692faf8b8701695c6ba67008b5922ae9f134fc1da7ffffed22220037fffffffffffffffffffffffffffffffffffffffffffffffff", &io.parent);
164 #else
165 memset(io.id, 0x00, sizeof(io.id));
166 memset(io.parent, 0xff, sizeof(io.id));
167 #endif
168 io.start = limit_start;
169 io.num = limit_num;
171 std::vector<std::string> ret;
172 ret = s.read_data_range(io, group_id, cflags);
174 std::cout << "range [LIMIT(" << limit_start << ", " << limit_num << "): " << ret.size() << " elements" << std::endl;
175 #if 0
176 for (size_t i = 0; i < ret.size(); ++i) {
177 char id_str[DNET_ID_SIZE * 2 + 1];
178 const char *data = ret[i].data();
179 const unsigned char *id = (const unsigned char *)data;
180 uint64_t size = dnet_bswap64(*(uint64_t *)(data + DNET_ID_SIZE));
181 char *str = (char *)(data + DNET_ID_SIZE + 8);
183 std::cout << "range [LIMIT(" << limit_start << ", " << limit_num << "): " <<
184 dnet_dump_id_len_raw(id, DNET_ID_SIZE, id_str) << ": size: " << size << ": " << str << std::endl;
186 #endif
189 static void test_lookup_parse(const std::string &key, const std::string &lret)
191 struct dnet_addr *addr = (struct dnet_addr *)lret.data();
192 struct dnet_cmd *cmd = (struct dnet_cmd *)(addr + 1);
193 struct dnet_addr_attr *a = (struct dnet_addr_attr *)(cmd + 1);
195 dnet_convert_addr_attr(a);
196 std::cout << key << ": lives on addr: " << dnet_server_convert_dnet_addr(&a->addr);
198 if (cmd->size > sizeof(struct dnet_addr_attr)) {
199 struct dnet_file_info *info = (struct dnet_file_info *)(a + 1);
201 dnet_convert_file_info(info);
202 std::cout << ": mode: " << std::oct << info->mode << std::dec;
203 std::cout << ", offset: " << (unsigned long long)info->offset;
204 std::cout << ", size: " << (unsigned long long)info->size;
205 std::cout << ", file: " << (char *)(info + 1);
207 std::cout << std::endl;
210 static void test_lookup(session &s, std::vector<int> &groups)
212 try {
213 std::string key = "2.xml";
214 std::string data = "lookup data";
216 std::string lret = s.write_data_wait(key, data, 0, 0, 0, 0);
217 test_lookup_parse(key, lret);
219 struct dnet_id id;
220 s.transform(key, id);
221 id.group_id = 0;
222 id.type = 0;
224 uint64_t cflags = 0;
226 struct timespec ts = {0, 0};
227 s.write_metadata(id, key, groups, ts, cflags);
229 lret = s.lookup(key);
230 test_lookup_parse(key, lret);
231 } catch (const std::exception &e) {
232 std::cerr << "LOOKUP test failed: " << e.what() << std::endl;
236 static void test_append(session &s)
238 try {
239 std::string key = "append-test";
240 std::string data = "first part of the message";
242 s.write_data_wait(key, data, 0, 0, 0, 0);
244 data = "| second part of the message";
245 s.write_data_wait(key, data, 0, 0, DNET_IO_FLAGS_APPEND, 0);
247 std::cout << key << ": " << s.read_data_wait(key, 0, 0, 0, 0, 0) << std::endl;
248 } catch (const std::exception &e) {
249 std::cerr << "APPEND test failed: " << e.what() << std::endl;
250 throw std::runtime_error("APPEND test failed");
254 static void read_column_raw(session &s, const std::string &key, const std::string &data, int column)
256 std::string ret;
257 try {
258 ret = s.read_data_wait(key, 0, 0, 0, 0, column);
259 } catch (const std::exception &e) {
260 std::cerr << "COLUMN-" << column << " read test failed: " << e.what() << std::endl;
261 throw;
264 std::cout << "read-column-" << column << ": " << key << " : " << ret << std::endl;
265 if (ret != data) {
266 throw std::runtime_error("column test failed");
270 static void column_test(session &s)
272 std::string key = "some-key-1";
274 std::string data0 = "some-compressed-data-in-column-0";
275 std::string data1 = "some-data-in-column-2";
276 std::string data2 = "some-data-in-column-3";
278 s.write_data_wait(key, data0, 0, 0, DNET_IO_FLAGS_COMPRESS, 0);
279 s.write_data_wait(key, data1, 0, 0, 0, 2);
280 s.write_data_wait(key, data2, 0, 0, 0, 3);
282 read_column_raw(s, key, data0, 0);
283 read_column_raw(s, key, data1, 2);
284 read_column_raw(s, key, data2, 3);
287 static void test_bulk_write(session &s)
289 try {
290 std::vector<struct dnet_io_attr> ios;
291 std::vector<std::string> data;
293 int i;
295 for (i = 0; i < 3; ++i) {
296 std::ostringstream os;
297 struct dnet_io_attr io;
298 struct dnet_id id;
300 os << "bulk_write" << i;
302 memset(&io, 0, sizeof(io));
303 memset(&id, 0, sizeof(id));
305 s.transform(os.str(), id);
306 memcpy(io.id, id.id, DNET_ID_SIZE);
307 io.type = id.type;
308 io.size = os.str().size();
310 ios.push_back(io);
311 data.push_back(os.str());
314 std::string ret = s.bulk_write(ios, data, 0);
316 std::cout << "ret size = " << ret.size() << std::endl;
318 uint64_t cflags = 0;
319 int ioflags = DNET_IO_FLAGS_NOCSUM;
320 int type = 0;
322 uint64_t offset = 0;
323 uint64_t size = 0;
325 /* read without checksums since we did not write metadata */
326 for (i = 0; i < 3; ++i) {
327 std::ostringstream os;
329 os << "bulk_write" << i;
330 std::cout << os.str() << ": " << s.read_data_wait(os.str(), offset, size, cflags, ioflags, type) << std::endl;
332 } catch (const std::exception &e) {
333 std::cerr << "BULK WRITE test failed: " << e.what() << std::endl;
337 static void test_bulk_read(session &s)
339 try {
340 std::vector<std::string> keys;
342 int i;
344 for (i = 0; i < 3; ++i) {
345 std::ostringstream os;
346 os << "bulk_write" << i;
347 keys.push_back(os.str());
350 std::vector<std::string> ret = s.bulk_read(keys, 0);
352 std::cout << "ret size = " << ret.size() << std::endl;
354 /* read without checksums since we did not write metadata */
355 for (i = 0; i < 3; ++i) {
356 std::ostringstream os;
358 os << "bulk_read" << i;
359 std::cout << os.str() << ": " << ret[i].substr(DNET_ID_SIZE + 8) << std::endl;
361 } catch (const std::exception &e) {
362 std::cerr << "BULK READ test failed: " << e.what() << std::endl;
367 static void memory_test_io(session &s, int num)
369 int ids[16];
371 for (int i = 0; i < num; ++i) {
372 std::string data;
374 data.resize(rand() % 102400 + 100);
376 for (int j = 0; j < (int)ARRAY_SIZE(ids); ++j)
377 ids[j] = rand();
379 std::string id((char *)ids, sizeof(ids));
380 std::string written;
382 try {
383 written = s.write_data_wait(id, data, 0, 0, 0, 0);
384 std::string res = s.read_data_wait(id, 0, 0, 0, 0, 0);
385 } catch (const std::exception &e) {
386 std::cerr << "could not perform read/write: " << e.what() << std::endl;
387 if (written.size() > 0) {
388 std::cerr << "but written successfully\n";
389 test_lookup_parse(id, written);
396 static void test_cache_write(session &s, int num)
398 try {
399 std::vector<struct dnet_io_attr> ios;
400 std::vector<std::string> data;
402 int i;
404 for (i = 0; i < num; ++i) {
405 std::ostringstream os;
406 struct dnet_io_attr io;
407 struct dnet_id id;
409 os << "test_cache" << i;
411 memset(&io, 0, sizeof(io));
412 memset(&id, 0, sizeof(id));
414 s.transform(os.str(), id);
415 memcpy(io.id, id.id, DNET_ID_SIZE);
416 io.type = id.type;
417 io.size = os.str().size();
419 ios.push_back(io);
420 data.push_back(os.str());
423 s.bulk_write(ios, data, 0);
424 } catch (const std::exception &e) {
425 std::cerr << "cache write test failed: " << e.what() << std::endl;
427 std::cout << "Cache entries writted: " << num << std::endl;
430 static void test_cache_read(session &s, int num)
432 int count = 0;
434 /* Read random 20% of records written by test_cache_write() */
435 for (int i = 0; i < num; ++i) {
436 if ((rand() % 100) > 20)
437 continue;
439 std::ostringstream os;
441 os << "test_cache" << i;
443 std::string id(os.str());
445 uint64_t cflags = 0;
446 int ioflags = DNET_IO_FLAGS_NOCSUM;
447 int type = 0;
449 uint64_t offset = 0;
450 uint64_t size = 0;
452 try {
453 s.read_data_wait(os.str(), offset, size, cflags, ioflags, type);
454 } catch (const std::exception &e) {
455 std::cerr << "could not perform read : " << e.what() << std::endl;
457 count++;
459 std::cout << "Cache entries read: " << count << std::endl;
462 static void test_cache_delete(session &s, int num)
464 int count = 0;
466 /* Read random 20% of records written by test_cache_write() */
467 for (int i = 0; i < num; ++i) {
468 if ((rand() % 100) > 20)
469 continue;
471 std::ostringstream os;
473 os << "test_cache" << i;
475 std::string id(os.str());
477 try {
478 s.remove(id, 0);
479 } catch (const std::exception &e) {
480 std::cerr << "could not perform remove: " << e.what() << std::endl;
482 count++;
484 std::cout << "Cache entries deleted: " << count << std::endl;
487 static void memory_test(session &s)
489 struct rusage start, end;
491 getrusage(RUSAGE_SELF, &start);
492 memory_test_io(s, 1000);
493 getrusage(RUSAGE_SELF, &end);
494 std::cout << "IO leaked: " << end.ru_maxrss - start.ru_maxrss << " Kb\n";
497 void usage(char *p)
499 fprintf(stderr, "Usage: %s <options>\n"
500 " -r host - remote host name\n"
501 " -p port - remote port\n"
502 " -g group_id - group_id for range request and bulk write\n"
503 " -w - write cache before read\n"
504 " -m - start client's memory leak test (rather long - several minutes, and space consuming)\n"
505 , p);
506 exit(-1);
509 int main(int argc, char *argv[])
511 int g[] = {1, 2, 3};
512 std::vector<int> groups(g, g+ARRAY_SIZE(g));
513 char *host = (char *)"localhost";
514 int port = 1025;
515 int ch, write_cache = 0;
516 int mem_check = 0;
517 int group_id = 2;
519 while ((ch = getopt(argc, argv, "mr:p:g:wh")) != -1) {
520 switch (ch) {
521 case 'r':
522 host = optarg;
523 break;
524 case 'p':
525 port = atoi(optarg);
526 break;
527 case 'g':
528 group_id = atoi(optarg);
529 break;
530 case 'w':
531 write_cache = 1;
532 break;
533 case 'm':
534 mem_check = 1;
535 break;
536 case 'h':
537 default:
538 usage(argv[0]);
543 try {
544 log_file log("/dev/stderr", DNET_LOG_DEBUG);
546 node n(log);
547 session s(n);
548 s.add_groups(groups);
550 try {
551 n.add_remote(host, port, AF_INET);
552 } catch (...) {
553 throw std::runtime_error("Could not add remote nodes, exiting");
556 test_lookup(s, groups);
558 s.stat_log();
560 column_test(s);
562 test_prepare_commit(s, 0, 0);
563 test_prepare_commit(s, 1, 0);
564 test_prepare_commit(s, 0, 1);
565 test_prepare_commit(s, 1, 1);
567 test_range_request(s, 0, 0, 0, group_id);
568 test_range_request(s, 0, 0, DNET_ATTR_SORT, group_id);
569 test_range_request(s, 1, 0, 0, group_id);
570 test_range_request(s, 0, 1, 0, group_id);
572 test_append(s);
574 test_bulk_write(s);
575 test_bulk_read(s);
577 if (mem_check)
578 memory_test(s);
580 if (write_cache)
581 test_cache_write(s, 1000);
583 test_cache_read(s, 1000);
584 test_cache_delete(s, 1000);
585 test_cache_write(s, 1000);
587 } catch (const std::exception &e) {
588 std::cerr << "Error occured : " << e.what() << std::endl;
589 } catch (int err) {
590 std::cerr << "Error : " << err << std::endl;