diff --git a/.gitignore b/.gitignore index 8b46e52ae05fa635144698467ef09e615c6daae3..1cf4bbec883150c1ca80778f7faa17d4c52c7db0 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,7 @@ doc/user/tarantool_user_guide.txt doc/tnt.ent doc/www-data/*.html doc/www-data/*.ru.html -doc/www-data.in/download +doc/www-data.in/_text/download.md extra/rpm.spec src/trivia/config.h install_manifest.txt diff --git a/client/tarantool/print.c b/client/tarantool/print.c index e6ace8a2a9429b900fad0e5d7ff69534de03dbfb..a0042e349cedc6398619032da828bf936e98935a 100644 --- a/client/tarantool/print.c +++ b/client/tarantool/print.c @@ -56,6 +56,8 @@ void tc_printf(char *fmt, ...) { if (stat == -1) tc_error("Can't write into pager - %d", errno); va_end(args); + if (str) + free(str); return; } diff --git a/connector/c/tb b/connector/c/tb index 0d24b62555958ae6d5e0682c25062a9354ba3ae0..dc8c432ac9840a7171301188777c75b5b17d3a02 160000 --- a/connector/c/tb +++ b/connector/c/tb @@ -1 +1 @@ -Subproject commit 0d24b62555958ae6d5e0682c25062a9354ba3ae0 +Subproject commit dc8c432ac9840a7171301188777c75b5b17d3a02 diff --git a/doc/www-data.in/_layout/base b/doc/www-data.in/_layout/base index 65114ca414e1f0192e94d8a2c114c782c9e602ba..7271eed61a049113a868a2bce912a5ad3848dfc7 100644 --- a/doc/www-data.in/_layout/base +++ b/doc/www-data.in/_layout/base @@ -17,7 +17,7 @@ <h2>An in-memory NoSQL database</h2> <h3> <a href="intro.html" class="intro">Overview</a> - <a href="doc/site-gen/mpage/index.html" class="documentation">Documentation</a> + <a href="doc/stable/mpage/index.html" class="documentation">Documentation</a> <a href="download.html" class="download">Download</a> <a href="support.html" class="support">Support</a> </h3> diff --git a/src/box/box.cc b/src/box/box.cc index 0be2b85f414075c431e311a9f583761bba2d44de..6a00a8572dff9ebe814fe456d14eee1415a42382 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -28,6 +28,7 @@ */ #include "box/box.h" #include <arpa/inet.h> +#include <sys/wait.h> extern "C" { #include <cfg/warning.h> diff --git a/src/box/tree_index.cc b/src/box/tree_index.cc index 540fc3cb426cf3d81b1a821a16f9b27ff3e6748f..c35126e752f40cbe68126d7af5dfdee7befcaa52 100644 --- a/src/box/tree_index.cc +++ b/src/box/tree_index.cc @@ -30,6 +30,7 @@ #include "tuple.h" #include "space.h" #include "exception.h" +#include "errinj.h" /* {{{ Utilities. *************************************************/ @@ -80,6 +81,16 @@ sptree_index_node_compare_with_key(const void *key, const void *node, key_data->part_count, key_def); } +#ifndef NDEBUG +void * +realloc_inject(void *ptr, size_t size) +{ + if (size) + ERROR_INJECT(ERRINJ_TREE_ALLOC, return 0); + return realloc(ptr, size); +} +#endif + /* {{{ TreeIndex Iterators ****************************************/ struct tree_iterator { @@ -252,7 +263,12 @@ TreeIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, void *p_dup_node = &dup_tuple; /* Try to optimistically replace the new_tuple. */ + int tree_res = sptree_index_replace(&tree, &new_tuple, &p_dup_node); + if (tree_res) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, tree_res, + "TreeIndex", "replace"); + } errcode = replace_check_dup(old_tuple, dup_tuple, mode); @@ -306,12 +322,19 @@ TreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, it->key_data.key = key; it->key_data.part_count = part_count; - if (iterator_type_is_reverse(type)) - sptree_index_iterator_reverse_init_set(&tree, &it->iter, - &it->key_data); - else - sptree_index_iterator_init_set(&tree, &it->iter, - &it->key_data); + if (iterator_type_is_reverse(type)) { + int r = sptree_index_iterator_reverse_init_set(&tree, + &it->iter, &it->key_data); + if (r) + tnt_raise(ClientError, ER_MEMORY_ISSUE, + r, "TreeIndex", "init iterator"); + } else { + int r = sptree_index_iterator_init_set(&tree, + &it->iter, &it->key_data); + if (r) + tnt_raise(ClientError, ER_MEMORY_ISSUE, + r, "TreeIndex", "init iterator"); + } switch (type) { case ITER_EQ: @@ -386,11 +409,15 @@ TreeIndex::endBuild() void *nodes = tree.members; /* If n_tuples == 0 then estimated_tuples = 0, elem == NULL, tree is empty */ + int tree_res = sptree_index_init(&tree, sizeof(struct tuple *), nodes, n_tuples, estimated_tuples, sptree_index_node_compare_with_key, key_def->is_unique ? sptree_index_node_compare : sptree_index_node_compare_dup, key_def); + if (tree_res) { + panic("tree_init: failed to allocate %d bytes", tree_res); + } } diff --git a/src/box/tree_index.h b/src/box/tree_index.h index 27ba9fbadd8f70ad9e97fed0ae6af0a5c9278e44..f4fd565ba12df5fabc1d041fde4fda8f4d746a5a 100644 --- a/src/box/tree_index.h +++ b/src/box/tree_index.h @@ -37,7 +37,13 @@ /** * Instantiate sptree definitions */ +#ifdef NDEBUG SPTREE_DEF(index, realloc, qsort_arg); +#else +void * +realloc_inject(void *ptr, size_t size); +SPTREE_DEF(index, realloc_inject, qsort_arg); +#endif class TreeIndex: public Index { public: diff --git a/src/errinj.h b/src/errinj.h index 11f6f81abd919f6f7712a1efe91e03b1f8b14d5a..ef38719065ef624e806eb107941129159df9a574 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -43,7 +43,8 @@ struct errinj { _(ERRINJ_TESTING, false) \ _(ERRINJ_WAL_IO, false) \ _(ERRINJ_WAL_ROTATE, false) \ - _(ERRINJ_INDEX_ALLOC, false) + _(ERRINJ_INDEX_ALLOC, false) \ + _(ERRINJ_TREE_ALLOC, false) ENUM0(errinj_enum, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg index 5d669baffafffb071a400d85ee2878343f10e938..55b49bebeba6d88ac4c0ea75854942a77412ce7f 100644 --- a/test/big/tarantool.cfg +++ b/test/big/tarantool.cfg @@ -9,3 +9,4 @@ secondary_port = 33014 admin_port = 33015 rows_per_wal = 50 + diff --git a/test/box/errinj.result b/test/box/errinj.result index b36bbd2b0ef151053542a9d71ef693bfe0e87702..4b92f5868a0cd3c510d89cac6f36e8e7adce78b8 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -6,13 +6,15 @@ space:create_index('primary', 'hash', { parts = { 0, 'num' }}) ... box.errinj.info() --- -- ERRINJ_INDEX_ALLOC: +- ERRINJ_WAL_IO: state: false - ERRINJ_WAL_IO: + ERRINJ_TESTING: + state: false + ERRINJ_INDEX_ALLOC: state: false ERRINJ_WAL_ROTATE: state: false - ERRINJ_TESTING: + ERRINJ_TREE_ALLOC: state: false ... box.errinj.set("some-injection", true) diff --git a/test/box/errinj_index.result b/test/box/errinj_index.result new file mode 100644 index 0000000000000000000000000000000000000000..71e489e95724b55dd47dc8ea3771569cb173eee7 --- /dev/null +++ b/test/box/errinj_index.result @@ -0,0 +1,238 @@ +s = box.schema.create_space('tweedledum') +--- +... +s:create_index('primary', 'tree', { parts = { 0, 'num' }}) +--- +... +-- Check a failed realloc in tree. +for i = 1,10 do s:insert(i, i, 'test' .. i) end +--- +... +res = {} +--- +... +for i = 1,10 do table.insert(res, s:select(0, i)) end +--- +... +res +--- +- - [1, 1, 'test1'] + - [2, 2, 'test2'] + - [3, 3, 'test3'] + - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [8, 8, 'test8'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +res = {} +--- +... +for t in s.index[0]:iterator(box.index.ALL) do table.insert(res, t) end +--- +... +res +--- +- - [1, 1, 'test1'] + - [2, 2, 'test2'] + - [3, 3, 'test3'] + - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [8, 8, 'test8'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +box.errinj.set("ERRINJ_TREE_ALLOC", true) +--- +- ok +... +res = {} +--- +... +for i = 1,10 do table.insert(res, s:select(0, i)) end +--- +... +res +--- +- - [1, 1, 'test1'] + - [2, 2, 'test2'] + - [3, 3, 'test3'] + - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [8, 8, 'test8'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +for i = 501,1000 do s:insert(i, i) end +--- +- error: Failed to allocate 1024 bytes in TreeIndex for replace +... +s:delete(1) +--- +- [1, 1, 'test1'] +... +res = {} +--- +... +for t in s.index[0]:iterator(box.index.ALL) do table.insert(res, t) end +--- +- error: Failed to allocate 196 bytes in TreeIndex for init iterator +... +res +--- +- [] +... +-- reserve memory for iterator in index. last insert may increase tree depth +box.errinj.set("ERRINJ_TREE_ALLOC", false) +--- +- ok +... +s:select(0, 1) +--- +... +box.errinj.set("ERRINJ_TREE_ALLOC", true) +--- +- ok +... +res = {} +--- +... +for i = 1,10 do table.insert(res, (s:select(0, i))) end +--- +... +res +--- +- - [2, 2, 'test2'] + - [3, 3, 'test3'] + - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [8, 8, 'test8'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +for i = 1001,1500 do s:insert(i, i) end +--- +- error: Failed to allocate 1024 bytes in TreeIndex for replace +... +s:delete(2) +--- +- [2, 2, 'test2'] +... +s.index[0]:iterator(box.index.ALL) +--- +- error: Failed to allocate 200 bytes in TreeIndex for init iterator +... +-- reserve memory for iterator in index. last insert may increase tree depth +-- (if rebalance was not initiated) +box.errinj.set("ERRINJ_TREE_ALLOC", false) +--- +- ok +... +s:select(0, 1) +--- +... +box.errinj.set("ERRINJ_TREE_ALLOC", true) +--- +- ok +... +res = {} +--- +... +for i = 1,10 do table.insert(res, (s:select(0, i))) end +--- +... +res +--- +- - [3, 3, 'test3'] + - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [8, 8, 'test8'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +for i = 1501,2000 do s:insert(i, i) end +--- +- error: Failed to allocate 1024 bytes in TreeIndex for replace +... +s:delete(3) +--- +- [3, 3, 'test3'] +... +s.index[0]:iterator(box.index.ALL) +--- +- error: Failed to allocate 200 bytes in TreeIndex for init iterator +... +box.errinj.set("ERRINJ_TREE_ALLOC", false) +--- +- ok +... +for i = 2001,2500 do s:insert(i, i) end +--- +... +res = {} +--- +... +for i = 1,10 do table.insert(res, (s:select(0, i))) end +--- +... +res +--- +- - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [8, 8, 'test8'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +s:delete(8) +--- +- [8, 8, 'test8'] +... +res = {} +--- +... +for i = 1,10 do table.insert(res, (s:select(0, i))) end +--- +... +res +--- +- - [4, 4, 'test4'] + - [5, 5, 'test5'] + - [6, 6, 'test6'] + - [7, 7, 'test7'] + - [9, 9, 'test9'] + - [10, 10, 'test10'] +... +res = {} +--- +... +for i = 2001,2010 do table.insert(res, (s:select(0, i))) end +--- +... +res +--- +- - [2001, 2001] + - [2002, 2002] + - [2003, 2003] + - [2004, 2004] + - [2005, 2005] + - [2006, 2006] + - [2007, 2007] + - [2008, 2008] + - [2009, 2009] + - [2010, 2010] +... +s:drop() +--- +... diff --git a/test/box/errinj_index.test.lua b/test/box/errinj_index.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..1dcbf22143596bbf461ad4d0492b7088692e8cf5 --- /dev/null +++ b/test/box/errinj_index.test.lua @@ -0,0 +1,65 @@ +s = box.schema.create_space('tweedledum') +s:create_index('primary', 'tree', { parts = { 0, 'num' }}) + +-- Check a failed realloc in tree. + +for i = 1,10 do s:insert(i, i, 'test' .. i) end +res = {} +for i = 1,10 do table.insert(res, s:select(0, i)) end +res +res = {} +for t in s.index[0]:iterator(box.index.ALL) do table.insert(res, t) end +res + +box.errinj.set("ERRINJ_TREE_ALLOC", true) + +res = {} +for i = 1,10 do table.insert(res, s:select(0, i)) end +res +for i = 501,1000 do s:insert(i, i) end +s:delete(1) +res = {} +for t in s.index[0]:iterator(box.index.ALL) do table.insert(res, t) end +res + +-- reserve memory for iterator in index. last insert may increase tree depth +box.errinj.set("ERRINJ_TREE_ALLOC", false) +s:select(0, 1) +box.errinj.set("ERRINJ_TREE_ALLOC", true) + +res = {} +for i = 1,10 do table.insert(res, (s:select(0, i))) end +res + +for i = 1001,1500 do s:insert(i, i) end +s:delete(2) +s.index[0]:iterator(box.index.ALL) + +-- reserve memory for iterator in index. last insert may increase tree depth +-- (if rebalance was not initiated) +box.errinj.set("ERRINJ_TREE_ALLOC", false) +s:select(0, 1) +box.errinj.set("ERRINJ_TREE_ALLOC", true) + +res = {} +for i = 1,10 do table.insert(res, (s:select(0, i))) end +res +for i = 1501,2000 do s:insert(i, i) end +s:delete(3) +s.index[0]:iterator(box.index.ALL) + +box.errinj.set("ERRINJ_TREE_ALLOC", false) + +for i = 2001,2500 do s:insert(i, i) end +res = {} +for i = 1,10 do table.insert(res, (s:select(0, i))) end +res +s:delete(8) +res = {} +for i = 1,10 do table.insert(res, (s:select(0, i))) end +res +res = {} +for i = 2001,2010 do table.insert(res, (s:select(0, i))) end +res +s:drop() + diff --git a/test/box/suite.ini b/test/box/suite.ini index d6dd29319553ca75cac479eae900cd137adfdab8..583d298659af9e017136f1ab623fc9b05dfdcda3 100644 --- a/test/box/suite.ini +++ b/test/box/suite.ini @@ -4,4 +4,4 @@ description = tarantool/box, minimal configuration config = tarantool.cfg disabled = snapshot_1_3.test.py session.test.py valgrind_disabled = admin_coredump.test.lua -release_disabled = errinj.test.lua +release_disabled = errinj.test.lua errinj_index.test.lua diff --git a/test/box/xlog.test.py b/test/box/xlog.test.py index 09e0857698e178217f864b26d13991055e0b6fa4..3e3e2b4e9b11fabd636430dfe3b260261c91aa4b 100644 --- a/test/box/xlog.test.py +++ b/test/box/xlog.test.py @@ -126,5 +126,3 @@ admin("#box.space[0]") # cleanup server.stop() server.deploy() - -# vim: syntax=python diff --git a/test/lib/colorer.py b/test/lib/colorer.py index 642199eb43f0c1abfaa60577fa25b34223f6f6c5..2edfeb2cf2366defde29b22beae3ebe39706e81c 100644 --- a/test/lib/colorer.py +++ b/test/lib/colorer.py @@ -8,6 +8,69 @@ class Singleton(type): cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] +class CSchema(object): + objects = {} + + def __init__(self): + self.main_objects = { + 'diff_mark': {}, + 'diff_in': {}, + 'diff_out': {}, + 'test_pass': {}, + 'test_fail': {}, + 'test_new': {}, + 'test_skip': {}, + 'test_disa': {}, + 'error': {}, + 'lerror': {}, + 'tail': {}, + 'ts_text': {}, + 'path': {}, + 'info': {}, + 'separator': {}, + 't_name': {}, + 'serv_text': {}, + 'version': {}, + 'tr_text': {}, + } + self.main_objects.update(self.objects) + +class SchemaAscetic(CSchema): + objects = { + 'diff_mark': {'fgcolor': 'magenta'}, + 'diff_in': {'fgcolor': 'green'}, + 'diff_out': {'fgcolor': 'red'}, + 'test_pass': {'fgcolor': 'green'}, + 'test_fail': {'fgcolor': 'red'}, + 'test_new': {'fgcolor': 'lblue'}, + 'test_skip': {'fgcolor': 'grey'}, + 'test_disa': {'fgcolor': 'grey'}, + 'error': {'fgcolor': 'red'}, + } + +class SchemaPretty(CSchema): + objects = { + 'diff_mark': {'fgcolor': 'magenta'}, + 'diff_in': {'fgcolor': 'blue'}, + 'diff_out': {'fgcolor': 'red'}, + 'test_pass': {'fgcolor': 'green'}, + 'test_fail': {'fgcolor': 'red'}, + 'test_new': {'fgcolor': 'lblue'}, + 'test_skip': {'fgcolor': 'grey'}, + 'test_disa': {'fgcolor': 'grey'}, + 'error': {'fgcolor': 'red'}, + 'lerror': {'fgcolor': 'lred'}, + 'tail': {'fgcolor': 'lblue'}, + 'ts_text': {'fgcolor': 'lmagenta'}, + 'path': {'fgcolor': 'green', 'bold':True}, + 'info': {'fgcolor': 'yellow', 'bold':True}, + 'separator': {'fgcolor': 'blue'}, + 't_name': {'fgcolor': 'lblue'}, + 'serv_text': {'fgcolor': 'lmagenta'}, + 'version': {'fgcolor': 'yellow', 'bold':True}, + 'tr_text': {'fgcolor': 'green'}, + } + class Colorer(object): """ Colorer/Styler based on VT220+ specifications (Not full). Based on: @@ -32,7 +95,7 @@ class Colorer(object): "lmagenta" : '1;35', "lcyan" : '1;36', "white" : '1;37', - } + } bgcolor = { "black" : '0;40', "red" : '0;41', @@ -66,6 +129,15 @@ class Colorer(object): self.stdout = sys.stdout self.is_term = self.stdout.isatty() self.colors = int(os.popen('tput colors').read()) if self.is_term else None + print os.getenv('TT_SCHEMA') + schema = os.getenv('TT_SCHEMA', 'ascetic') + if schema == 'ascetic': + self.schema = SchemaAscetic() + elif schema == 'pretty': + self.schema = SchemaPretty() + else: + self.schema = CSchema() + self.schema = self.schema.main_objects def set_stdout(self): sys.stdout = self @@ -75,6 +147,8 @@ class Colorer(object): def write(self, *args, **kwargs): flags = [] + if 'schema' in kwargs: + kwargs.update(self.schema[kwargs['schema']]) for i in self.attributes: if i in kwargs and kwargs[i] == True: flags.append(self.attributes[i]) @@ -95,11 +169,11 @@ class Colorer(object): def writeout_unidiff(self, diff): for i in diff: if i.startswith('+'): - self.write(i, fgcolor='blue') + self.write(i, schema='diff_in') elif i.startswith('-'): - self.write(i, fgcolor='red') + self.write(i, schema='diff_out') elif i.startswith('@'): - self.write(i, fgcolor='magenta') + self.write(i, schema='diff_mark') def flush(self): return self.stdout.flush() diff --git a/test/lib/tarantool_server.py b/test/lib/tarantool_server.py index b385a6894f4d1aaef24f4e5750bec19e81cac2a3..c331e62afc7c3840143ff7e4c01feb14bd3a357a 100644 --- a/test/lib/tarantool_server.py +++ b/test/lib/tarantool_server.py @@ -165,8 +165,8 @@ class TarantoolServer(Server): builddir = os.path.join(builddir, "src/box") path = builddir + os.pathsep + os.environ["PATH"] if not silent: - color_stdout("Looking for server binary in ", fgcolor='lmagenta') - color_stdout(path+" ...\n", fgcolor='green') + color_stdout("Looking for server binary in ", schema='serv_text') + color_stdout(path+" ...\n", schema='path') for _dir in path.split(os.pathsep): exe = os.path.join(_dir, self.default_bin_name) if os.access(exe, os.X_OK): @@ -186,15 +186,15 @@ class TarantoolServer(Server): self.valgrind_log = os.path.abspath(os.path.join(self.vardir, self.valgrind_log)) if not silent: - color_stdout("Installing the server ...\n", fgcolor='lmagenta') - color_stdout(" Found executable at ", fgcolor='lmagenta') - color_stdout(self.binary+'\n', fgcolor='green', bold=True) - color_stdout(" Creating and populating working directory in ", fgcolor='lmagenta') - color_stdout(self.vardir+' ...\n', fgcolor='green', bold=True) + color_stdout("Installing the server ...\n", schema='serv_text') + color_stdout(" Found executable at ", schema='serv_text') + color_stdout(self.binary+'\n', schema='path') + color_stdout(" Creating and populating working directory in ", schema='serv_text') + color_stdout(self.vardir+' ...\n', schema='path') if os.access(self.vardir, os.F_OK): if not silent: - color_stdout(" Found old vardir, deleting ...\n", fgcolor='lmagenta') + color_stdout(" Found old vardir, deleting ...\n", schema='serv_text') self.kill_old_server() self.cleanup() else: @@ -257,7 +257,7 @@ class TarantoolServer(Server): stderr = subprocess.STDOUT, stdout = subprocess.PIPE) retcode = _init.wait() if retcode: - sys.stderr.write("tarantool_box --init-storage error: \n%s\n" % _init.stdout.read()) + color_stdout("tarantool_box --init-storage error: \n%s\n" % _init.stdout.read(), schema='error') raise subprocess.CalledProcessError(retcode, cmd) def get_param(self, param): @@ -305,23 +305,23 @@ class TarantoolServer(Server): if self.is_started: if not silent: - color_stdout("The server is already started.\n", fgcolor='lred') + color_stdout("The server is already started.\n", schema='lerror') return if not silent: - color_stdout("Starting the server ...\n", fgcolor='lmagenta') + color_stdout("Starting the server ...\n", schema='serv_text') version = self.version() - color_stdout("Starting ", fgcolor='lmagenta') - color_stdout(os.path.basename(self.binary), " \n", fgcolor='green') - color_stdout(version, "\n", fgcolor='grey') + color_stdout("Starting ", schema='serv_text') + color_stdout(os.path.basename(self.binary), " \n", schema='path') + color_stdout(version, "\n", schema='version') check_port(self.port) args = self.prepare_args() if self.gdb: args = prepare_gdb(self.binary, args) - color_stdout("You started the server in gdb mode.\n", fgcolor='yellow', bold=True) - color_stdout("To attach, use `screen -r tnt-gdb`\n", fgcolor='yellow', bold=True) + color_stdout("You started the server in gdb mode.\n", schema='info') + color_stdout("To attach, use `screen -r tnt-gdb`\n", schema='info') elif self.valgrind: args = prepare_valgrind([self.binary] + args, self.valgrind_log, os.path.abspath(os.path.join(self.vardir, @@ -347,11 +347,11 @@ class TarantoolServer(Server): start up.""" if not self.is_started: if not silent: - color_stdout("The server is not started.\n", fgcolor='red') + color_stdout("The server is not started.\n", schema='lerror') return if not silent: - color_stdout("Stopping the server ...\n", fgcolor='lmagenta') + color_stdout("Stopping the server ...\n", schema='serv_text') if self.process == None: self.kill_old_server() @@ -429,7 +429,7 @@ class TarantoolServer(Server): return # Nothing to do if not silent: - color_stdout(" Found old server, pid {0}, killing ...".format(pid), fgcolor='yellow') + color_stdout(" Found old server, pid {0}, killing ...".format(pid), schema='info') try: os.kill(pid, signal.SIGTERM) @@ -452,10 +452,10 @@ class TarantoolServer(Server): return pid def print_log(self, lines): - color_stdout.write("\nLast {0} lines of Tarantool Log file:\n".format(lines), fgcolor='lred') + color_stdout("\nLast {0} lines of Tarantool Log file:\n".format(lines), schema='error') with open(os.path.join(self.vardir, 'tarantool.log'), 'r') as log: for i in log.readlines()[-lines:]: - color_stdout.write(i, fgcolor='grey') + color_stdout(i, schema='log') def wait_until_started(self): """Wait until the server is started and accepting connections""" diff --git a/test/lib/test_suite.py b/test/lib/test_suite.py index 0ac0133b6290fd985c1d38d569b62165e18aa840..c00008e89717d1dc4fc7cef41d15b69db967972e 100644 --- a/test/lib/test_suite.py +++ b/test/lib/test_suite.py @@ -35,7 +35,7 @@ def check_libs(): try: __import__(mod_name) except ImportError: - sys.stderr.write("\n\nNo %s library found\n" % mod_name) + color_stdout("\n\nNo %s library found\n" % mod_name, schema='error') sys.exit(1) @@ -89,7 +89,7 @@ def print_tail_n(filename, num_lines): with open(filename, "r+") as logfile: tail_n = collections.deque(logfile, num_lines) for line in tail_n: - color_stdout.write(line, fgcolor='lblue') + color_stdout(line, schema='tail') class Test: @@ -174,20 +174,20 @@ class Test: check_valgrind_log(server.valgrind_log) == False elif self.skip: - color_stdout("[ skip ]\n", fgcolor='grey') + color_stdout("[ skip ]\n", schema='test_skip') if os.path.exists(self.tmp_result): os.remove(self.tmp_result) elif self.is_executed_ok and self.is_equal_result and self.is_valgrind_clean: - color_stdout("[ pass ]\n", fgcolor='green') + color_stdout("[ pass ]\n", schema='test_pass') if os.path.exists(self.tmp_result): os.remove(self.tmp_result) elif (self.is_executed_ok and not self.is_equal_result and not os.path.isfile(self.result)): os.rename(self.tmp_result, self.result) - color_stdout("[ NEW ]\n", fgcolor='lblue') + color_stdout("[ new ]\n", schema='test_new') else: os.rename(self.tmp_result, self.reject) - color_stdout("[ FAIL ]\n" if not self.is_terminated else "[ TERMINATED ]\n", fgcolor='red') + color_stdout("[ fail ]\n", schema='test_fail') where = "" if not self.is_executed_ok: @@ -210,7 +210,7 @@ class Test: """Print 10 lines of client program output leading to test failure. Used to diagnose a failure of the client program""" - color_stdout(message, color='lred') + color_stdout(message, schema='error') print_tail_n(logfile, 10) def print_unidiff(self): @@ -218,7 +218,7 @@ class Test: to establish the cause of a failure when .test differs from .result.""" - color_stdout("\nTest failed! Result content mismatch:\n", fgcolor='lred') + color_stdout("\nTest failed! Result content mismatch:\n", schema='error') with open(self.result, "r") as result: with open(self.reject, "r") as reject: result_time = time.ctime(os.stat(self.result).st_mtime) @@ -282,11 +282,11 @@ class TestSuite: raise RuntimeError("Unknown server: core = {0}".format( self.ini["core"])) - color_stdout("Collecting tests in ", fgcolor='lmagenta') - color_stdout(repr(suite_path), fgcolor='green') - color_stdout(": ", self.ini["description"], ".\n", fgcolor='lmagenta') + color_stdout("Collecting tests in ", schema='ts_text') + color_stdout(repr(suite_path), schema='path') + color_stdout(": ", self.ini["description"], ".\n", schema='ts_text') self.server.find_tests(self, suite_path) - color_stdout("Found ", str(len(self.tests)), " tests.\n", fgcolor='green') + color_stdout("Found ", str(len(self.tests)), " tests.\n", schema='path') def run_all(self): """For each file in the test suite, run client program @@ -310,19 +310,19 @@ class TestSuite: shutil.copy(i, self.args.vardir) if self.args.start_and_exit: - color_stdout(" Start and exit requested, exiting...\n", fgcolor='yellow') + color_stdout(" Start and exit requested, exiting...\n", schema='info') exit(0) longsep = '='*70 shortsep = '-'*60 - color_stdout(longsep, "\n", fgcolor='blue') - color_stdout("TEST".ljust(48), fgcolor='lblue') - color_stdout("RESULT\n", fgcolor='green') - color_stdout(shortsep, "\n", fgcolor='blue') + color_stdout(longsep, "\n", schema='separator') + color_stdout("TEST".ljust(48), schema='t_name') + color_stdout("RESULT\n", schema='test_pass') + color_stdout(shortsep, "\n", schema='separator') failed_tests = [] try: for test in self.tests: - color_stdout(test.name.ljust(48), fgcolor='lblue') + color_stdout(test.name.ljust(48), schema='t_name') # for better diagnostics in case of a long-running test test_name = os.path.basename(test.name) @@ -330,7 +330,7 @@ class TestSuite: if (test_name in self.ini["disabled"] or not self.server.debug and test_name in self.ini["release_disabled"] or self.args.valgrind and test_name in self.ini["valgrind_disabled"]): - color_stdout("[ disabled ]\n", fgcolor='grey') + color_stdout("[ disabled ]\n", schema='t_name') else: test.run(self.server) if not test.passed(): @@ -339,17 +339,17 @@ class TestSuite: color_stdout('\n') raise finally: - color_stdout(shortsep, "\n", fgcolor='blue') + color_stdout(shortsep, "\n", schema='separator') self.server.stop(silent=False) self.server.cleanup() if failed_tests: color_stdout("Failed {0} tests: {1}.".format(len(failed_tests), ", ".join(failed_tests)), - fgcolor='red') + schema='error') if self.args.valgrind and check_valgrind_log(self.server.valgrind_log): - color_stdout(" Error! There were warnings/errors in valgrind log file:", fgcolor='red') + color_stdout(" Error! There were warnings/errors in valgrind log file:", schema='error') print_tail_n(self.server.valgrind_log, 20) return ['valgrind error in ' + self.suite_path] return failed_tests diff --git a/test/test-run.py b/test/test-run.py index 9e8f795680f90db480f4c47cff3b33279ab8df1c..ac780e79223ff9e20dd68baf3ec6b2f6dfd7d526 100755 --- a/test/test-run.py +++ b/test/test-run.py @@ -132,7 +132,7 @@ class Options: """Check the arguments for correctness.""" check_error = False if self.args.gdb and self.args.valgrind: - color_stdout("Error: option --gdb is not compatible with option --valgrind", fgcolor='red') + color_stdout("Error: option --gdb is not compatible with option --valgrind", schema='error') check_error = True if check_error: exit(-1) @@ -169,7 +169,7 @@ def main(): failed_tests = [] try: - color_stdout("Started {}\n".format(" ".join(sys.argv)), fgcolor='green') + color_stdout("Started {}\n".format(" ".join(sys.argv)), schema='tr_text') suite_names = [] if options.args.suites != []: suite_names = options.args.suites @@ -183,7 +183,7 @@ def main(): for suite in suites: failed_tests.extend(suite.run_all()) except RuntimeError as e: - color_stdout("\nFatal error: {0}. Execution aborted.\n".format(e), fgcolor='red') + color_stdout("\nFatal error: {0}. Execution aborted.\n".format(e), schema='error') if options.args.gdb: time.sleep(100) return (-1) @@ -191,9 +191,9 @@ def main(): os.chdir(oldcwd) if failed_tests and options.args.is_force: - color_stdout("\n===== {0} tests failed:".format(len(failed_tests))+"\n", fgcolor="red") + color_stdout("\n===== {0} tests failed:".format(len(failed_tests))+"\n", schema='error') for test in failed_tests: - color_stdout("----- "+test+"\n", fgcolor="yellow") + color_stdout("----- "+test+"\n", schema='info') return (-1 if failed_tests else 0) diff --git a/third_party/sptree.h b/third_party/sptree.h index a22e2a41d0dbfd2cbdecb0b27a3fd09c9ed2fb6e..6b109be4bbb41bc93337f445cfe1d6e0b0c2ca43 100644 --- a/third_party/sptree.h +++ b/third_party/sptree.h @@ -139,7 +139,7 @@ sptree_##name##_mktree(sptree_##name *t, spnode_t depth, spnode_t start, spnode_ return half; \ } \ \ -static inline void \ +static inline int \ sptree_##name##_init(sptree_##name *t, size_t elemsize, void *m, \ spnode_t nm, spnode_t nt, \ sptree_##name##_compare compare, \ @@ -176,13 +176,21 @@ sptree_##name##_init(sptree_##name *t, size_t elemsize, void *m, /* create tree */ \ t->root = sptree_##name##_mktree(t, 1, 0, t->nmember); \ } \ + if (t->members && t->lrpointers) \ + return 0; \ + else if (t->members) \ + return t->ntotal * sizeof(sptree_node_pointers); \ + else if (t->lrpointers) \ + return t->ntotal * elemsize; \ + else \ + return t->ntotal * (sizeof(sptree_node_pointers) + elemsize); \ } \ \ static inline void \ sptree_##name##_destroy(sptree_##name *t) { \ if (t == NULL) return; \ - free(t->members); \ - free(t->lrpointers); \ + t->members = realloc(t->members, 0); \ + t->lrpointers = (sptree_node_pointers *)realloc(t->lrpointers, 0); \ } \ \ /** Nodes in the garbage list have a loop on their right link. */ \ @@ -254,6 +262,25 @@ sptree_##name##_size_of_subtree(sptree_##name *t, spnode_t node) { sptree_##name##_size_of_subtree(t, _GET_SPNODE_RIGHT(node)); \ } \ \ +static inline int \ +sptree_##name##_reserve_places(sptree_##name *t, spnode_t nreserve) { \ + spnode_t num_free = t->ntotal - t->size; \ + if (num_free >= nreserve) \ + return 0; \ + spnode_t new_ntotal = MAX(t->ntotal * 2, t->ntotal + nreserve - num_free); \ + void *new_members = realloc(t->members, new_ntotal * t->elemsize); \ + if (!new_members) \ + return new_ntotal * t->elemsize; \ + t->members = new_members; \ + sptree_node_pointers *new_lrpointers = (sptree_node_pointers *) \ + realloc(t->lrpointers, new_ntotal * sizeof(sptree_node_pointers)); \ + if (!new_lrpointers) \ + return new_ntotal * sizeof(sptree_node_pointers); \ + t->lrpointers = new_lrpointers; \ + t->ntotal = new_ntotal; \ + return 0; \ +} \ + \ static inline spnode_t \ sptree_##name##_get_place(sptree_##name *t) { \ spnode_t node; \ @@ -262,10 +289,11 @@ sptree_##name##_get_place(sptree_##name *t) { t->garbage_head = _GET_SPNODE_LEFT(t->garbage_head); \ } else { \ if (t->nmember >= t->ntotal) { \ - t->ntotal *= 2; \ - t->members = realloc(t->members, t->ntotal * t->elemsize); \ + spnode_t new_ntotal = t->ntotal * 2; \ + t->members = realloc(t->members, new_ntotal * t->elemsize); \ t->lrpointers = (sptree_node_pointers *) realloc(t->lrpointers, \ - t->ntotal * sizeof(sptree_node_pointers)); \ + new_ntotal * sizeof(sptree_node_pointers)); \ + t->ntotal = new_ntotal; \ } \ \ node = t->nmember; \ @@ -323,7 +351,7 @@ sptree_##name##_balance(sptree_##name *t, spnode_t node, spnode_t size) { return z; \ } \ \ -static inline void \ +static inline int \ sptree_##name##_replace(sptree_##name *t, void *v, void **p_old) { \ spnode_t node, depth = 0; \ spnode_t path[ t->max_depth + 2]; \ @@ -338,7 +366,7 @@ sptree_##name##_replace(sptree_##name *t, void *v, void **p_old) { t->size=1; \ if (p_old) \ *p_old = NULL; \ - return; \ + return 0; \ } else { \ spnode_t parent = t->root; \ \ @@ -348,12 +376,16 @@ sptree_##name##_replace(sptree_##name *t, void *v, void **p_old) { if (p_old) \ memcpy(*p_old, ITHELEM(t, parent), t->elemsize); \ memcpy(ITHELEM(t, parent), v, t->elemsize); \ - return; \ + return 0; \ } \ path[depth] = parent; \ depth++; \ if (r>0) { \ if (_GET_SPNODE_RIGHT(parent) == SPNIL) { \ + /* extra element can be needed for current balance implementation*/ \ + int reserve_result = sptree_##name##_reserve_places(t, 2); \ + if (reserve_result) \ + return reserve_result; \ node = sptree_##name##_get_place(t); \ memcpy(ITHELEM(t, node), v, t->elemsize); \ _SET_SPNODE_RIGHT(parent, node); \ @@ -363,6 +395,10 @@ sptree_##name##_replace(sptree_##name *t, void *v, void **p_old) { } \ } else { \ if (_GET_SPNODE_LEFT(parent) == SPNIL) { \ + /* extra element can be needed for current balance implementation*/ \ + int reserve_result = sptree_##name##_reserve_places(t, 2); \ + if (reserve_result) \ + return reserve_result; \ node = sptree_##name##_get_place(t); \ memcpy(ITHELEM(t, node), v, t->elemsize); \ _SET_SPNODE_LEFT(parent, node); \ @@ -410,6 +446,7 @@ sptree_##name##_replace(sptree_##name *t, void *v, void **p_old) { } \ } \ } \ + return 0; \ } \ \ static inline void \ @@ -559,9 +596,11 @@ static inline sptree_##name##_iterator * sptree_##name##_iterator_alloc(sptree_##name *t) { \ sptree_##name##_iterator *i = (sptree_##name##_iterator *) \ realloc(NULL, sizeof(*i) + sizeof(spnode_t) * (t->max_depth + 1)); \ - i->t = t; \ - i->level = 0; \ - i->stack[0] = t->root; \ + if (i) { \ + i->t = t; \ + i->level = 0; \ + i->stack[0] = t->root; \ + } \ return i; \ } \ \ @@ -572,6 +611,8 @@ sptree_##name##_iterator_init(sptree_##name *t) { \ if (t->root == SPNIL) return NULL; \ i = sptree_##name##_iterator_alloc(t); \ + if (!i) \ + return i; \ \ while( (node = _GET_SPNODE_LEFT( i->stack[i->level] )) != SPNIL ) { \ i->level++; \ @@ -581,21 +622,26 @@ sptree_##name##_iterator_init(sptree_##name *t) { return i; \ } \ \ -static inline void \ +static inline int \ sptree_##name##_iterator_init_set(const sptree_##name *t, sptree_##name##_iterator **i, \ void *k) { \ spnode_t node; \ int lastLevelEq = -1, cmp; \ \ - if ((*i) == NULL || t->max_depth > (*i)->max_depth) \ - *i = (sptree_##name##_iterator *) realloc(*i, sizeof(**i) + \ + if ((*i) == NULL || t->max_depth > (*i)->max_depth) { \ + sptree_##name##_iterator *new_i; \ + new_i = (sptree_##name##_iterator *) realloc(*i, sizeof(**i) + \ sizeof(spnode_t) * (t->max_depth + 31)); \ + if (!new_i) \ + return sizeof(**i) + sizeof(spnode_t) * (t->max_depth + 31); \ + *i = new_i; \ + } \ \ (*i)->t = t; \ (*i)->level = -1; \ if (t->root == SPNIL) { \ (*i)->max_depth = 0; /* valgrind points out it's used in the check above ^.*/ \ - return; \ + return 0; \ } \ \ (*i)->max_depth = t->max_depth; \ @@ -621,6 +667,7 @@ sptree_##name##_iterator_init_set(const sptree_##name *t, sptree_##name##_iterat \ if (lastLevelEq >= 0) \ (*i)->level = lastLevelEq; \ + return 0; \ } \ \ static inline sptree_##name##_iterator * \ @@ -630,6 +677,8 @@ sptree_##name##_iterator_reverse_init(sptree_##name *t) { \ if (t->root == SPNIL) return NULL; \ i = sptree_##name##_iterator_alloc(t); \ + if (!i) \ + return i; \ \ while( (node = _GET_SPNODE_RIGHT( i->stack[i->level] )) != SPNIL ) { \ i->level++; \ @@ -639,21 +688,26 @@ sptree_##name##_iterator_reverse_init(sptree_##name *t) { return i; \ } \ \ -static inline void \ +static inline int \ sptree_##name##_iterator_reverse_init_set(const sptree_##name *t, \ sptree_##name##_iterator **i, void *k) { \ spnode_t node; \ int lastLevelEq = -1, cmp; \ \ - if ((*i) == NULL || t->max_depth > (*i)->max_depth) \ - *i = (sptree_##name##_iterator *) realloc(*i, sizeof(**i) + \ - sizeof(spnode_t) * (t->max_depth + 31)); \ + if ((*i) == NULL || t->max_depth > (*i)->max_depth) { \ + sptree_##name##_iterator *new_i; \ + new_i = (sptree_##name##_iterator *) realloc(*i, sizeof(**i) + \ + sizeof(spnode_t) * (t->max_depth + 31)); \ + if (!new_i) \ + return sizeof(**i) + sizeof(spnode_t) * (t->max_depth + 31); \ + *i = new_i; \ + } \ \ (*i)->t = t; \ (*i)->level = -1; \ if (t->root == SPNIL) { \ (*i)->max_depth = 0; \ - return; \ + return 0; \ } \ \ (*i)->max_depth = t->max_depth; \ @@ -679,12 +733,13 @@ sptree_##name##_iterator_reverse_init_set(const sptree_##name *t, \ if (lastLevelEq >= 0) \ (*i)->level = lastLevelEq; \ + return 0; \ } \ \ static inline void \ sptree_##name##_iterator_free(sptree_##name##_iterator *i) { \ if (i == NULL) return; \ - free(i); \ + i = (sptree_##name##_iterator *)realloc(i, 0); \ } \ \ /** \