mirror of
https://github.com/isar/libmdbx.git
synced 2025-11-22 04:08:58 +08:00
mdbx: subdirs.
Change-Id: Iea70b29ed39f55ee363729300f6ce54127b4e880
This commit is contained in:
156
src/barriers.h
Normal file
156
src/barriers.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Properly compiler/memory/coherence barriers
|
||||
* in the most portable way for libmdbx project.
|
||||
*
|
||||
* Feedback and comments are welcome.
|
||||
* https://gist.github.com/leo-yuriev/ba186a6bf5cf3a27bae7 */
|
||||
|
||||
#pragma once
|
||||
/* *INDENT-OFF* */
|
||||
/* clang-format off */
|
||||
|
||||
#if defined(__mips) && defined(__linux)
|
||||
/* Only MIPS has explicit cache control */
|
||||
# include <asm/cachectl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define MDBX_INLINE __inline
|
||||
#elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */
|
||||
# include <intrin.h>
|
||||
# if defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
# pragma intrinsic(__mf)
|
||||
# elif defined(__i386__) || defined(__x86_64__)
|
||||
# pragma intrinsic(_mm_mfence)
|
||||
# endif
|
||||
# define MDBX_INLINE __inline
|
||||
#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
# include <mbarrier.h>
|
||||
# define MDBX_INLINE inline
|
||||
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) \
|
||||
&& (defined(HP_IA64) || defined(__ia64))
|
||||
# include <machine/sys/inline.h>
|
||||
# define MDBX_INLINE
|
||||
#elif defined(__IBMC__) && defined(__powerpc)
|
||||
# include <atomic.h>
|
||||
# define MDBX_INLINE
|
||||
#elif defined(_AIX)
|
||||
# include <builtins.h>
|
||||
# include <sys/atomic_op.h>
|
||||
# define MDBX_INLINE
|
||||
#elif (defined(__osf__) && defined(__DECC)) || defined(__alpha)
|
||||
# include <machine/builtins.h>
|
||||
# include <c_asm.h>
|
||||
# define MDBX_INLINE
|
||||
#elif defined(__MWERKS__)
|
||||
/* CodeWarrior - troubles ? */
|
||||
# pragma gcc_extensions
|
||||
# define MDBX_INLINE
|
||||
#elif defined(__SNC__)
|
||||
/* Sony PS3 - troubles ? */
|
||||
# define MDBX_INLINE
|
||||
#else
|
||||
# define MDBX_INLINE
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) \
|
||||
|| defined(_M_AMD64) || defined(_M_IX86) \
|
||||
|| defined(__i386) || defined(__amd64) \
|
||||
|| defined(i386) || defined(__x86_64) \
|
||||
|| defined(_AMD64_) || defined(_M_X64)
|
||||
# define MDB_CACHE_IS_COHERENT 1
|
||||
#elif defined(__hppa) || defined(__hppa__)
|
||||
# define MDB_CACHE_IS_COHERENT 1
|
||||
#endif
|
||||
|
||||
#ifndef MDB_CACHE_IS_COHERENT
|
||||
# define MDB_CACHE_IS_COHERENT 0
|
||||
#endif
|
||||
|
||||
#define MDBX_BARRIER_COMPILER 0
|
||||
#define MDBX_BARRIER_MEMORY 1
|
||||
|
||||
static MDBX_INLINE void mdbx_barrier(int type) {
|
||||
#if defined(__clang__)
|
||||
__asm__ __volatile__ ("" ::: "memory");
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
# if __has_extension(c_atomic) || __has_extension(cxx_atomic)
|
||||
__c11_atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
# else
|
||||
__sync_synchronize();
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
__asm__ __volatile__ ("" ::: "memory");
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
# if defined(__ATOMIC_SEQ_CST)
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
# else
|
||||
__sync_synchronize();
|
||||
# endif
|
||||
#elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */
|
||||
__memory_barrier();
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
# if defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
__mf();
|
||||
# elif defined(__i386__) || defined(__x86_64__)
|
||||
_mm_mfence();
|
||||
# else
|
||||
# error "Unknown target for Intel Compiler, please report to us."
|
||||
# endif
|
||||
#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
__compiler_barrier();
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
__machine_rw_barrier();
|
||||
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) \
|
||||
&& (defined(HP_IA64) || defined(__ia64))
|
||||
_Asm_sched_fence(/* LY: no-arg meaning 'all expect ALU', e.g. 0x3D3D */);
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
_Asm_mf();
|
||||
#elif defined(_AIX) || defined(__ppc__) || defined(__powerpc__) \
|
||||
|| defined(__ppc64__) || defined(__powerpc64__)
|
||||
__fence();
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
__lwsync();
|
||||
#elif (defined(__osf__) && defined(__DECC)) || defined(__alpha)
|
||||
__PAL_DRAINA(); /* LY: excessive ? */
|
||||
__MB();
|
||||
#else
|
||||
# error "Could not guess the kind of compiler, please report to us."
|
||||
#endif
|
||||
}
|
||||
|
||||
#define mdbx_compiler_barrier() \
|
||||
mdbx_barrier(MDBX_BARRIER_COMPILER)
|
||||
#define mdbx_memory_barrier() \
|
||||
mdbx_barrier(MDBX_BARRIER_MEMORY)
|
||||
#define mdbx_coherent_barrier() \
|
||||
mdbx_barrier(MDB_CACHE_IS_COHERENT ? MDBX_BARRIER_COMPILER : MDBX_BARRIER_MEMORY)
|
||||
|
||||
static MDBX_INLINE void mdbx_invalidate_cache(void *addr, int nbytes) {
|
||||
mdbx_coherent_barrier();
|
||||
#if defined(__mips) && defined(__linux)
|
||||
/* MIPS has cache coherency issues.
|
||||
* Note: for any nbytes >= on-chip cache size, entire is flushed. */
|
||||
cacheflush(addr, nbytes, DCACHE);
|
||||
#elif defined(_M_MRX000) || defined(_MIPS_)
|
||||
# error "Sorry, cacheflush() for MIPS not implemented"
|
||||
#else
|
||||
/* LY: assume no mmap/dcache issues. */
|
||||
(void) addr;
|
||||
(void) nbytes;
|
||||
#endif
|
||||
}
|
||||
11542
src/mdbx.c
Normal file
11542
src/mdbx.c
Normal file
File diff suppressed because it is too large
Load Diff
1656
src/mdbx.h
Normal file
1656
src/mdbx.h
Normal file
File diff suppressed because it is too large
Load Diff
979
src/mdbx_chk.c
Normal file
979
src/mdbx_chk.c
Normal file
@@ -0,0 +1,979 @@
|
||||
/* mdbx_chk.c - memory-mapped database check tool */
|
||||
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
*
|
||||
* This file is part of libmdbx.
|
||||
*
|
||||
* libmdbx is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libmdbx is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mdbx.h"
|
||||
#include "midl.h"
|
||||
|
||||
typedef struct flagbit {
|
||||
int bit;
|
||||
char *name;
|
||||
} flagbit;
|
||||
|
||||
flagbit dbflags[] = {{MDB_DUPSORT, "dupsort"},
|
||||
{MDB_INTEGERKEY, "integerkey"},
|
||||
{MDB_REVERSEKEY, "reversekey"},
|
||||
{MDB_DUPFIXED, "dupfixed"},
|
||||
{MDB_REVERSEDUP, "reversedup"},
|
||||
{MDB_INTEGERDUP, "integerdup"},
|
||||
{0, NULL}};
|
||||
|
||||
static volatile sig_atomic_t gotsignal;
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
(void)sig;
|
||||
gotsignal = 1;
|
||||
}
|
||||
|
||||
#define MAX_DBI 32768
|
||||
|
||||
#define EXIT_INTERRUPTED (EXIT_FAILURE + 4)
|
||||
#define EXIT_FAILURE_SYS (EXIT_FAILURE + 3)
|
||||
#define EXIT_FAILURE_MDB (EXIT_FAILURE + 2)
|
||||
#define EXIT_FAILURE_CHECK_MAJOR (EXIT_FAILURE + 1)
|
||||
#define EXIT_FAILURE_CHECK_MINOR EXIT_FAILURE
|
||||
|
||||
struct {
|
||||
const char *dbi_names[MAX_DBI];
|
||||
size_t dbi_pages[MAX_DBI];
|
||||
size_t dbi_empty_pages[MAX_DBI];
|
||||
size_t dbi_payload_bytes[MAX_DBI];
|
||||
size_t dbi_lost_bytes[MAX_DBI];
|
||||
short *pagemap;
|
||||
size_t total_payload_bytes;
|
||||
size_t pgcount;
|
||||
} walk;
|
||||
|
||||
static __attribute__((constructor)) void init_walk(void) {
|
||||
walk.dbi_names[0] = "@gc";
|
||||
}
|
||||
|
||||
size_t total_unused_bytes;
|
||||
int exclusive = 2;
|
||||
|
||||
MDB_env *env;
|
||||
MDB_txn *txn, *locktxn;
|
||||
MDBX_envinfo info;
|
||||
MDBX_stat stat;
|
||||
size_t maxkeysize, reclaimable_pages, freedb_pages, lastpgno;
|
||||
size_t userdb_count, skipped_subdb;
|
||||
unsigned verbose, quiet;
|
||||
const char *only_subdb;
|
||||
|
||||
struct problem {
|
||||
struct problem *pr_next;
|
||||
size_t count;
|
||||
const char *caption;
|
||||
};
|
||||
|
||||
struct problem *problems_list;
|
||||
size_t total_problems;
|
||||
|
||||
static void __attribute__((format(printf, 1, 2))) print(const char *msg, ...) {
|
||||
if (!quiet) {
|
||||
va_list args;
|
||||
|
||||
fflush(stderr);
|
||||
va_start(args, msg);
|
||||
vfprintf(stdout, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((format(printf, 1, 2))) error(const char *msg, ...) {
|
||||
total_problems++;
|
||||
|
||||
if (!quiet) {
|
||||
va_list args;
|
||||
|
||||
fflush(stdout);
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
fflush(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void pagemap_cleanup(void) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < MAX_DBI; ++i) {
|
||||
if (walk.dbi_names[i]) {
|
||||
free((void *)walk.dbi_names[i]);
|
||||
walk.dbi_names[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(walk.pagemap);
|
||||
walk.pagemap = NULL;
|
||||
}
|
||||
|
||||
static int pagemap_lookup_dbi(const char *dbi) {
|
||||
static int last;
|
||||
int i;
|
||||
|
||||
if (last > 0 && strcmp(walk.dbi_names[last], dbi) == 0)
|
||||
return last;
|
||||
|
||||
for (i = 1; walk.dbi_names[i] && last < MAX_DBI; ++i)
|
||||
if (strcmp(walk.dbi_names[i], dbi) == 0)
|
||||
return last = i;
|
||||
|
||||
if (i == MAX_DBI)
|
||||
return -1;
|
||||
|
||||
walk.dbi_names[i] = strdup(dbi);
|
||||
|
||||
if (verbose > 1) {
|
||||
print(" - found '%s' area\n", dbi);
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
return last = i;
|
||||
}
|
||||
|
||||
static void problem_add(const char *object, size_t entry_number,
|
||||
const char *msg, const char *extra, ...) {
|
||||
total_problems++;
|
||||
|
||||
if (!quiet) {
|
||||
int need_fflush = 0;
|
||||
struct problem *p;
|
||||
|
||||
for (p = problems_list; p; p = p->pr_next)
|
||||
if (p->caption == msg)
|
||||
break;
|
||||
|
||||
if (!p) {
|
||||
p = calloc(1, sizeof(*p));
|
||||
p->caption = msg;
|
||||
p->pr_next = problems_list;
|
||||
problems_list = p;
|
||||
need_fflush = 1;
|
||||
}
|
||||
|
||||
p->count++;
|
||||
if (verbose > 1) {
|
||||
print(" %s #%zu: %s", object, entry_number, msg);
|
||||
if (extra) {
|
||||
va_list args;
|
||||
printf(" (");
|
||||
va_start(args, extra);
|
||||
vfprintf(stdout, extra, args);
|
||||
va_end(args);
|
||||
printf(")");
|
||||
}
|
||||
printf("\n");
|
||||
if (need_fflush)
|
||||
fflush(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct problem *problems_push() {
|
||||
struct problem *p = problems_list;
|
||||
problems_list = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
static size_t problems_pop(struct problem *list) {
|
||||
size_t count = 0;
|
||||
|
||||
if (problems_list) {
|
||||
int i;
|
||||
|
||||
print(" - problems: ");
|
||||
for (i = 0; problems_list; ++i) {
|
||||
struct problem *p = problems_list->pr_next;
|
||||
count += problems_list->count;
|
||||
print("%s%s (%zu)", i ? ", " : "", problems_list->caption,
|
||||
problems_list->count);
|
||||
free(problems_list);
|
||||
problems_list = p;
|
||||
}
|
||||
print("\n");
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
problems_list = list;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int pgvisitor(size_t pgno, unsigned pgnumber, void *ctx, const char *dbi,
|
||||
const char *type, int nentries, int payload_bytes,
|
||||
int header_bytes, int unused_bytes) {
|
||||
(void)ctx;
|
||||
|
||||
if (type) {
|
||||
size_t page_bytes = payload_bytes + header_bytes + unused_bytes;
|
||||
size_t page_size = pgnumber * stat.ms_psize;
|
||||
int index = pagemap_lookup_dbi(dbi);
|
||||
if (index < 0)
|
||||
return ENOMEM;
|
||||
|
||||
if (verbose > 2 && (!only_subdb || strcmp(only_subdb, dbi) == 0)) {
|
||||
if (pgnumber == 1)
|
||||
print(" %s-page %zu", type, pgno);
|
||||
else
|
||||
print(" %s-span %zu[%u]", type, pgno, pgnumber);
|
||||
print(" of %s: header %i, payload %i, unused %i\n", dbi, header_bytes,
|
||||
payload_bytes, unused_bytes);
|
||||
}
|
||||
|
||||
walk.pgcount += pgnumber;
|
||||
|
||||
if (unused_bytes < 0 || (size_t)unused_bytes > page_size)
|
||||
problem_add("page", pgno, "illegal unused-bytes", "%zu < %i < %zu", 0,
|
||||
unused_bytes, stat.ms_psize);
|
||||
|
||||
if (header_bytes < (int)sizeof(long) ||
|
||||
(size_t)header_bytes >= stat.ms_psize - sizeof(long))
|
||||
problem_add("page", pgno, "illegal header-length", "%zu < %i < %zu",
|
||||
sizeof(long), header_bytes, stat.ms_psize - sizeof(long));
|
||||
if (payload_bytes < 1) {
|
||||
if (nentries > 1) {
|
||||
problem_add("page", pgno, "zero size-of-entry",
|
||||
"payload %i bytes, %i entries", payload_bytes, nentries);
|
||||
if ((size_t)header_bytes + unused_bytes < page_size) {
|
||||
/* LY: hush a misuse error */
|
||||
page_bytes = page_size;
|
||||
}
|
||||
} else {
|
||||
problem_add("page", pgno, "empty", "payload %i bytes, %i entries",
|
||||
payload_bytes, nentries);
|
||||
walk.dbi_empty_pages[index] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (page_bytes != page_size) {
|
||||
problem_add("page", pgno, "misused", "%zu != %zu (%ih + %ip + %iu)",
|
||||
page_size, page_bytes, header_bytes, payload_bytes,
|
||||
unused_bytes);
|
||||
if (page_size > page_bytes)
|
||||
walk.dbi_lost_bytes[index] += page_size - page_bytes;
|
||||
} else {
|
||||
walk.dbi_payload_bytes[index] += payload_bytes + header_bytes;
|
||||
walk.total_payload_bytes += payload_bytes + header_bytes;
|
||||
}
|
||||
|
||||
if (pgnumber) {
|
||||
do {
|
||||
if (pgno >= lastpgno)
|
||||
problem_add("page", pgno, "wrong page-no", "%zu > %zi", pgno,
|
||||
lastpgno);
|
||||
else if (walk.pagemap[pgno])
|
||||
problem_add("page", pgno, "already used", "in %s",
|
||||
walk.dbi_names[walk.pagemap[pgno]]);
|
||||
else {
|
||||
walk.pagemap[pgno] = index;
|
||||
walk.dbi_pages[index] += 1;
|
||||
}
|
||||
++pgno;
|
||||
} while (--pgnumber);
|
||||
}
|
||||
}
|
||||
|
||||
return gotsignal ? EINTR : MDB_SUCCESS;
|
||||
}
|
||||
|
||||
typedef int(visitor)(size_t record_number, MDB_val *key, MDB_val *data);
|
||||
static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent);
|
||||
|
||||
static int handle_userdb(size_t record_number, MDB_val *key, MDB_val *data) {
|
||||
(void)record_number;
|
||||
(void)key;
|
||||
(void)data;
|
||||
return MDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int handle_freedb(size_t record_number, MDB_val *key, MDB_val *data) {
|
||||
char *bad = "";
|
||||
size_t pg, prev;
|
||||
ssize_t i, number, span = 0;
|
||||
size_t *iptr = data->mv_data, txnid = *(size_t *)key->mv_data;
|
||||
|
||||
if (key->mv_size != sizeof(txnid))
|
||||
problem_add("entry", record_number, "wrong txn-id size", "key-size %zi",
|
||||
key->mv_size);
|
||||
else if (txnid < 1 || txnid > info.me_last_txnid)
|
||||
problem_add("entry", record_number, "wrong txn-id", "%zu", txnid);
|
||||
|
||||
if (data->mv_size < sizeof(size_t) || data->mv_size % sizeof(size_t))
|
||||
problem_add("entry", record_number, "wrong idl size", "%zu", data->mv_size);
|
||||
else {
|
||||
number = *iptr++;
|
||||
if (number >= MDB_IDL_UM_MAX)
|
||||
problem_add("entry", record_number, "wrong idl length", "%zi", number);
|
||||
else if ((number + 1) * sizeof(size_t) != data->mv_size)
|
||||
problem_add("entry", record_number, "mismatch idl length", "%zi != %zu",
|
||||
number * sizeof(size_t), data->mv_size);
|
||||
else {
|
||||
freedb_pages += number;
|
||||
if (info.me_tail_txnid > txnid)
|
||||
reclaimable_pages += number;
|
||||
for (i = number, prev = 1; --i >= 0;) {
|
||||
pg = iptr[i];
|
||||
if (pg < 2 /* META_PAGE */ || pg > info.me_last_pgno)
|
||||
problem_add("entry", record_number, "wrong idl entry",
|
||||
"2 < %zi < %zi", pg, info.me_last_pgno);
|
||||
else if (pg <= prev) {
|
||||
bad = " [bad sequence]";
|
||||
problem_add("entry", record_number, "bad sequence", "%zi <= %zi", pg,
|
||||
prev);
|
||||
}
|
||||
prev = pg;
|
||||
pg += span;
|
||||
for (; i >= span && iptr[i - span] == pg; span++, pg++)
|
||||
;
|
||||
}
|
||||
if (verbose > 2 && !only_subdb) {
|
||||
print(" transaction %zu, %zd pages, maxspan %zd%s\n",
|
||||
*(size_t *)key->mv_data, number, span, bad);
|
||||
if (verbose > 3) {
|
||||
int j = number - 1;
|
||||
while (j >= 0) {
|
||||
pg = iptr[j];
|
||||
for (span = 1; --j >= 0 && iptr[j] == pg + span; span++)
|
||||
;
|
||||
if (span > 1)
|
||||
print(" %9zu[%zd]\n", pg, span);
|
||||
else
|
||||
print(" %9zu\n", pg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int handle_maindb(size_t record_number, MDB_val *key, MDB_val *data) {
|
||||
char *name;
|
||||
int rc;
|
||||
size_t i;
|
||||
|
||||
name = key->mv_data;
|
||||
for (i = 0; i < key->mv_size; ++i) {
|
||||
if (name[i] < ' ')
|
||||
return handle_userdb(record_number, key, data);
|
||||
}
|
||||
|
||||
name = malloc(key->mv_size + 1);
|
||||
memcpy(name, key->mv_data, key->mv_size);
|
||||
name[key->mv_size] = '\0';
|
||||
userdb_count++;
|
||||
|
||||
rc = process_db(-1, name, handle_userdb, 0);
|
||||
free(name);
|
||||
if (rc != MDB_INCOMPATIBLE)
|
||||
return rc;
|
||||
|
||||
return handle_userdb(record_number, key, data);
|
||||
}
|
||||
|
||||
static int process_db(MDB_dbi dbi, char *name, visitor *handler, int silent) {
|
||||
MDB_cursor *mc;
|
||||
MDBX_stat ms;
|
||||
MDB_val key, data;
|
||||
MDB_val prev_key, prev_data;
|
||||
unsigned flags;
|
||||
int rc, i;
|
||||
struct problem *saved_list;
|
||||
size_t problems_count;
|
||||
|
||||
unsigned record_count = 0, dups = 0;
|
||||
size_t key_bytes = 0, data_bytes = 0;
|
||||
|
||||
if (0 > (int)dbi) {
|
||||
rc = mdbx_dbi_open(txn, name, 0, &dbi);
|
||||
if (rc) {
|
||||
if (!name ||
|
||||
rc !=
|
||||
MDB_INCOMPATIBLE) /* LY: mainDB's record is not a user's DB. */ {
|
||||
error(" - mdbx_open '%s' failed, error %d %s\n", name ? name : "main",
|
||||
rc, mdbx_strerror(rc));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (dbi >= 2 /* CORE_DBS */ && name && only_subdb &&
|
||||
strcmp(only_subdb, name)) {
|
||||
if (verbose) {
|
||||
print("Skip processing '%s'...\n", name);
|
||||
fflush(NULL);
|
||||
}
|
||||
skipped_subdb++;
|
||||
return MDB_SUCCESS;
|
||||
}
|
||||
|
||||
if (!silent && verbose) {
|
||||
print("Processing '%s'...\n", name ? name : "main");
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
rc = mdbx_dbi_flags(txn, dbi, &flags);
|
||||
if (rc) {
|
||||
error(" - mdbx_dbi_flags failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = mdbx_stat(txn, dbi, &ms, sizeof(ms));
|
||||
if (rc) {
|
||||
error(" - mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!silent && verbose) {
|
||||
print(" - dbi-id %d, flags:", dbi);
|
||||
if (!flags)
|
||||
print(" none");
|
||||
else {
|
||||
for (i = 0; dbflags[i].bit; i++)
|
||||
if (flags & dbflags[i].bit)
|
||||
print(" %s", dbflags[i].name);
|
||||
}
|
||||
print(" (0x%02X)\n", flags);
|
||||
if (verbose > 1) {
|
||||
print(" - page size %u, entries %zu\n", ms.ms_psize, ms.ms_entries);
|
||||
print(" - b-tree depth %u, pages: branch %zu, leaf %zu, overflow %zu\n",
|
||||
ms.ms_depth, ms.ms_branch_pages, ms.ms_leaf_pages,
|
||||
ms.ms_overflow_pages);
|
||||
}
|
||||
}
|
||||
|
||||
rc = mdbx_cursor_open(txn, dbi, &mc);
|
||||
if (rc) {
|
||||
error(" - mdbx_cursor_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
saved_list = problems_push();
|
||||
prev_key.mv_data = NULL;
|
||||
prev_data.mv_size = 0;
|
||||
rc = mdbx_cursor_get(mc, &key, &data, MDB_FIRST);
|
||||
while (rc == MDB_SUCCESS) {
|
||||
if (gotsignal) {
|
||||
print(" - interrupted by signal\n");
|
||||
fflush(NULL);
|
||||
rc = EINTR;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (key.mv_size > maxkeysize) {
|
||||
problem_add("entry", record_count, "key length exceeds max-key-size",
|
||||
"%zu > %zu", key.mv_size, maxkeysize);
|
||||
} else if ((flags & MDB_INTEGERKEY) && key.mv_size != sizeof(size_t) &&
|
||||
key.mv_size != sizeof(int)) {
|
||||
problem_add("entry", record_count, "wrong key length", "%zu != %zu",
|
||||
key.mv_size, sizeof(size_t));
|
||||
}
|
||||
|
||||
if ((flags & MDB_INTEGERDUP) && data.mv_size != sizeof(size_t) &&
|
||||
data.mv_size != sizeof(int)) {
|
||||
problem_add("entry", record_count, "wrong data length", "%zu != %zu",
|
||||
data.mv_size, sizeof(size_t));
|
||||
}
|
||||
|
||||
if (prev_key.mv_data) {
|
||||
if ((flags & MDB_DUPFIXED) && prev_data.mv_size != data.mv_size) {
|
||||
problem_add("entry", record_count, "different data length",
|
||||
"%zu != %zu", prev_data.mv_size, data.mv_size);
|
||||
}
|
||||
|
||||
int cmp = mdbx_cmp(txn, dbi, &prev_key, &key);
|
||||
if (cmp > 0) {
|
||||
problem_add("entry", record_count, "broken ordering of entries", NULL);
|
||||
} else if (cmp == 0) {
|
||||
++dups;
|
||||
if (!(flags & MDB_DUPSORT))
|
||||
problem_add("entry", record_count, "duplicated entries", NULL);
|
||||
else if (flags & MDB_INTEGERDUP) {
|
||||
cmp = mdbx_dcmp(txn, dbi, &prev_data, &data);
|
||||
if (cmp > 0)
|
||||
problem_add("entry", record_count,
|
||||
"broken ordering of multi-values", NULL);
|
||||
}
|
||||
}
|
||||
} else if (verbose) {
|
||||
if (flags & MDB_INTEGERKEY)
|
||||
print(" - fixed key-size %zu\n", key.mv_size);
|
||||
if (flags & (MDB_INTEGERDUP | MDB_DUPFIXED))
|
||||
print(" - fixed data-size %zu\n", data.mv_size);
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
rc = handler(record_count, &key, &data);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
record_count++;
|
||||
key_bytes += key.mv_size;
|
||||
data_bytes += data.mv_size;
|
||||
|
||||
prev_key = key;
|
||||
prev_data = data;
|
||||
rc = mdbx_cursor_get(mc, &key, &data, MDB_NEXT);
|
||||
}
|
||||
if (rc != MDB_NOTFOUND)
|
||||
error(" - mdbx_cursor_get failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
else
|
||||
rc = 0;
|
||||
|
||||
if (record_count != ms.ms_entries)
|
||||
problem_add("entry", record_count, "differentent number of entries",
|
||||
"%zu != %zu", record_count, ms.ms_entries);
|
||||
bailout:
|
||||
problems_count = problems_pop(saved_list);
|
||||
if (!silent && verbose) {
|
||||
print(" - summary: %u records, %u dups, %zu key's bytes, %zu data's "
|
||||
"bytes, %zu problems\n",
|
||||
record_count, dups, key_bytes, data_bytes, problems_count);
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
mdbx_cursor_close(mc);
|
||||
return rc || problems_count;
|
||||
}
|
||||
|
||||
static void usage(char *prog) {
|
||||
fprintf(stderr,
|
||||
"usage: %s dbpath [-V] [-v] [-n] [-q] [-w] [-c] [-d] [-s subdb]\n"
|
||||
" -V\t\tshow version\n"
|
||||
" -v\t\tmore verbose, could be used multiple times\n"
|
||||
" -n\t\tNOSUBDIR mode for open\n"
|
||||
" -q\t\tbe quiet\n"
|
||||
" -w\t\tlock DB for writing while checking\n"
|
||||
" -d\t\tdisable page-by-page traversal of b-tree\n"
|
||||
" -s subdb\tprocess a specific subdatabase only\n"
|
||||
" -c\t\tforce cooperative mode (don't try exclusive)\n",
|
||||
prog);
|
||||
exit(EXIT_INTERRUPTED);
|
||||
}
|
||||
|
||||
const char *meta_synctype(size_t sign) {
|
||||
switch (sign) {
|
||||
case 0:
|
||||
return "no-sync/legacy";
|
||||
case 1:
|
||||
return "weak";
|
||||
default:
|
||||
return "steady";
|
||||
}
|
||||
}
|
||||
|
||||
int meta_lt(size_t txn1, size_t sign1, size_t txn2, size_t sign2) {
|
||||
return ((sign1 > 1) == (sign2 > 1)) ? txn1 < txn2 : txn2 && sign2 > 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
char *prog = argv[0];
|
||||
char *envname;
|
||||
int envflags = MDB_RDONLY;
|
||||
int problems_maindb = 0, problems_freedb = 0, problems_meta = 0;
|
||||
int dont_traversal = 0;
|
||||
size_t n;
|
||||
struct timespec timestamp_start, timestamp_finish;
|
||||
double elapsed;
|
||||
|
||||
atexit(pagemap_cleanup);
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, ×tamp_start)) {
|
||||
rc = errno;
|
||||
error("clock_gettime failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
return EXIT_FAILURE_SYS;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
usage(prog);
|
||||
}
|
||||
|
||||
while ((i = getopt(argc, argv, "Vvqnwcds:")) != EOF) {
|
||||
switch (i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 'w':
|
||||
envflags &= ~MDB_RDONLY;
|
||||
break;
|
||||
case 'c':
|
||||
exclusive = 0;
|
||||
break;
|
||||
case 'd':
|
||||
dont_traversal = 1;
|
||||
break;
|
||||
case 's':
|
||||
if (only_subdb && strcmp(only_subdb, optarg))
|
||||
usage(prog);
|
||||
only_subdb = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(prog);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage(prog);
|
||||
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, signal_handler);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, signal_handler);
|
||||
#endif
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
envname = argv[optind];
|
||||
print("Running mdbx_chk for '%s' in %s mode...\n", envname,
|
||||
(envflags & MDB_RDONLY) ? "read-only" : "write-lock");
|
||||
fflush(NULL);
|
||||
|
||||
rc = mdbx_env_create(&env);
|
||||
if (rc) {
|
||||
error("mdbx_env_create failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
return rc < 0 ? EXIT_FAILURE_MDB : EXIT_FAILURE_SYS;
|
||||
}
|
||||
|
||||
rc = mdbx_env_get_maxkeysize(env);
|
||||
if (rc < 0) {
|
||||
error("mdbx_env_get_maxkeysize failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
maxkeysize = rc;
|
||||
|
||||
rc = mdbx_env_set_maxdbs(env, MAX_DBI);
|
||||
if (rc < 0) {
|
||||
error("mdbx_env_set_maxdbs failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
rc = mdbx_env_open_ex(env, envname, envflags, 0664, &exclusive);
|
||||
if (rc) {
|
||||
error("mdbx_env_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
if (verbose)
|
||||
print(" - %s mode\n", exclusive ? "monopolistic" : "cooperative");
|
||||
|
||||
if (!(envflags & MDB_RDONLY)) {
|
||||
rc = mdbx_txn_begin(env, NULL, 0, &locktxn);
|
||||
if (rc) {
|
||||
error("mdbx_txn_begin(lock-write) failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
|
||||
rc = mdbx_txn_begin(env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) {
|
||||
error("mdbx_txn_begin(read-only) failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
rc = mdbx_env_info(env, &info, sizeof(info));
|
||||
if (rc) {
|
||||
error("mdbx_env_info failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
rc = mdbx_env_stat(env, &stat, sizeof(stat));
|
||||
if (rc) {
|
||||
error("mdbx_env_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
lastpgno = info.me_last_pgno + 1;
|
||||
errno = 0;
|
||||
|
||||
if (verbose) {
|
||||
double k = 1024.0;
|
||||
const char sf[] =
|
||||
"KMGTPEZY"; /* LY: Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta! */
|
||||
for (i = 0; sf[i + 1] && info.me_mapsize / k > 1000.0; ++i)
|
||||
k *= 1024;
|
||||
print(" - map size %zu (%.2f %cb)\n", info.me_mapsize, info.me_mapsize / k,
|
||||
sf[i]);
|
||||
if (info.me_mapaddr)
|
||||
print(" - mapaddr %p\n", info.me_mapaddr);
|
||||
print(" - pagesize %u, max keysize %zu (%s), max readers %u\n",
|
||||
stat.ms_psize, maxkeysize,
|
||||
(maxkeysize == 511) ? "default" : (maxkeysize == 0) ? "devel"
|
||||
: "custom",
|
||||
info.me_maxreaders);
|
||||
print(" - transactions: last %zu, bottom %zu, lag reading %zi\n",
|
||||
info.me_last_txnid, info.me_tail_txnid,
|
||||
info.me_last_txnid - info.me_tail_txnid);
|
||||
|
||||
print(" - meta-1: %s %zu, %s", meta_synctype(info.me_meta1_sign),
|
||||
info.me_meta1_txnid, meta_lt(info.me_meta1_txnid, info.me_meta1_sign,
|
||||
info.me_meta2_txnid, info.me_meta2_sign)
|
||||
? "tail"
|
||||
: "head");
|
||||
if (info.me_meta1_txnid > info.me_last_txnid)
|
||||
print(", rolled-back %zu (%zu >>> %zu)",
|
||||
info.me_meta1_txnid - info.me_last_txnid, info.me_meta1_txnid,
|
||||
info.me_last_txnid);
|
||||
print("\n");
|
||||
|
||||
print(" - meta-2: %s %zu, %s", meta_synctype(info.me_meta2_sign),
|
||||
info.me_meta2_txnid, meta_lt(info.me_meta2_txnid, info.me_meta2_sign,
|
||||
info.me_meta1_txnid, info.me_meta1_sign)
|
||||
? "tail"
|
||||
: "head");
|
||||
if (info.me_meta2_txnid > info.me_last_txnid)
|
||||
print(", rolled-back %zu (%zu >>> %zu)",
|
||||
info.me_meta2_txnid - info.me_last_txnid, info.me_meta2_txnid,
|
||||
info.me_last_txnid);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
if (exclusive > 1) {
|
||||
if (verbose)
|
||||
print(" - perform full check last-txn-id with meta-pages\n");
|
||||
|
||||
if (!meta_lt(info.me_meta1_txnid, info.me_meta1_sign, info.me_meta2_txnid,
|
||||
info.me_meta2_sign) &&
|
||||
info.me_meta1_txnid != info.me_last_txnid) {
|
||||
print(" - meta-1 txn-id mismatch last-txn-id (%zi != %zi)\n",
|
||||
info.me_meta1_txnid, info.me_last_txnid);
|
||||
++problems_meta;
|
||||
}
|
||||
|
||||
if (!meta_lt(info.me_meta2_txnid, info.me_meta2_sign, info.me_meta1_txnid,
|
||||
info.me_meta1_sign) &&
|
||||
info.me_meta2_txnid != info.me_last_txnid) {
|
||||
print(" - meta-2 txn-id mismatch last-txn-id (%zi != %zi)\n",
|
||||
info.me_meta2_txnid, info.me_last_txnid);
|
||||
++problems_meta;
|
||||
}
|
||||
} else if (locktxn) {
|
||||
if (verbose)
|
||||
print(" - perform lite check last-txn-id with meta-pages (not a "
|
||||
"monopolistic mode)\n");
|
||||
size_t last = (info.me_meta2_txnid > info.me_meta1_txnid)
|
||||
? info.me_meta2_txnid
|
||||
: info.me_meta1_txnid;
|
||||
if (last != info.me_last_txnid) {
|
||||
print(" - last-meta mismatch last-txn-id (%zi != %zi)\n", last,
|
||||
info.me_last_txnid);
|
||||
++problems_meta;
|
||||
}
|
||||
} else if (verbose) {
|
||||
print(" - skip check last-txn-id with meta-pages (monopolistic or "
|
||||
"write-lock mode only)\n");
|
||||
}
|
||||
|
||||
if (!dont_traversal) {
|
||||
struct problem *saved_list;
|
||||
size_t traversal_problems;
|
||||
size_t empty_pages, lost_bytes;
|
||||
|
||||
print("Traversal b-tree...\n");
|
||||
fflush(NULL);
|
||||
walk.pagemap = calloc(lastpgno, sizeof(*walk.pagemap));
|
||||
if (!walk.pagemap) {
|
||||
rc = errno ? errno : ENOMEM;
|
||||
error("calloc failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
saved_list = problems_push();
|
||||
rc = mdbx_env_pgwalk(txn, pgvisitor, NULL);
|
||||
traversal_problems = problems_pop(saved_list);
|
||||
|
||||
if (rc) {
|
||||
if (rc == EINTR && gotsignal) {
|
||||
print(" - interrupted by signal\n");
|
||||
fflush(NULL);
|
||||
} else {
|
||||
error("mdbx_env_pgwalk failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
}
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
for (n = 0; n < lastpgno; ++n)
|
||||
if (!walk.pagemap[n])
|
||||
walk.dbi_pages[0] += 1;
|
||||
|
||||
empty_pages = lost_bytes = 0;
|
||||
for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i) {
|
||||
empty_pages += walk.dbi_empty_pages[i];
|
||||
lost_bytes += walk.dbi_lost_bytes[i];
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
size_t total_page_bytes = walk.pgcount * stat.ms_psize;
|
||||
print(" - dbi pages: %zu total", walk.pgcount);
|
||||
if (verbose > 1)
|
||||
for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i)
|
||||
print(", %s %zu", walk.dbi_names[i], walk.dbi_pages[i]);
|
||||
print(", %s %zu\n", walk.dbi_names[0], walk.dbi_pages[0]);
|
||||
if (verbose > 1) {
|
||||
print(" - space info: total %zu bytes, payload %zu (%.1f%%), unused "
|
||||
"%zu (%.1f%%)\n",
|
||||
total_page_bytes, walk.total_payload_bytes,
|
||||
walk.total_payload_bytes * 100.0 / total_page_bytes,
|
||||
total_page_bytes - walk.total_payload_bytes,
|
||||
(total_page_bytes - walk.total_payload_bytes) * 100.0 /
|
||||
total_page_bytes);
|
||||
for (i = 1; i < MAX_DBI && walk.dbi_names[i]; ++i) {
|
||||
size_t dbi_bytes = walk.dbi_pages[i] * stat.ms_psize;
|
||||
print(" %s: subtotal %zu bytes (%.1f%%), payload %zu (%.1f%%), "
|
||||
"unused %zu (%.1f%%)",
|
||||
walk.dbi_names[i], dbi_bytes,
|
||||
dbi_bytes * 100.0 / total_page_bytes, walk.dbi_payload_bytes[i],
|
||||
walk.dbi_payload_bytes[i] * 100.0 / dbi_bytes,
|
||||
dbi_bytes - walk.dbi_payload_bytes[i],
|
||||
(dbi_bytes - walk.dbi_payload_bytes[i]) * 100.0 / dbi_bytes);
|
||||
if (walk.dbi_empty_pages[i])
|
||||
print(", %zu empty pages", walk.dbi_empty_pages[i]);
|
||||
if (walk.dbi_lost_bytes[i])
|
||||
print(", %zu bytes lost", walk.dbi_lost_bytes[i]);
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
print(" - summary: average fill %.1f%%",
|
||||
walk.total_payload_bytes * 100.0 / total_page_bytes);
|
||||
if (empty_pages)
|
||||
print(", %zu empty pages", empty_pages);
|
||||
if (lost_bytes)
|
||||
print(", %zu bytes lost", lost_bytes);
|
||||
print(", %zu problems\n", traversal_problems);
|
||||
}
|
||||
} else if (verbose) {
|
||||
print("Skipping b-tree walk...\n");
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
print("Iterating DBIs...\n");
|
||||
problems_maindb = process_db(-1, /* MAIN_DBI */ NULL, NULL, 0);
|
||||
problems_freedb = process_db(0 /* FREE_DBI */, "free", handle_freedb, 0);
|
||||
|
||||
if (verbose) {
|
||||
size_t value = info.me_mapsize / stat.ms_psize;
|
||||
double percent = value / 100.0;
|
||||
print(" - pages info: %zu total", value);
|
||||
print(", allocated %zu (%.1f%%)", lastpgno, lastpgno / percent);
|
||||
|
||||
if (verbose > 1) {
|
||||
value = info.me_mapsize / stat.ms_psize - lastpgno;
|
||||
print(", remained %zu (%.1f%%)", value, value / percent);
|
||||
|
||||
value = lastpgno - freedb_pages;
|
||||
print(", used %zu (%.1f%%)", value, value / percent);
|
||||
|
||||
print(", gc %zu (%.1f%%)", freedb_pages, freedb_pages / percent);
|
||||
|
||||
value = freedb_pages - reclaimable_pages;
|
||||
print(", detained %zu (%.1f%%)", value, value / percent);
|
||||
|
||||
print(", reclaimable %zu (%.1f%%)", reclaimable_pages,
|
||||
reclaimable_pages / percent);
|
||||
}
|
||||
|
||||
value = info.me_mapsize / stat.ms_psize - lastpgno + reclaimable_pages;
|
||||
print(", available %zu (%.1f%%)\n", value, value / percent);
|
||||
}
|
||||
|
||||
if (problems_maindb == 0 && problems_freedb == 0) {
|
||||
if (!dont_traversal && (exclusive || locktxn)) {
|
||||
if (walk.pgcount != lastpgno - freedb_pages) {
|
||||
error("used pages mismatch (%zu != %zu)\n", walk.pgcount,
|
||||
lastpgno - freedb_pages);
|
||||
}
|
||||
if (walk.dbi_pages[0] != freedb_pages) {
|
||||
error("gc pages mismatch (%zu != %zu)\n", walk.dbi_pages[0],
|
||||
freedb_pages);
|
||||
}
|
||||
} else if (verbose) {
|
||||
print(" - skip check used and gc pages (btree-traversal with "
|
||||
"monopolistic or write-lock mode only)\n");
|
||||
}
|
||||
|
||||
if (!process_db(-1, NULL, handle_maindb, 1)) {
|
||||
if (!userdb_count && verbose)
|
||||
print(" - does not contain multiple databases\n");
|
||||
}
|
||||
}
|
||||
|
||||
bailout:
|
||||
if (txn)
|
||||
mdbx_txn_abort(txn);
|
||||
if (locktxn)
|
||||
mdbx_txn_abort(locktxn);
|
||||
if (env)
|
||||
mdbx_env_close(env);
|
||||
fflush(NULL);
|
||||
if (rc) {
|
||||
if (rc < 0)
|
||||
return gotsignal ? EXIT_INTERRUPTED : EXIT_FAILURE_SYS;
|
||||
return EXIT_FAILURE_MDB;
|
||||
}
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, ×tamp_finish)) {
|
||||
rc = errno;
|
||||
error("clock_gettime failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
return EXIT_FAILURE_SYS;
|
||||
}
|
||||
|
||||
elapsed = timestamp_finish.tv_sec - timestamp_start.tv_sec +
|
||||
(timestamp_finish.tv_nsec - timestamp_start.tv_nsec) * 1e-9;
|
||||
|
||||
total_problems += problems_meta;
|
||||
if (total_problems || problems_maindb || problems_freedb) {
|
||||
print("Total %zu error(s) is detected, elapsed %.3f seconds.\n",
|
||||
total_problems, elapsed);
|
||||
if (problems_meta || problems_maindb || problems_freedb)
|
||||
return EXIT_FAILURE_CHECK_MAJOR;
|
||||
return EXIT_FAILURE_CHECK_MINOR;
|
||||
}
|
||||
print("No error is detected, elapsed %.3f seconds\n", elapsed);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
57
src/mdbx_copy.1
Normal file
57
src/mdbx_copy.1
Normal file
@@ -0,0 +1,57 @@
|
||||
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDB_COPY 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.SH NAME
|
||||
mdbx_copy \- LMDB environment copy tool
|
||||
.SH SYNOPSIS
|
||||
.B mdbx_copy
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BR \-c ]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
.B srcpath
|
||||
[\c
|
||||
.BR dstpath ]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdbx_copy
|
||||
utility copies an LMDB environment. The environment can
|
||||
be copied regardless of whether it is currently in use.
|
||||
No lockfile is created, since it gets recreated at need.
|
||||
|
||||
If
|
||||
.I dstpath
|
||||
is specified it must be the path of an empty directory
|
||||
for storing the backup. Otherwise, the backup will be
|
||||
written to stdout.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-c
|
||||
Compact while copying. Only current data pages will be copied; freed
|
||||
or unused pages will be omitted from the copy. This option will
|
||||
slow down the backup process as it is more CPU-intensive.
|
||||
Currently it fails if the environment has suffered a page leak.
|
||||
.TP
|
||||
.BR \-n
|
||||
Open LDMB environment(s) which do not use subdirectories.
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
.SH CAVEATS
|
||||
This utility can trigger significant file size growth if run
|
||||
in parallel with write transactions, because pages which they
|
||||
free during copying cannot be reused until the copy is done.
|
||||
.SH "SEE ALSO"
|
||||
.BR mdbx_stat (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
76
src/mdbx_copy.c
Normal file
76
src/mdbx_copy.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/* mdbx_copy.c - memory-mapped database backup tool */
|
||||
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2012-2017 Howard Chu, Symas Corp.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include "mdbx.h"
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void sighandle(int sig) { (void)sig; }
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int rc;
|
||||
MDB_env *env = NULL;
|
||||
const char *progname = argv[0], *act;
|
||||
unsigned flags = MDB_RDONLY;
|
||||
unsigned cpflags = 0;
|
||||
|
||||
for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
|
||||
if (argv[1][1] == 'n' && argv[1][2] == '\0')
|
||||
flags |= MDB_NOSUBDIR;
|
||||
else if (argv[1][1] == 'c' && argv[1][2] == '\0')
|
||||
cpflags |= MDB_CP_COMPACT;
|
||||
else if (argv[1][1] == 'V' && argv[1][2] == '\0') {
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
} else
|
||||
argc = 0;
|
||||
}
|
||||
|
||||
if (argc < 2 || argc > 3) {
|
||||
fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, sighandle);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, sighandle);
|
||||
#endif
|
||||
signal(SIGINT, sighandle);
|
||||
signal(SIGTERM, sighandle);
|
||||
|
||||
act = "opening environment";
|
||||
rc = mdbx_env_create(&env);
|
||||
if (rc == MDB_SUCCESS) {
|
||||
rc = mdbx_env_open(env, argv[1], flags, 0640);
|
||||
}
|
||||
if (rc == MDB_SUCCESS) {
|
||||
act = "copying";
|
||||
if (argc == 2)
|
||||
rc = mdbx_env_copyfd2(env, STDOUT_FILENO, cpflags);
|
||||
else
|
||||
rc = mdbx_env_copy2(env, argv[2], cpflags);
|
||||
}
|
||||
if (rc)
|
||||
fprintf(stderr, "%s: %s failed, error %d (%s)\n", progname, act, rc,
|
||||
mdbx_strerror(rc));
|
||||
mdbx_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
77
src/mdbx_dump.1
Normal file
77
src/mdbx_dump.1
Normal file
@@ -0,0 +1,77 @@
|
||||
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDB_DUMP 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.SH NAME
|
||||
mdbx_dump \- LMDB environment export tool
|
||||
.SH SYNOPSIS
|
||||
.B mdbx_dump
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BI \-f \ file\fR]
|
||||
[\c
|
||||
.BR \-l ]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
[\c
|
||||
.BR \-p ]
|
||||
[\c
|
||||
.BR \-a \ |
|
||||
.BI \-s \ subdb\fR]
|
||||
.BR \ envpath
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdbx_dump
|
||||
utility reads a database and writes its contents to the
|
||||
standard output using a portable flat-text format
|
||||
understood by the
|
||||
.BR mdbx_load (1)
|
||||
utility.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-f \ file
|
||||
Write to the specified file instead of to the standard output.
|
||||
.TP
|
||||
.BR \-l
|
||||
List the databases stored in the environment. Just the
|
||||
names will be listed, no data will be output.
|
||||
.TP
|
||||
.BR \-n
|
||||
Dump an LMDB database which does not use subdirectories.
|
||||
.TP
|
||||
.BR \-p
|
||||
If characters in either the key or data items are printing characters (as
|
||||
defined by isprint(3)), output them directly. This option permits users to
|
||||
use standard text editors and tools to modify the contents of databases.
|
||||
|
||||
Note: different systems may have different notions about what characters
|
||||
are considered printing characters, and databases dumped in this manner may
|
||||
be less portable to external systems.
|
||||
.TP
|
||||
.BR \-a
|
||||
Dump all of the subdatabases in the environment.
|
||||
.TP
|
||||
.BR \-s \ subdb
|
||||
Dump a specific subdatabase. If no database is specified, only the main database is dumped.
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
|
||||
Dumping and reloading databases that use user-defined comparison functions
|
||||
will result in new databases that use the default comparison functions.
|
||||
\fBIn this case it is quite likely that the reloaded database will be
|
||||
damaged beyond repair permitting neither record storage nor retrieval.\fP
|
||||
|
||||
The only available workaround is to modify the source for the
|
||||
.BR mdbx_load (1)
|
||||
utility to load the database using the correct comparison functions.
|
||||
.SH "SEE ALSO"
|
||||
.BR mdbx_load (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
316
src/mdbx_dump.c
Normal file
316
src/mdbx_dump.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/* mdbx_dump.c - memory-mapped database dump tool */
|
||||
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include "mdbx.h"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PRINT 1
|
||||
static int mode;
|
||||
|
||||
typedef struct flagbit {
|
||||
int bit;
|
||||
char *name;
|
||||
} flagbit;
|
||||
|
||||
flagbit dbflags[] = {{MDB_REVERSEKEY, "reversekey"},
|
||||
{MDB_DUPSORT, "dupsort"},
|
||||
{MDB_INTEGERKEY, "integerkey"},
|
||||
{MDB_DUPFIXED, "dupfixed"},
|
||||
{MDB_INTEGERDUP, "integerdup"},
|
||||
{MDB_REVERSEDUP, "reversedup"},
|
||||
{0, NULL}};
|
||||
|
||||
static volatile sig_atomic_t gotsig;
|
||||
|
||||
static void dumpsig(int sig) {
|
||||
(void)sig;
|
||||
gotsig = 1;
|
||||
}
|
||||
|
||||
static const char hexc[] = "0123456789abcdef";
|
||||
|
||||
static void hex(unsigned char c) {
|
||||
putchar(hexc[c >> 4]);
|
||||
putchar(hexc[c & 0xf]);
|
||||
}
|
||||
|
||||
static void text(MDB_val *v) {
|
||||
unsigned char *c, *end;
|
||||
|
||||
putchar(' ');
|
||||
c = v->mv_data;
|
||||
end = c + v->mv_size;
|
||||
while (c < end) {
|
||||
if (isprint(*c)) {
|
||||
putchar(*c);
|
||||
} else {
|
||||
putchar('\\');
|
||||
hex(*c);
|
||||
}
|
||||
c++;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void byte(MDB_val *v) {
|
||||
unsigned char *c, *end;
|
||||
|
||||
putchar(' ');
|
||||
c = v->mv_data;
|
||||
end = c + v->mv_size;
|
||||
while (c < end) {
|
||||
hex(*c++);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/* Dump in BDB-compatible format */
|
||||
static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name) {
|
||||
MDB_cursor *mc;
|
||||
MDBX_stat ms;
|
||||
MDB_val key, data;
|
||||
MDBX_envinfo info;
|
||||
unsigned int flags;
|
||||
int rc, i;
|
||||
|
||||
rc = mdbx_dbi_flags(txn, dbi, &flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = mdbx_stat(txn, dbi, &ms, sizeof(ms));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = mdbx_env_info(mdbx_txn_env(txn), &info, sizeof(info));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
printf("VERSION=3\n");
|
||||
printf("format=%s\n", mode & PRINT ? "print" : "bytevalue");
|
||||
if (name)
|
||||
printf("database=%s\n", name);
|
||||
printf("type=btree\n");
|
||||
printf("mapsize=%zu\n", info.me_mapsize);
|
||||
if (info.me_mapaddr)
|
||||
printf("mapaddr=%p\n", info.me_mapaddr);
|
||||
printf("maxreaders=%u\n", info.me_maxreaders);
|
||||
|
||||
for (i = 0; dbflags[i].bit; i++)
|
||||
if (flags & dbflags[i].bit)
|
||||
printf("%s=1\n", dbflags[i].name);
|
||||
|
||||
printf("db_pagesize=%d\n", ms.ms_psize);
|
||||
printf("HEADER=END\n");
|
||||
|
||||
rc = mdbx_cursor_open(txn, dbi, &mc);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
while ((rc = mdbx_cursor_get(mc, &key, &data, MDB_NEXT)) == MDB_SUCCESS) {
|
||||
if (gotsig) {
|
||||
rc = EINTR;
|
||||
break;
|
||||
}
|
||||
if (mode & PRINT) {
|
||||
text(&key);
|
||||
text(&data);
|
||||
} else {
|
||||
byte(&key);
|
||||
byte(&data);
|
||||
}
|
||||
}
|
||||
printf("DATA=END\n");
|
||||
if (rc == MDB_NOTFOUND)
|
||||
rc = MDB_SUCCESS;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void usage(char *prog) {
|
||||
fprintf(stderr,
|
||||
"usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n",
|
||||
prog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
MDB_env *env;
|
||||
MDB_txn *txn;
|
||||
MDB_dbi dbi;
|
||||
char *prog = argv[0];
|
||||
char *envname;
|
||||
char *subname = NULL;
|
||||
int alldbs = 0, envflags = 0, list = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
usage(prog);
|
||||
}
|
||||
|
||||
/* -a: dump main DB and all subDBs
|
||||
* -s: dump only the named subDB
|
||||
* -n: use NOSUBDIR flag on env_open
|
||||
* -p: use printable characters
|
||||
* -f: write to file instead of stdout
|
||||
* -V: print version and exit
|
||||
* (default) dump only the main DB
|
||||
*/
|
||||
while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) {
|
||||
switch (i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
break;
|
||||
case 'l':
|
||||
list = 1;
|
||||
/*FALLTHROUGH*/;
|
||||
case 'a':
|
||||
if (subname)
|
||||
usage(prog);
|
||||
alldbs++;
|
||||
break;
|
||||
case 'f':
|
||||
if (freopen(optarg, "w", stdout) == NULL) {
|
||||
fprintf(stderr, "%s: %s: reopen: %s\n", prog, optarg, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 'p':
|
||||
mode |= PRINT;
|
||||
break;
|
||||
case 's':
|
||||
if (alldbs)
|
||||
usage(prog);
|
||||
subname = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(prog);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage(prog);
|
||||
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, dumpsig);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, dumpsig);
|
||||
#endif
|
||||
signal(SIGINT, dumpsig);
|
||||
signal(SIGTERM, dumpsig);
|
||||
|
||||
envname = argv[optind];
|
||||
rc = mdbx_env_create(&env);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (alldbs || subname) {
|
||||
mdbx_env_set_maxdbs(env, 2);
|
||||
}
|
||||
|
||||
rc = mdbx_env_open(env, envname, envflags | MDB_RDONLY, 0664);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdbx_txn_begin(env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdbx_dbi_open(txn, subname, 0, &dbi);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
if (alldbs) {
|
||||
MDB_cursor *cursor;
|
||||
MDB_val key;
|
||||
int count = 0;
|
||||
|
||||
rc = mdbx_cursor_open(txn, dbi, &cursor);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
while ((rc = mdbx_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
|
||||
char *str;
|
||||
MDB_dbi db2;
|
||||
if (memchr(key.mv_data, '\0', key.mv_size))
|
||||
continue;
|
||||
count++;
|
||||
str = malloc(key.mv_size + 1);
|
||||
memcpy(str, key.mv_data, key.mv_size);
|
||||
str[key.mv_size] = '\0';
|
||||
rc = mdbx_dbi_open(txn, str, 0, &db2);
|
||||
if (rc == MDB_SUCCESS) {
|
||||
if (list) {
|
||||
printf("%s\n", str);
|
||||
list++;
|
||||
} else {
|
||||
rc = dumpit(txn, db2, str);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
mdbx_dbi_close(env, db2);
|
||||
}
|
||||
free(str);
|
||||
if (rc)
|
||||
continue;
|
||||
}
|
||||
mdbx_cursor_close(cursor);
|
||||
if (!count) {
|
||||
fprintf(stderr, "%s: %s does not contain multiple databases\n", prog,
|
||||
envname);
|
||||
rc = MDB_NOTFOUND;
|
||||
} else if (rc == MDB_INCOMPATIBLE) {
|
||||
/* LY: the record it not a named sub-db. */
|
||||
rc = MDB_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
rc = dumpit(txn, dbi, subname);
|
||||
}
|
||||
if (rc && rc != MDB_NOTFOUND)
|
||||
fprintf(stderr, "%s: %s: %s\n", prog, envname, mdbx_strerror(rc));
|
||||
|
||||
mdbx_dbi_close(env, dbi);
|
||||
txn_abort:
|
||||
mdbx_txn_abort(txn);
|
||||
env_close:
|
||||
mdbx_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
79
src/mdbx_load.1
Normal file
79
src/mdbx_load.1
Normal file
@@ -0,0 +1,79 @@
|
||||
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.SH NAME
|
||||
mdbx_load \- LMDB environment import tool
|
||||
.SH SYNOPSIS
|
||||
.B mdbx_load
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BI \-f \ file\fR]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
[\c
|
||||
.BI \-s \ subdb\fR]
|
||||
[\c
|
||||
.BR \-N ]
|
||||
[\c
|
||||
.BR \-T ]
|
||||
.BR \ envpath
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdbx_load
|
||||
utility reads from the standard input and loads it into the
|
||||
LMDB environment
|
||||
.BR envpath .
|
||||
|
||||
The input to
|
||||
.B mdbx_load
|
||||
must be in the output format specified by the
|
||||
.BR mdbx_dump (1)
|
||||
utility or as specified by the
|
||||
.B -T
|
||||
option below.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-f \ file
|
||||
Read from the specified file instead of from the standard input.
|
||||
.TP
|
||||
.BR \-n
|
||||
Load an LMDB database which does not use subdirectories.
|
||||
.TP
|
||||
.BR \-s \ subdb
|
||||
Load a specific subdatabase. If no database is specified, data is loaded into the main database.
|
||||
.TP
|
||||
.BR \-N
|
||||
Don't overwrite existing records when loading into an already existing database; just skip them.
|
||||
.TP
|
||||
.BR \-T
|
||||
Load data from simple text files. The input must be paired lines of text, where the first
|
||||
line of the pair is the key item, and the second line of the pair is its corresponding
|
||||
data item.
|
||||
|
||||
A simple escape mechanism, where newline and backslash (\\) characters are special, is
|
||||
applied to the text input. Newline characters are interpreted as record separators.
|
||||
Backslash characters in the text will be interpreted in one of two ways: If the backslash
|
||||
character precedes another backslash character, the pair will be interpreted as a literal
|
||||
backslash. If the backslash character precedes any other character, the two characters
|
||||
following the backslash will be interpreted as a hexadecimal specification of a single
|
||||
character; for example, \\0a is a newline character in the ASCII character set.
|
||||
|
||||
For this reason, any backslash or newline characters that naturally occur in the text
|
||||
input must be escaped to avoid misinterpretation by
|
||||
.BR mdbx_load .
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.BR mdbx_dump (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
466
src/mdbx_load.c
Normal file
466
src/mdbx_load.c
Normal file
@@ -0,0 +1,466 @@
|
||||
/* mdbx_load.c - memory-mapped database load tool */
|
||||
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include "mdbx.h"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PRINT 1
|
||||
#define NOHDR 2
|
||||
static int mode;
|
||||
|
||||
static char *subname = NULL;
|
||||
|
||||
static size_t lineno;
|
||||
static int version;
|
||||
|
||||
static int dbi_flags;
|
||||
|
||||
static char *prog;
|
||||
|
||||
static int Eof;
|
||||
|
||||
static MDBX_envinfo info;
|
||||
|
||||
static MDB_val kbuf, dbuf;
|
||||
|
||||
#define STRLENOF(s) (sizeof(s) - 1)
|
||||
|
||||
typedef struct flagbit {
|
||||
int bit;
|
||||
char *name;
|
||||
int len;
|
||||
} flagbit;
|
||||
|
||||
#define S(s) s, STRLENOF(s)
|
||||
|
||||
flagbit dbflags[] = {{MDB_REVERSEKEY, S("reversekey")},
|
||||
{MDB_DUPSORT, S("dupsort")},
|
||||
{MDB_INTEGERKEY, S("integerkey")},
|
||||
{MDB_DUPFIXED, S("dupfixed")},
|
||||
{MDB_INTEGERDUP, S("integerdup")},
|
||||
{MDB_REVERSEDUP, S("reversedup")},
|
||||
{0, NULL, 0}};
|
||||
|
||||
static void readhdr(void) {
|
||||
char *ptr;
|
||||
|
||||
dbi_flags = 0;
|
||||
while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) {
|
||||
lineno++;
|
||||
if (!strncmp(dbuf.mv_data, "db_pagesize=", STRLENOF("db_pagesize=")) ||
|
||||
!strncmp(dbuf.mv_data, "duplicates=", STRLENOF("duplicates="))) {
|
||||
/* LY: silently ignore information fields. */
|
||||
continue;
|
||||
} else if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) {
|
||||
version = atoi((char *)dbuf.mv_data + STRLENOF("VERSION="));
|
||||
if (version > 3) {
|
||||
fprintf(stderr, "%s: line %zd: unsupported VERSION %d\n", prog, lineno,
|
||||
version);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "HEADER=END", STRLENOF("HEADER=END"))) {
|
||||
break;
|
||||
} else if (!strncmp(dbuf.mv_data, "format=", STRLENOF("format="))) {
|
||||
if (!strncmp((char *)dbuf.mv_data + STRLENOF("FORMAT="), "print",
|
||||
STRLENOF("print")))
|
||||
mode |= PRINT;
|
||||
else if (strncmp((char *)dbuf.mv_data + STRLENOF("FORMAT="), "bytevalue",
|
||||
STRLENOF("bytevalue"))) {
|
||||
fprintf(stderr, "%s: line %zd: unsupported FORMAT %s\n", prog, lineno,
|
||||
(char *)dbuf.mv_data + STRLENOF("FORMAT="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "database=", STRLENOF("database="))) {
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (subname)
|
||||
free(subname);
|
||||
subname = strdup((char *)dbuf.mv_data + STRLENOF("database="));
|
||||
} else if (!strncmp(dbuf.mv_data, "type=", STRLENOF("type="))) {
|
||||
if (strncmp((char *)dbuf.mv_data + STRLENOF("type="), "btree",
|
||||
STRLENOF("btree"))) {
|
||||
fprintf(stderr, "%s: line %zd: unsupported type %s\n", prog, lineno,
|
||||
(char *)dbuf.mv_data + STRLENOF("type="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "mapaddr=", STRLENOF("mapaddr="))) {
|
||||
int i;
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
i = sscanf((char *)dbuf.mv_data + STRLENOF("mapaddr="), "%p",
|
||||
&info.me_mapaddr);
|
||||
if (i != 1) {
|
||||
fprintf(stderr, "%s: line %zd: invalid mapaddr %s\n", prog, lineno,
|
||||
(char *)dbuf.mv_data + STRLENOF("mapaddr="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "mapsize=", STRLENOF("mapsize="))) {
|
||||
int i;
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
i = sscanf((char *)dbuf.mv_data + STRLENOF("mapsize="), "%zu",
|
||||
&info.me_mapsize);
|
||||
if (i != 1) {
|
||||
fprintf(stderr, "%s: line %zd: invalid mapsize %s\n", prog, lineno,
|
||||
(char *)dbuf.mv_data + STRLENOF("mapsize="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "maxreaders=", STRLENOF("maxreaders="))) {
|
||||
int i;
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
i = sscanf((char *)dbuf.mv_data + STRLENOF("maxreaders="), "%u",
|
||||
&info.me_maxreaders);
|
||||
if (i != 1) {
|
||||
fprintf(stderr, "%s: line %zd: invalid maxreaders %s\n", prog, lineno,
|
||||
(char *)dbuf.mv_data + STRLENOF("maxreaders="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; dbflags[i].bit; i++) {
|
||||
if (!strncmp(dbuf.mv_data, dbflags[i].name, dbflags[i].len) &&
|
||||
((char *)dbuf.mv_data)[dbflags[i].len] == '=') {
|
||||
if (((char *)dbuf.mv_data)[dbflags[i].len + 1] == '1')
|
||||
dbi_flags |= dbflags[i].bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dbflags[i].bit) {
|
||||
ptr = memchr(dbuf.mv_data, '=', dbuf.mv_size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "%s: line %zd: unexpected format\n", prog, lineno);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
*ptr = '\0';
|
||||
fprintf(stderr, "%s: line %zd: unrecognized keyword ignored: %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void badend(void) {
|
||||
fprintf(stderr, "%s: line %zd: unexpected end of input\n", prog, lineno);
|
||||
}
|
||||
|
||||
static int unhex(unsigned char *c2) {
|
||||
int x, c;
|
||||
x = *c2++ & 0x4f;
|
||||
if (x & 0x40)
|
||||
x -= 55;
|
||||
c = x << 4;
|
||||
x = *c2 & 0x4f;
|
||||
if (x & 0x40)
|
||||
x -= 55;
|
||||
c |= x;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int readline(MDB_val *out, MDB_val *buf) {
|
||||
unsigned char *c1, *c2, *end;
|
||||
size_t len, l2;
|
||||
int c;
|
||||
|
||||
if (!(mode & NOHDR)) {
|
||||
c = fgetc(stdin);
|
||||
if (c == EOF) {
|
||||
Eof = 1;
|
||||
return EOF;
|
||||
}
|
||||
if (c != ' ') {
|
||||
lineno++;
|
||||
if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
|
||||
badend:
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
if (c == 'D' && !strncmp(buf->mv_data, "ATA=END", STRLENOF("ATA=END")))
|
||||
return EOF;
|
||||
goto badend;
|
||||
}
|
||||
}
|
||||
if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
|
||||
Eof = 1;
|
||||
return EOF;
|
||||
}
|
||||
lineno++;
|
||||
|
||||
c1 = buf->mv_data;
|
||||
len = strlen((char *)c1);
|
||||
l2 = len;
|
||||
|
||||
/* Is buffer too short? */
|
||||
while (c1[len - 1] != '\n') {
|
||||
buf->mv_data = realloc(buf->mv_data, buf->mv_size * 2);
|
||||
if (!buf->mv_data) {
|
||||
Eof = 1;
|
||||
fprintf(stderr, "%s: line %zd: out of memory, line too long\n", prog,
|
||||
lineno);
|
||||
return EOF;
|
||||
}
|
||||
c1 = buf->mv_data;
|
||||
c1 += l2;
|
||||
if (fgets((char *)c1, buf->mv_size + 1, stdin) == NULL) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
buf->mv_size *= 2;
|
||||
len = strlen((char *)c1);
|
||||
l2 += len;
|
||||
}
|
||||
c1 = c2 = buf->mv_data;
|
||||
len = l2;
|
||||
c1[--len] = '\0';
|
||||
end = c1 + len;
|
||||
|
||||
if (mode & PRINT) {
|
||||
while (c2 < end) {
|
||||
if (*c2 == '\\') {
|
||||
if (c2[1] == '\\') {
|
||||
c1++;
|
||||
c2 += 2;
|
||||
} else {
|
||||
if (c2 + 3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
*c1++ = unhex(++c2);
|
||||
c2 += 2;
|
||||
}
|
||||
} else {
|
||||
/* copies are redundant when no escapes were used */
|
||||
*c1++ = *c2++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* odd length not allowed */
|
||||
if (len & 1) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
while (c2 < end) {
|
||||
if (!isxdigit(*c2) || !isxdigit(c2[1])) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
*c1++ = unhex(c2);
|
||||
c2 += 2;
|
||||
}
|
||||
}
|
||||
c2 = out->mv_data = buf->mv_data;
|
||||
out->mv_size = c1 - c2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "usage: %s [-V] [-f input] [-n] [-s name] [-N] [-T] dbpath\n",
|
||||
prog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
MDB_env *env;
|
||||
MDB_txn *txn;
|
||||
MDB_cursor *mc;
|
||||
MDB_dbi dbi;
|
||||
char *envname;
|
||||
int envflags = 0, putflags = 0;
|
||||
|
||||
prog = argv[0];
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
}
|
||||
|
||||
/* -f: load file instead of stdin
|
||||
* -n: use NOSUBDIR flag on env_open
|
||||
* -s: load into named subDB
|
||||
* -N: use NOOVERWRITE on puts
|
||||
* -T: read plaintext
|
||||
* -V: print version and exit
|
||||
*/
|
||||
while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) {
|
||||
switch (i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
break;
|
||||
case 'f':
|
||||
if (freopen(optarg, "r", stdin) == NULL) {
|
||||
fprintf(stderr, "%s: %s: reopen: %s\n", prog, optarg, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 's':
|
||||
subname = strdup(optarg);
|
||||
break;
|
||||
case 'N':
|
||||
putflags = MDB_NOOVERWRITE | MDB_NODUPDATA;
|
||||
break;
|
||||
case 'T':
|
||||
mode |= NOHDR | PRINT;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage();
|
||||
|
||||
dbuf.mv_size = 4096;
|
||||
dbuf.mv_data = malloc(dbuf.mv_size);
|
||||
|
||||
if (!(mode & NOHDR))
|
||||
readhdr();
|
||||
|
||||
envname = argv[optind];
|
||||
rc = mdbx_env_create(&env);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mdbx_env_set_maxdbs(env, 2);
|
||||
|
||||
if (info.me_maxreaders)
|
||||
mdbx_env_set_maxreaders(env, info.me_maxreaders);
|
||||
|
||||
if (info.me_mapsize)
|
||||
mdbx_env_set_mapsize(env, info.me_mapsize);
|
||||
|
||||
if (info.me_mapaddr)
|
||||
envflags |= MDB_FIXEDMAP;
|
||||
|
||||
rc = mdbx_env_open(env, envname, envflags, 0664);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
kbuf.mv_size = mdbx_env_get_maxkeysize(env) * 2 + 2;
|
||||
kbuf.mv_data = malloc(kbuf.mv_size);
|
||||
|
||||
while (!Eof) {
|
||||
MDB_val key, data;
|
||||
int batch = 0;
|
||||
|
||||
rc = mdbx_txn_begin(env, NULL, 0, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdbx_dbi_open(txn, subname, dbi_flags | MDB_CREATE, &dbi);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
rc = mdbx_cursor_open(txn, dbi, &mc);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
rc = readline(&key, &kbuf);
|
||||
if (rc) /* rc == EOF */
|
||||
break;
|
||||
|
||||
rc = readline(&data, &dbuf);
|
||||
if (rc) {
|
||||
fprintf(stderr, "%s: line %zd: failed to read key value\n", prog,
|
||||
lineno);
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
rc = mdbx_cursor_put(mc, &key, &data, putflags);
|
||||
if (rc == MDB_KEYEXIST && putflags)
|
||||
continue;
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_cursor_put failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
batch++;
|
||||
if (batch == 100) {
|
||||
rc = mdbx_txn_commit(txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "%s: line %zd: txn_commit: %s\n", prog, lineno,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
rc = mdbx_txn_begin(env, NULL, 0, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
rc = mdbx_cursor_open(txn, dbi, &mc);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
batch = 0;
|
||||
}
|
||||
}
|
||||
rc = mdbx_txn_commit(txn);
|
||||
txn = NULL;
|
||||
if (rc) {
|
||||
fprintf(stderr, "%s: line %zd: txn_commit: %s\n", prog, lineno,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
mdbx_dbi_close(env, dbi);
|
||||
if (!(mode & NOHDR))
|
||||
readhdr();
|
||||
}
|
||||
|
||||
txn_abort:
|
||||
mdbx_txn_abort(txn);
|
||||
env_close:
|
||||
mdbx_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
66
src/mdbx_stat.1
Normal file
66
src/mdbx_stat.1
Normal file
@@ -0,0 +1,66 @@
|
||||
.\" Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
.\" Copyright 2012-2017 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.TH MDB_STAT 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.SH NAME
|
||||
mdbx_stat \- LMDB environment status tool
|
||||
.SH SYNOPSIS
|
||||
.B mdbx_stat
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BR \-e ]
|
||||
[\c
|
||||
.BR \-f [ f [ f ]]]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
[\c
|
||||
.BR \-r [ r ]]
|
||||
[\c
|
||||
.BR \-a \ |
|
||||
.BI \-s \ subdb\fR]
|
||||
.BR \ envpath
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdbx_stat
|
||||
utility displays the status of an LMDB environment.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-e
|
||||
Display information about the database environment.
|
||||
.TP
|
||||
.BR \-f
|
||||
Display information about the environment freelist.
|
||||
If \fB\-ff\fP is given, summarize each freelist entry.
|
||||
If \fB\-fff\fP is given, display the full list of page IDs in the freelist.
|
||||
.TP
|
||||
.BR \-n
|
||||
Display the status of an LMDB database which does not use subdirectories.
|
||||
.TP
|
||||
.BR \-r
|
||||
Display information about the environment reader table.
|
||||
Shows the process ID, thread ID, and transaction ID for each active
|
||||
reader slot. The process ID and transaction ID are in decimal, the
|
||||
thread ID is in hexadecimal. The transaction ID is displayed as "-"
|
||||
if the reader does not currently have a read transaction open.
|
||||
If \fB\-rr\fP is given, check for stale entries in the reader
|
||||
table and clear them. The reader table will be printed again
|
||||
after the check is performed.
|
||||
.TP
|
||||
.BR \-a
|
||||
Display the status of all of the subdatabases in the environment.
|
||||
.TP
|
||||
.BR \-s \ subdb
|
||||
Display the status of a specific subdatabase.
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
.SH "SEE ALSO"
|
||||
.BR mdbx_copy (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
306
src/mdbx_stat.c
Normal file
306
src/mdbx_stat.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/* mdbx_stat.c - memory-mapped database status tool */
|
||||
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2011-2017 Howard Chu, Symas Corp.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include "mdbx.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void prstat(MDBX_stat *ms) {
|
||||
printf(" Page size: %u\n", ms->ms_psize);
|
||||
printf(" Tree depth: %u\n", ms->ms_depth);
|
||||
printf(" Branch pages: %zu\n", ms->ms_branch_pages);
|
||||
printf(" Leaf pages: %zu\n", ms->ms_leaf_pages);
|
||||
printf(" Overflow pages: %zu\n", ms->ms_overflow_pages);
|
||||
printf(" Entries: %zu\n", ms->ms_entries);
|
||||
}
|
||||
|
||||
static void usage(char *prog) {
|
||||
fprintf(stderr,
|
||||
"usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n",
|
||||
prog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, rc;
|
||||
MDB_env *env;
|
||||
MDB_txn *txn;
|
||||
MDB_dbi dbi;
|
||||
MDBX_stat mst;
|
||||
MDBX_envinfo mei;
|
||||
char *prog = argv[0];
|
||||
char *envname;
|
||||
char *subname = NULL;
|
||||
int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
usage(prog);
|
||||
}
|
||||
|
||||
/* -a: print stat of main DB and all subDBs
|
||||
* -s: print stat of only the named subDB
|
||||
* -e: print env info
|
||||
* -f: print freelist info
|
||||
* -r: print reader info
|
||||
* -n: use NOSUBDIR flag on env_open
|
||||
* -V: print version and exit
|
||||
* (default) print stat of only the main DB
|
||||
*/
|
||||
while ((i = getopt(argc, argv, "Vaefnrs:")) != EOF) {
|
||||
switch (i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
if (subname)
|
||||
usage(prog);
|
||||
alldbs++;
|
||||
break;
|
||||
case 'e':
|
||||
envinfo++;
|
||||
break;
|
||||
case 'f':
|
||||
freinfo++;
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 'r':
|
||||
rdrinfo++;
|
||||
break;
|
||||
case 's':
|
||||
if (alldbs)
|
||||
usage(prog);
|
||||
subname = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(prog);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage(prog);
|
||||
|
||||
envname = argv[optind];
|
||||
rc = mdbx_env_create(&env);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_env_create failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (alldbs || subname) {
|
||||
mdbx_env_set_maxdbs(env, 4);
|
||||
}
|
||||
|
||||
rc = mdbx_env_open(env, envname, envflags | MDB_RDONLY, 0664);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_env_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
if (envinfo) {
|
||||
(void)mdbx_env_stat(env, &mst, sizeof(mst));
|
||||
(void)mdbx_env_info(env, &mei, sizeof(mei));
|
||||
printf("Environment Info\n");
|
||||
printf(" Map address: %p\n", mei.me_mapaddr);
|
||||
printf(" Map size: %zu\n", mei.me_mapsize);
|
||||
printf(" Page size: %u\n", mst.ms_psize);
|
||||
printf(" Max pages: %zu\n", mei.me_mapsize / mst.ms_psize);
|
||||
printf(" Number of pages used: %zu\n", mei.me_last_pgno + 1);
|
||||
printf(" Last transaction ID: %zu\n", mei.me_last_txnid);
|
||||
printf(" Tail transaction ID: %zu (%zi)\n", mei.me_tail_txnid,
|
||||
mei.me_tail_txnid - mei.me_last_txnid);
|
||||
printf(" Max readers: %u\n", mei.me_maxreaders);
|
||||
printf(" Number of readers used: %u\n", mei.me_numreaders);
|
||||
} else {
|
||||
/* LY: zap warnings from gcc */
|
||||
memset(&mst, 0, sizeof(mst));
|
||||
memset(&mei, 0, sizeof(mei));
|
||||
}
|
||||
|
||||
if (rdrinfo) {
|
||||
printf("Reader Table Status\n");
|
||||
rc = mdbx_reader_list(env, (MDB_msg_func *)fputs, stdout);
|
||||
if (rdrinfo > 1) {
|
||||
int dead;
|
||||
mdbx_reader_check(env, &dead);
|
||||
printf(" %d stale readers cleared.\n", dead);
|
||||
rc = mdbx_reader_list(env, (MDB_msg_func *)fputs, stdout);
|
||||
}
|
||||
if (!(subname || alldbs || freinfo))
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdbx_txn_begin(env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_txn_begin failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
if (freinfo) {
|
||||
MDB_cursor *cursor;
|
||||
MDB_val key, data;
|
||||
size_t pages = 0, *iptr;
|
||||
size_t reclaimable = 0;
|
||||
|
||||
printf("Freelist Status\n");
|
||||
dbi = 0;
|
||||
rc = mdbx_cursor_open(txn, dbi, &cursor);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
rc = mdbx_stat(txn, dbi, &mst, sizeof(mst));
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
prstat(&mst);
|
||||
while ((rc = mdbx_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
iptr = data.mv_data;
|
||||
pages += *iptr;
|
||||
if (envinfo && mei.me_tail_txnid > *(size_t *)key.mv_data)
|
||||
reclaimable += *iptr;
|
||||
if (freinfo > 1) {
|
||||
char *bad = "";
|
||||
size_t pg, prev;
|
||||
ssize_t i, j, span = 0;
|
||||
j = *iptr++;
|
||||
for (i = j, prev = 1; --i >= 0;) {
|
||||
pg = iptr[i];
|
||||
if (pg <= prev)
|
||||
bad = " [bad sequence]";
|
||||
prev = pg;
|
||||
pg += span;
|
||||
for (; i >= span && iptr[i - span] == pg; span++, pg++)
|
||||
;
|
||||
}
|
||||
printf(" Transaction %zu, %zd pages, maxspan %zd%s\n",
|
||||
*(size_t *)key.mv_data, j, span, bad);
|
||||
if (freinfo > 2) {
|
||||
for (--j; j >= 0;) {
|
||||
pg = iptr[j];
|
||||
for (span = 1; --j >= 0 && iptr[j] == pg + span; span++)
|
||||
;
|
||||
if (span > 1)
|
||||
printf(" %9zu[%zd]\n", pg, span);
|
||||
else
|
||||
printf(" %9zu\n", pg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mdbx_cursor_close(cursor);
|
||||
if (envinfo) {
|
||||
size_t value = mei.me_mapsize / mst.ms_psize;
|
||||
double percent = value / 100.0;
|
||||
printf("Page Allocation Info\n");
|
||||
printf(" Max pages: %9zu 100%%\n", value);
|
||||
|
||||
value = mei.me_last_pgno + 1;
|
||||
printf(" Number of pages used: %zu %.1f%%\n", value, value / percent);
|
||||
|
||||
value = mei.me_mapsize / mst.ms_psize - (mei.me_last_pgno + 1);
|
||||
printf(" Remained: %zu %.1f%%\n", value, value / percent);
|
||||
|
||||
value = mei.me_last_pgno + 1 - pages;
|
||||
printf(" Used now: %zu %.1f%%\n", value, value / percent);
|
||||
|
||||
value = pages;
|
||||
printf(" Unallocated: %zu %.1f%%\n", value, value / percent);
|
||||
|
||||
value = pages - reclaimable;
|
||||
printf(" Detained: %zu %.1f%%\n", value, value / percent);
|
||||
|
||||
value = reclaimable;
|
||||
printf(" Reclaimable: %zu %.1f%%\n", value, value / percent);
|
||||
|
||||
value =
|
||||
mei.me_mapsize / mst.ms_psize - (mei.me_last_pgno + 1) + reclaimable;
|
||||
printf(" Available: %zu %.1f%%\n", value, value / percent);
|
||||
} else
|
||||
printf(" Free pages: %zu\n", pages);
|
||||
}
|
||||
|
||||
rc = mdbx_dbi_open(txn, subname, 0, &dbi);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_open failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
rc = mdbx_stat(txn, dbi, &mst, sizeof(mst));
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_stat failed, error %d %s\n", rc, mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
printf("Status of %s\n", subname ? subname : "Main DB");
|
||||
prstat(&mst);
|
||||
|
||||
if (alldbs) {
|
||||
MDB_cursor *cursor;
|
||||
MDB_val key;
|
||||
|
||||
rc = mdbx_cursor_open(txn, dbi, &cursor);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_cursor_open failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
while ((rc = mdbx_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
|
||||
char *str;
|
||||
MDB_dbi db2;
|
||||
if (memchr(key.mv_data, '\0', key.mv_size))
|
||||
continue;
|
||||
str = malloc(key.mv_size + 1);
|
||||
memcpy(str, key.mv_data, key.mv_size);
|
||||
str[key.mv_size] = '\0';
|
||||
rc = mdbx_dbi_open(txn, str, 0, &db2);
|
||||
if (rc == MDB_SUCCESS)
|
||||
printf("Status of %s\n", str);
|
||||
free(str);
|
||||
if (rc)
|
||||
continue;
|
||||
rc = mdbx_stat(txn, db2, &mst, sizeof(mst));
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdbx_stat failed, error %d %s\n", rc,
|
||||
mdbx_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
prstat(&mst);
|
||||
mdbx_dbi_close(env, db2);
|
||||
}
|
||||
mdbx_cursor_close(cursor);
|
||||
}
|
||||
|
||||
if (rc == MDB_NOTFOUND)
|
||||
rc = MDB_SUCCESS;
|
||||
|
||||
mdbx_dbi_close(env, dbi);
|
||||
txn_abort:
|
||||
mdbx_txn_abort(txn);
|
||||
env_close:
|
||||
mdbx_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
53
src/midl.h
Normal file
53
src/midl.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/** A generic unsigned ID number. These were entryIDs in back-bdb.
|
||||
* Preferably it should have the same size as a pointer.
|
||||
*/
|
||||
typedef size_t MDB_ID;
|
||||
|
||||
/** An IDL is an ID List, a sorted array of IDs. The first
|
||||
* element of the array is a counter for how many actual
|
||||
* IDs are in the list. In the original back-bdb code, IDLs are
|
||||
* sorted in ascending order. For libmdb IDLs are sorted in
|
||||
* descending order.
|
||||
*/
|
||||
typedef MDB_ID *MDB_IDL;
|
||||
|
||||
/* IDL sizes - likely should be even bigger
|
||||
* limiting factors: sizeof(ID), thread stack size
|
||||
*/
|
||||
#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
|
||||
#define MDB_IDL_DB_SIZE (1 << MDB_IDL_LOGN)
|
||||
#define MDB_IDL_UM_SIZE (1 << (MDB_IDL_LOGN + 1))
|
||||
|
||||
#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE - 1)
|
||||
#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE - 1)
|
||||
|
||||
#define MDB_IDL_SIZEOF(ids) (((ids)[0] + 1) * sizeof(MDB_ID))
|
||||
#define MDB_IDL_IS_ZERO(ids) ((ids)[0] == 0)
|
||||
#define MDB_IDL_CPY(dst, src) (memcpy(dst, src, MDB_IDL_SIZEOF(src)))
|
||||
#define MDB_IDL_FIRST(ids) ((ids)[1])
|
||||
#define MDB_IDL_LAST(ids) ((ids)[(ids)[0]])
|
||||
|
||||
/** Current max length of an #mdbx_midl_alloc()ed IDL */
|
||||
#define MDB_IDL_ALLOCLEN(ids) ((ids)[-1])
|
||||
|
||||
/** Append ID to IDL. The IDL must be big enough. */
|
||||
#define mdbx_midl_xappend(idl, id) \
|
||||
do { \
|
||||
MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \
|
||||
xidl[xlen] = (id); \
|
||||
} while (0)
|
||||
|
||||
/** An ID2 is an ID/pointer pair.
|
||||
*/
|
||||
typedef struct MDB_ID2 {
|
||||
MDB_ID mid; /**< The ID */
|
||||
void *mptr; /**< The pointer */
|
||||
} MDB_ID2;
|
||||
|
||||
/** An ID2L is an ID2 List, a sorted array of ID2s.
|
||||
* The first element's \b mid member is a count of how many actual
|
||||
* elements are in the array. The \b mptr member of the first element is
|
||||
* unused.
|
||||
* The array is sorted in ascending order by \b mid.
|
||||
*/
|
||||
typedef MDB_ID2 *MDB_ID2L;
|
||||
236
src/reopen.h
Normal file
236
src/reopen.h
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>.
|
||||
* Copyright 2015,2016 Peter-Service R&D LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
/* *INDENT-OFF* */
|
||||
/* clang-format off */
|
||||
|
||||
#ifndef __CLANG_PREREQ
|
||||
# ifdef __clang__
|
||||
# define __CLANG_PREREQ(maj,min) \
|
||||
((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))
|
||||
# else
|
||||
# define __CLANG_PREREQ(maj,min) (0)
|
||||
# endif
|
||||
#endif /* __CLANG_PREREQ */
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) (0)
|
||||
#endif
|
||||
|
||||
#if !defined(__thread) && (defined(_MSC_VER) || defined(__DMC__))
|
||||
# define __thread __declspec(thread)
|
||||
#endif
|
||||
|
||||
#ifndef __forceinline
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __forceinline __inline __attribute__((always_inline))
|
||||
# elif ! defined(_MSC_VER)
|
||||
# define __forceinline
|
||||
# endif
|
||||
#endif /* __forceinline */
|
||||
|
||||
#ifndef __noinline
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __noinline __attribute__((noinline))
|
||||
# elif defined(_MSC_VER)
|
||||
# define __noinline __declspec(noinline)
|
||||
# endif
|
||||
#endif /* __noinline */
|
||||
|
||||
#ifndef __must_check_result
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __must_check_result __attribute__((warn_unused_result))
|
||||
# else
|
||||
# define __must_check_result
|
||||
# endif
|
||||
#endif /* __must_check_result */
|
||||
|
||||
#ifndef __hot
|
||||
# if defined(__OPTIMIZE__) && (defined(__GNUC__) && !defined(__clang__))
|
||||
# define __hot __attribute__((hot, optimize("O3")))
|
||||
# elif defined(__GNUC__)
|
||||
/* cland case, just put frequently used functions in separate section */
|
||||
# define __hot __attribute__((section("text.hot")))
|
||||
# else
|
||||
# define __hot
|
||||
# endif
|
||||
#endif /* __hot */
|
||||
|
||||
#ifndef __cold
|
||||
# if defined(__OPTIMIZE__) && (defined(__GNUC__) && !defined(__clang__))
|
||||
# define __cold __attribute__((cold, optimize("Os")))
|
||||
# elif defined(__GNUC__)
|
||||
/* cland case, just put infrequently used functions in separate section */
|
||||
# define __cold __attribute__((section("text.unlikely")))
|
||||
# else
|
||||
# define __cold
|
||||
# endif
|
||||
#endif /* __cold */
|
||||
|
||||
#ifndef __flatten
|
||||
# if defined(__OPTIMIZE__) && (defined(__GNUC__) || defined(__clang__))
|
||||
# define __flatten __attribute__((flatten))
|
||||
# else
|
||||
# define __flatten
|
||||
# endif
|
||||
#endif /* __flatten */
|
||||
|
||||
#ifndef __aligned
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __aligned(N) __attribute__((aligned(N)))
|
||||
# elif defined(__MSC_VER)
|
||||
# define __aligned(N) __declspec(align(N))
|
||||
# else
|
||||
# define __aligned(N)
|
||||
# endif
|
||||
#endif /* __align */
|
||||
|
||||
#ifndef __noreturn
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __noreturn __attribute__((noreturn))
|
||||
# elif defined(__MSC_VER)
|
||||
# define __noreturn __declspec(noreturn)
|
||||
# else
|
||||
# define __noreturn
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __nothrow
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __nothrow __attribute__((nothrow))
|
||||
# elif defined(__MSC_VER)
|
||||
# define __nothrow __declspec(nothrow)
|
||||
# else
|
||||
# define __nothrow
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CACHELINE_SIZE
|
||||
# if defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
# define CACHELINE_SIZE 128
|
||||
# else
|
||||
# define CACHELINE_SIZE 64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __cache_aligned
|
||||
# define __cache_aligned __aligned(CACHELINE_SIZE)
|
||||
#endif
|
||||
|
||||
#ifndef likely
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# ifdef __cplusplus
|
||||
/* LY: workaround for "pretty" boost */
|
||||
static __inline __attribute__((always_inline))
|
||||
bool likely(bool cond) { return __builtin_expect(cond, 1); }
|
||||
# else
|
||||
# define likely(cond) __builtin_expect(!!(cond), 1)
|
||||
# endif
|
||||
# else
|
||||
# define likely(x) (x)
|
||||
# endif
|
||||
#endif /* likely */
|
||||
|
||||
#ifndef unlikely
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# ifdef __cplusplus
|
||||
/* LY: workaround for "pretty" boost */
|
||||
static __inline __attribute__((always_inline))
|
||||
bool unlikely(bool cond) { return __builtin_expect(cond, 0); }
|
||||
# else
|
||||
# define unlikely(cond) __builtin_expect(!!(cond), 0)
|
||||
# endif
|
||||
# else
|
||||
# define unlikely(x) (x)
|
||||
# endif
|
||||
#endif /* unlikely */
|
||||
|
||||
#ifndef __extern_C
|
||||
# ifdef __cplusplus
|
||||
# define __extern_C extern "C"
|
||||
# else
|
||||
# define __extern_C
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __noop
|
||||
# define __noop() do {} while (0)
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* Prototype should match libc runtime. ISO POSIX (2003) & LSB 3.1 */
|
||||
__extern_C void __assert_fail(
|
||||
const char* assertion,
|
||||
const char* file,
|
||||
unsigned line,
|
||||
const char* function) __nothrow __noreturn;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#if defined(HAVE_VALGRIND) || defined(USE_VALGRIND)
|
||||
/* Get debugging help from Valgrind */
|
||||
# include <valgrind/memcheck.h>
|
||||
# ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
|
||||
/* LY: available since Valgrind 3.10 */
|
||||
# define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# endif
|
||||
#else
|
||||
# define VALGRIND_CREATE_MEMPOOL(h,r,z)
|
||||
# define VALGRIND_DESTROY_MEMPOOL(h)
|
||||
# define VALGRIND_MEMPOOL_TRIM(h,a,s)
|
||||
# define VALGRIND_MEMPOOL_ALLOC(h,a,s)
|
||||
# define VALGRIND_MEMPOOL_FREE(h,a)
|
||||
# define VALGRIND_MEMPOOL_CHANGE(h,a,b,s)
|
||||
# define VALGRIND_MAKE_MEM_NOACCESS(a,s)
|
||||
# define VALGRIND_MAKE_MEM_DEFINED(a,s)
|
||||
# define VALGRIND_MAKE_MEM_UNDEFINED(a,s)
|
||||
# define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a,s) (0)
|
||||
# define VALGRIND_CHECK_MEM_IS_DEFINED(a,s) (0)
|
||||
#endif /* ! USE_VALGRIND */
|
||||
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(thread_sanitizer)
|
||||
# define __SANITIZE_THREAD__ 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __SANITIZE_THREAD__
|
||||
# define ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread, noinline))
|
||||
#else
|
||||
# define ATTRIBUTE_NO_SANITIZE_THREAD
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define __SANITIZE_ADDRESS__ 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
# include <sanitizer/asan_interface.h>
|
||||
# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address, noinline))
|
||||
#else
|
||||
# define ASAN_POISON_MEMORY_REGION(addr, size) \
|
||||
((void)(addr), (void)(size))
|
||||
# define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
|
||||
((void)(addr), (void)(size))
|
||||
# define ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
#endif /* __SANITIZE_ADDRESS__ */
|
||||
Reference in New Issue
Block a user