Implementing Big Integers?

2 views
Skip to first unread message

Ethin Probst

unread,
Apr 17, 2018, 9:08:45 AM4/17/18
to MOO Talk
So, I decided tonight to try and implement a BIGINT type into MOO with
Stunt, primarily as an experiment. It works (sorta); at least, it compiles. And while ;BIGINT
gives '13', ';tobigint(30)' returns 'E_TYPE'. Not sure where I went
wrong so thought I'd ask. :) Here's what I've done, based off of
Adding New MOO Types.txt. I'll paste the steps, then what I did.
For all new types, both visible and invisible, you must:
-- add an item *at the end* of the `var_type' enumeration in structures.h
// After including <boost/multiprecision/cpp_int.hpp>, right after _TYPE_ANON:
    TYPE_BIGINT, /* Big integer, user-visible */
-- perhaps add a clause to the union in `struct Var' in structures.h
// New struct Var {
struct Var {
    union {
        const char *str;        /* STR */
        int32 num;              /* NUM, CATCH, FINALLY */
        Objid obj;              /* OBJ */
        enum error err;         /* ERR */
        Var *list;              /* LIST */
        rbtree *tree;           /* MAP */
        rbtrav *trav;           /* ITER */
        double *fnum;           /* FLOAT */
        Object *anon;           /* ANON */
        boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>> *bignum; /* Big integer */
    } v;
//...
};
(Did I go overboard with the min/max bits? Perhaps that's why?)
-- add a case to each of dbio_read_var() and dbio_write_var(), in db_io.c
// db_io.cc:
// Headers
#include "my-ctype.h"
#include <float.h>
#include "my-stdarg.h"
#include "my-stdio.h"
#include "my-stdlib.h"
#include "db.h"
#include "db_io.h"
#include "db_private.h"
#include "list.h"
#include "log.h"
#include "map.h"
#include "numbers.h"
#include "parser.h"
#include "server.h"
#include "storage.h"
#include "streams.h"
#include "structures.h"
#include "str_intern.h"
#include "unparse.h"
#include "version.h"
#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
#include <iterator>
// Dbio_read_var():
Var
dbio_read_var(void)
{
    Var r;
    int i, l = dbio_read_num();

    if (l == (int) TYPE_ANY && dbio_input_version == DBV_Prehistory)
        l = TYPE_NONE;          /* Old encoding for VM's empty temp register
                                 * and any as-yet unassigned variables.
                                 */
    r.type = (var_type) l;
    switch (l) {
    case TYPE_CLEAR:
    case TYPE_NONE:
        break;
    case _TYPE_STR:
        r.v.str = dbio_read_string_intern();
        r.type = TYPE_STR;
        break;
    case TYPE_OBJ:
    case TYPE_ERR:
    case TYPE_INT:
    case TYPE_CATCH:
    case TYPE_FINALLY:
        r.v.num = dbio_read_num();
        break;
    case _TYPE_FLOAT:
        r = new_float(dbio_read_float());
        break;
    case _TYPE_MAP:
        l = dbio_read_num();
        r = new_map();
        for (i = 0; i < l; i++) {
            Var key, value;
            key = dbio_read_var();
            value = dbio_read_var();
            r = mapinsert(r, key, value);
        }
        break;
    case _TYPE_LIST:
        l = dbio_read_num();
        r = new_list(l);
        for (i = 0; i < l; i++)
            r.v.list[i + 1] = dbio_read_var();
        break;
    case _TYPE_ITER:
        r = dbio_read_var();
        break;
    case _TYPE_ANON:
        r = db_read_anonymous();
        break;
        case TYPE_BIGINT: {
                r.type = TYPE_BIGINT;
                auto num = dbio_read_bignum();
                r.v.bignum = &num;
                }
                break;
    default:
        errlog("DBIO_READ_VAR: Unknown type (%d) at DB file pos. %ld\n",
               l, ftell(input));
        r = zero;
        break;
    }
    return r;
}
// dbio_write_var():
void
dbio_write_var(Var v)
{
    int i;

    /* don't write out the iterator */
    if (v.type == TYPE_ITER) {
        var_pair pair;
        iterget(v, &pair)
            ? dbio_write_var(pair.a)
            : dbio_write_var(clear);
        return;
    }

    dbio_write_num((int) v.type & TYPE_DB_MASK);

    switch ((int) v.type) {
    case TYPE_CLEAR:
    case TYPE_NONE:
        break;
    case TYPE_STR:
        dbio_write_string(v.v.str);
        break;
    case TYPE_OBJ:
    case TYPE_ERR:
    case TYPE_INT:
    case TYPE_CATCH:
    case TYPE_FINALLY:
        dbio_write_num(v.v.num);
        break;
    case TYPE_FLOAT:
        dbio_write_float(*v.v.fnum);
        break;
    case TYPE_MAP:
        dbio_write_num(maplength(v));
        mapforeach(v, dbio_write_map, NULL);
        break;
    case TYPE_LIST:
        dbio_write_num(v.v.list[0].v.num);
        for (i = 0; i < v.v.list[0].v.num; i++)
            dbio_write_var(v.v.list[i + 1]);
        break;
    case TYPE_ANON:
        db_write_anonymous(v);
        break;
        case TYPE_BIGINT:
        dbio_write_bignum(v.v.bignum);
    default:
        errlog("DBIO_WRITE_VAR: Unknown type (%d)\n", (int)v.type);
        break;
    }
}
// dbio_read_bignum():
boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>>
dbio_read_bignum(void)
{
std::vector<char> s;
    boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>> i;
    // Read until \n found.
int ch;
while ((ch = fgetc(input)) != '\n') {
s.push_back(ch);
}
    try {
    i = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>> (std::string(s.begin(),
s.end()));
    } catch (std::runtime_error &error) {
    errlog("DBIO_READ_BIGNUM: Error reading bigint: %s\n", error.what());
    }
    return i;
}
// dbio_write_bignum():
void
dbio_write_bignum(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>>* n)
{
std::vector<char> data;
boost::multiprecision::export_bits(*n, std::back_inserter(data),
boost::multiprecision::signed_magnitude,
boost::multiprecision::checked);
if (data.size()>0) {
    dbio_printf("%s\n", std::string(data.begin(), data.end()).c_str());
    } else {
    errlog("DBIO_WRITE_BIGNUM: Cannot write bignum! Database may be
unstable!\n");
    }
}
-- if you're adding a refcounted type, modify var_refcount() and
   refcount_overhead() in storage.c
(I didn't do this because INTs aren't refcounted, so neither should
bignums... correct me if they should be.)
-- also add a case to each of complex_free_var(), complex_var_ref(),
   and complex_var_dup() in utils.c
(Again, same reason above.)

In addition, for visible types, you must:
-- consider adding a new built-in MOO variable like OBJ, STR, and LIST:
        -- add a new SLOT_XXX constant *at the end* of the list in sym_table.h
// New slots:
/* Environment slots for built-in variables */
#define SLOT_NUM        0
#define SLOT_OBJ        1
#define SLOT_STR        2
#define SLOT_LIST       3
#define SLOT_ERR        4
#define SLOT_PLAYER     5
#define SLOT_THIS       6
#define SLOT_CALLER     7
#define SLOT_VERB       8
#define SLOT_ARGS       9
#define SLOT_ARGSTR     10
#define SLOT_DOBJ       11
#define SLOT_DOBJSTR    12
#define SLOT_PREPSTR    13
#define SLOT_IOBJ       14
#define SLOT_IOBJSTR    15

/* Added in DBV_Float: */
#define SLOT_INT        16
#define SLOT_FLOAT      17

/* Added in DBV_Map: */
#define SLOT_MAP        18

/* Added in DBV_Anon */
#define SLOT_ANON       19

/* Added in DBV_BIGINT */
#define SLOT_BIGINT     20
        -- add a new DB version number *just before the end* of the
           `DB_Version' enumeration in version.h
// New db_version enumeration:
/* The following list must never be reordered, only appended to.  There is one
 * element per version of the database format (including incompatible changes
 * to the language, such as the addition of new keywords).  The integer value
 * of each element is used in the DB header on disk to identify the format
 * version in use in that file.
 */
typedef enum {
    DBV_Prehistory,             /* Before format versions */
    DBV_Exceptions,             /* Addition of the `try', `except', `finally',
                                 * and `endtry' keywords.
                                 */
    DBV_BreakCont,              /* Addition of the `break' and `continue'
                                 * keywords.
                                 */
    DBV_Float,                  /* Addition of `FLOAT' and `INT' variables and
                                 * the `E_FLOAT' keyword, along with version
                                 * numbers on each frame of a suspended task.
                                 */
    DBV_BFBugFixed,             /* Bug in built-in function overrides fixed by
                                 * making it use tail-calling.  This DB_Version
                                 * change exists solely to turn off special
                                 * bug handling in read_bi_func_data().
                                 */
    DBV_NextGen,                /* Introduced the next-generation database
                                 * format which fixes the data locality
                                 * problems in the v4 format.
                                 */
    DBV_TaskLocal,              /* Addition of task local value.
                                 */
    DBV_Map,                    /* Addition of `MAP' variables
                                 */
    DBV_FileIO,                 /* Includes addition of the 'E_FILE' keyword.
                                 */
    DBV_Exec,                   /* Includes addition of the 'E_EXEC' keyword.
                                 */
    DBV_Interrupt,              /* Includes addition of the 'E_INTRPT' keyword.
                                 */
    DBV_This,                   /* Varification of `this'.
                                 */
    DBV_Iter,                   /* Addition of map iterator
                                 */
    DBV_Anon,                   /* Addition of anonymous objects
                                 */
                                 DBV_BIGINT, /* addition of big integers. */
    Num_DB_Versions             /* Special: the current version is this - 1. */
} DB_Version;
        -- add a version-contingent clause to fill_in_rt_consts(),
           in eval_env.c
// New fill_in_rt_consts():
void
fill_in_rt_consts(Var * env, DB_Version version)
{
    Var v;

    v.type = TYPE_INT;
    v.v.num = (int) TYPE_ERR;
    env[SLOT_ERR] = var_ref(v);
    v.v.num = (int) TYPE_INT;
    env[SLOT_NUM] = var_ref(v);
    v.v.num = (int) _TYPE_STR;
    env[SLOT_STR] = var_ref(v);
    v.v.num = (int) TYPE_OBJ;
    env[SLOT_OBJ] = var_ref(v);
    v.v.num = (int) _TYPE_LIST;
    env[SLOT_LIST] = var_ref(v);

    if (version >= DBV_Float) {
        v.v.num = (int) TYPE_INT;
        env[SLOT_INT] = var_ref(v);
        v.v.num = (int) _TYPE_FLOAT;
        env[SLOT_FLOAT] = var_ref(v);
    }
    if (version >= DBV_Map) {
        v.v.num = (int) _TYPE_MAP;
        env[SLOT_MAP] = var_ref(v);
    }
    if (version >= DBV_Anon) {
        v.v.num = (int) _TYPE_ANON;
        env[SLOT_ANON] = var_ref(v);
    }
    if (version >= DBV_BIGINT) {
        v.v.num = (int) TYPE_BIGINT;
        env[SLOT_BIGINT] = var_ref(v);
    }
}
        -- add a version-contingent clause to each of new_builtin_names() and
           first_user_slot(), in sym_table.c
// new_builtin_names():
Names *
new_builtin_names(DB_Version version)
{
    static Names *builtins[Num_DB_Versions];

    if (builtins[version] == 0) {
        Names *bi = new_names(first_user_slot(version));

        builtins[version] = bi;
        bi->size = bi->max_size;

        bi->names[SLOT_NUM] = str_dup("NUM");
        bi->names[SLOT_OBJ] = str_dup("OBJ");
        bi->names[SLOT_STR] = str_dup("STR");
        bi->names[SLOT_LIST] = str_dup("LIST");
        bi->names[SLOT_ERR] = str_dup("ERR");
        bi->names[SLOT_PLAYER] = str_dup("player");
        bi->names[SLOT_THIS] = str_dup("this");
        bi->names[SLOT_CALLER] = str_dup("caller");
        bi->names[SLOT_VERB] = str_dup("verb");
        bi->names[SLOT_ARGS] = str_dup("args");
        bi->names[SLOT_ARGSTR] = str_dup("argstr");
        bi->names[SLOT_DOBJ] = str_dup("dobj");
        bi->names[SLOT_DOBJSTR] = str_dup("dobjstr");
        bi->names[SLOT_PREPSTR] = str_dup("prepstr");
        bi->names[SLOT_IOBJ] = str_dup("iobj");
        bi->names[SLOT_IOBJSTR] = str_dup("iobjstr");

        if (version >= DBV_Float) {
            bi->names[SLOT_INT] = str_dup("INT");
            bi->names[SLOT_FLOAT] = str_dup("FLOAT");
        }
        if (version >= DBV_Map) {
            bi->names[SLOT_MAP] = str_dup("MAP");
        }
        if (version >= DBV_Anon) {
            bi->names[SLOT_ANON] = str_dup("ANON");
        }
        if (version >= DBV_BIGINT) {
            bi->names[SLOT_BIGINT] = str_dup("BIGINT");
        }
    }
    return copy_names(builtins[version]);
}
// first_user_slot():
int
first_user_slot(DB_Version version)
{
    int count = 16;             /* DBV_Prehistory count */

    if (version >= DBV_Float)
        count += 2;

    if (version >= DBV_Map)
        count += 1;

    if (version >= DBV_Anon)
        count += 1;

    if (version >= DBV_BIGINT)
        count += 1;

    return count;
}
-- consider adding a new kind of MOO literal or expression that produces values
   of the new type.
(I had no idea what this meant rofl)
-- add a case to each of list2str() and print_to_stream(), in list.c; the
   former is used by the MOO function tostr() and the latter by toliteral().
This was a bit difficult to find. I had to do some digging; apparently
in stunt its changed to stream_add_tostr(). So here's that:
// stream_add_tostr():
static void
stream_add_tostr(Stream * s, Var v)
{
    switch (v.type) {
    case TYPE_INT:
        stream_printf(s, "%d", v.v.num);
        break;
    case TYPE_OBJ:
        stream_printf(s, "#%d", v.v.obj);
        break;
    case TYPE_STR:
        stream_add_string(s, v.v.str);
        break;
    case TYPE_ERR:
        stream_add_string(s, unparse_error(v.v.err));
        break;
    case TYPE_FLOAT:
        stream_printf(s, "%g", *v.v.fnum);
        break;
    case TYPE_MAP:
        stream_add_string(s, "[map]");
        break;
    case TYPE_LIST:
        stream_add_string(s, "{list}");
        break;
    case TYPE_ANON:
        stream_add_string(s, "*anonymous*");
        break;
        case TYPE_BIGINT:
                stream_printf(s, "%s", std::string(*v.v.bignum).c_str());
        break;
    default:
        panic("STREAM_ADD_TOSTR: Unknown Var type");
    }
}
(I could not find print_to_stream() nor list2str(). stream_add_tostr()
seemed to be the right place because that's where I found {list}. :))
-- add a case to to each of become_integer() and become_float(), in numbers.c;
   the former is used by toint() and toobj() and the latter by tofloat().
I just made this return E_TYPE since boost cpp_int objects can be
higher than the maximum 32-bit integer, and I didn't want to rework
the entire program to use cpp_ints. Instead, I made this:
static package
bf_tobigint(Var arglist, Byte next, void *vdata, Objid progr)
{
    Var r;
    enum error e;

    r.type = TYPE_BIGINT;
switch(arglist.v.list[1].type) {
case TYPE_INT: {
boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>> num =
boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>>(arglist.v.list[1].v.num);
r.v.bignum=&num;
}
break;
case TYPE_STR: {
boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>> num =
boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked,
void>>(std::string(arglist.v.list[1].v.str));
r.v.bignum=&num;
}
break;
case TYPE_BIGINT:
r.v.bignum = arglist.v.list[1].v.bignum;
break;
default:
e = E_TYPE;
break;
}
    free_var(arglist);
    if (e != E_NONE)
        return make_error_pack(e);

    return make_var_pack(r);
}
Followed by its appropriate register_function() call:
    register_function("tobigint", 1, 1, bf_tobigint, TYPE_ANY);
-- consider adding a clause to is_true(), equality(), and value_bytes(),
   in utils.c; the first implements MOO's notion of truth values, the second is
   used in the `==' and `!=' expressions and the equals() function, and the
   third is used in the MOO function of the same name.
So, here's is_true():
int
is_true(Var v)
{
    return ((v.type == TYPE_INT && v.v.num != 0)
            || (v.type == TYPE_FLOAT && *v.v.fnum != 0.0)
            || (v.type == TYPE_STR && v.v.str && *v.v.str != '\0')
            || (v.type == TYPE_LIST && v.v.list[0].v.num != 0)
            || (v.type == TYPE_MAP && !mapempty(v))
            ||(v.type==TYPE_BIGINT&&v.v.bignum!=0));
}
equality():
int
equality(Var lhs, Var rhs, int case_matters)
{
    if (lhs.type == rhs.type) {
        switch (lhs.type) {
        case TYPE_CLEAR:
            return 1;
        case TYPE_NONE:
            return 1;
        case TYPE_INT:
            return lhs.v.num == rhs.v.num;
        case TYPE_OBJ:
            return lhs.v.obj == rhs.v.obj;
        case TYPE_ERR:
            return lhs.v.err == rhs.v.err;
        case TYPE_STR:
            if (lhs.v.str == rhs.v.str)
                return 1;
            else if (case_matters)
                return !strcmp(lhs.v.str, rhs.v.str);
            else
                return !mystrcasecmp(lhs.v.str, rhs.v.str);
        case TYPE_FLOAT:
            if (lhs.v.fnum == rhs.v.fnum)
                return 1;
            else
                return *(lhs.v.fnum) == *(rhs.v.fnum);
        case TYPE_LIST:
            return listequal(lhs, rhs, case_matters);
        case TYPE_MAP:
            return mapequal(lhs, rhs, case_matters);
        case TYPE_ANON:
            return lhs.v.anon == rhs.v.anon;
            case TYPE_BIGINT:
            return lhs.v.bignum==rhs.v.bignum;
        default:
            panic("EQUALITY: Unknown value type");
        }
    }
    return 0;
}
value_bytes():
int
value_bytes(Var v)
{
    int size = sizeof(Var);

    switch (v.type) {
    case TYPE_STR:
        size += memo_strlen(v.v.str) + 1;
        break;
    case TYPE_FLOAT:
        size += sizeof(double);
        break;
    case TYPE_LIST:
        size += list_sizeof(v.v.list);
        break;
    case TYPE_MAP:
        size += map_sizeof(v.v.tree);
        break;
        case TYPE_BIGINT:
        size+=sizeof(boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>>*);
        break;
    default:
        break;
    }

    return size;
}
So, like I asked above -- did I mess up somewhere? Do I need to add
refcounting? :)

Todd Sundsted

unread,
Apr 17, 2018, 9:05:10 PM4/17/18
to MOO Talk
> (I didn't do this because INTs aren't refcounted, so neither shouldbignums... correct me if they should be.)

I'm still looking at this, but it looks like you added the bignum to the Var struct as a *pointer* to an instance so you will have to manage memory via refcounting. In this regard, your bigint will follow the pattern of floats, which, for historical reasons, are also managed via a pointer.


        boost::multiprecision::number<
boost::multiprecision::cpp_int_backend<1048576,
1048576, boost::multiprecision::signed_magnitude,
boost::multiprecision::checked, void>> *bignum; /* Big integer */

Todd

Ethin Probst

unread,
Apr 17, 2018, 11:01:05 PM4/17/18
to Todd Sundsted, MOO Talk
I had to; the compiler would not allow me to create a direct
declaration of this type in a union. (sad) Guess I will need to
refcount things...
> --
> You received this message because you are subscribed to the Google Groups
> "MOO Talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to MOO-talk+u...@googlegroups.com.
> To post to this group, send email to MOO-...@googlegroups.com.
> Visit this group at https://groups.google.com/group/MOO-talk.
> For more options, visit https://groups.google.com/d/optout.
>


--
Signed,
Ethin D. Probst

Todd Sundsted

unread,
Apr 18, 2018, 7:17:02 AM4/18/18
to MOO Talk
You want a pointer anyway. Var is the data type for values of all kinds, so you want to keep its size small. A pointer to an instance will likely be smaller than an instance (though I've not looked to confirm the size of this type). Just follow the path of floats through the code, and make similar changes.

Ethin Probst

unread,
Apr 18, 2018, 5:14:37 PM4/18/18
to MOO Talk
OK, update:
I managed to figure out how to get it to work (sort of). I keep
sending my replies to the wrong person (bleh!) so... yeah. :P
Anyway, I was suffering linking errors because I switched to GMP...
got that fixed. But now it segfaults... not sure why. The backtrace
from GDB is horribly confusing and is of no help:
GNU gdb (GDB) 8.1 [GDB v8.1 for FreeBSD]
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-portbld-freebsd11.1".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from moo...done.
(gdb) run
Starting program: /usr/home/ethin/stunt/moo

Program received signal SIGSEGV, Segmentation fault.
0x0000000801ff9d08 in vtable for __cxxabiv1::__si_class_type_info ()
from /lib/libcxxrt.so.1
(gdb) bt
#0 0x0000000801ff9d08 in vtable for __cxxabiv1::__si_class_type_info
() from /lib/libcxxrt.so.1
#1 0x0000000801022e69 in __dynamic_cast () from
/usr/local/lib/gcc6/libstdc++.so.6
#2 0x00000008010a7210 in bool std::has_facet<std::ctype<char>
>(std::locale const&) ()
from /usr/local/lib/gcc6/libstdc++.so.6
#3 0x000000080109aad4 in std::basic_ios<char, std::char_traits<char>
>::_M_cache_locale(std::locale const&) ()
from /usr/local/lib/gcc6/libstdc++.so.6
#4 0x000000080109ac50 in std::basic_ios<char, std::char_traits<char>
>::init(std::basic_streambuf<char, std::char_traits<char> >*) () from
/usr/local/lib/gcc6/libstdc++.so.6
#5 0x00000008010372a1 in std::ios_base::Init::Init() () from
/usr/local/lib/gcc6/libstdc++.so.6
#6 0x000000000042d7a3 in __static_initialization_and_destruction_0
(__priority=65535, __initialize_p=1)
at /usr/local/lib/gcc7/include/c++/iostream:74
#7 _GLOBAL__sub_I__Z21dbpriv_set_dbio_inputP7 () at db_io.cc:499
#8 0x00000008006d361a in ?? () from /libexec/ld-elf.so.1
#9 0x00000008006d29db in ?? () from /libexec/ld-elf.so.1
#10 0x00000008006d0669 in ?? () from /libexec/ld-elf.so.1
#11 0x0000000000000000 in ?? ()
(gdb) quit
A debugging session is active.

Inferior 1 [process 92747] will be killed.

Quit anyway? (y or n) y
However, list reveals the following lines, from server.cc:
who = dbio_read_num();
listener = SYSTEM_OBJECT;
}
checkpointed_connections.v.list[i] = v = new_list(2);
v.v.list[1].type = v.v.list[2].type = TYPE_OBJ;
v.v.list[1].v.obj = who;
v.v.list[2].v.obj = listener;
}

return 1;
}
I'm guessing it has something to do with dbio_read_num(). But I never
touched that function. I touched dbio_read_var(), though, but it
doesn't call that at such an early point (its just looking for active
connections at this stage). It doesn't even get to logging before it
segfaults. I'll re-post the modifications I made to dbio_wrie_var() in
case I messed up pointers or something:
mpz_class num = dbio_read_bignum();
r.v.bignum = &num;
}
break;
default:
errlog("DBIO_READ_VAR: Unknown type (%d) at DB file pos. %ld\n",
l, ftell(input));
r = zero;
break;
}
return r;
}
Granted... I modified dbio_read_bignum()/dbio_write_bignum() but those
haven't been called because I haven't saved any in the DB yet:
mpz_class
dbio_read_bignum(void)
{
std::vector<char> s;
mpz_class i;
// Read until \n found.
int ch;
while ((ch = fgetc(input)) != '\n') {
s.push_back(ch);
}
try {
i = mpz_class(std::string(s.begin(), s.end()));
} catch (const std::exception& error) {
errlog("DBIO_READ_BIGNUM: Error reading bigint: %s\n", error.what());
}
return i;
}
void
dbio_write_bignum(mpz_class* n)
{
try {
dbio_printf("%s\n", *n->get_str().c_str());
} catch (const std::exception& ex) {
errlog("DBIO_WRITE_BIGNUM: Cannot write big integer: %s\n", ex.what());
}
}
Any ideas about what's happening and what I'm missing?

Tim van Dijen

unread,
Apr 19, 2018, 2:44:35 AM4/19/18
to MOO-...@googlegroups.com

I think you should be looking for the issue here:

#7  _GLOBAL__sub_I__Z21dbpriv_set_dbio_inputP7 () at db_io.cc:499
Perhaps it can help to run the moo with Valgrind.. It's a bit more specific in telling you where it segfaulted..

- Tim

Ethin Probst

unread,
Apr 19, 2018, 2:09:18 PM4/19/18
to Tim van Dijen, MOO Talk
That might be a wise idea. The idea you gave me (about checking line
499) doesn't help, it's just a dbio_printf satement in this function:
void
dbio_write_forked_program(Program * program, int f_index)
{
    unparse_program(program, receiver, 0, 1, 0, f_index);
    dbio_printf(".\n");
}

On 4/19/18, Tim van Dijen <tvd...@gmail.com> wrote:
> I think you should be looking for the issue here:
>
>> #7 _GLOBAL__sub_I__Z21*dbpriv_set_dbio_input*P7 () at*db_io.cc:499*
> Perhaps it can help to run the moo with Valgrind.. It's a bit more
> specific in telling you where it segfaulted..
>
> - Tim
>

Ethin Probst

unread,
Apr 21, 2018, 7:52:38 PM4/21/18
to MOO Talk
OK, so here's the output I got when running it from valgrind (I even removed my list.cc modifications for unparse_value):
$ valgrind ./moo
==72497== Memcheck, a memory error detector
==72497== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==72497== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==72497== Command: ./moo
==72497==
==72497==
==72497== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==72497==  Bad permissions for mapped region at address 0x6538D08
==72497==    at 0x6538D08: ??? (in /lib/libcxxrt.so.1)
==72497==    by 0x5561E68: __dynamic_cast (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72497==    by 0x55E620F: bool std::has_facet<std::ctype<char> >(std::locale const&) (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72497==    by 0x55D9AD3: std::basic_ios<char, std::char_traits<char> >::_M_cache_locale(std::locale const&) (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72497==    by 0x55D9C4F: std::basic_ios<char, std::char_traits<char> >::init(std::basic_streambuf<char, std::char_traits<char> >*) (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72497==    by 0x55762A0: std::ios_base::Init::Init() (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72497==    by 0x42D79D: __static_initialization_and_destruction_0 (iostream:74)
==72497==    by 0x42D79D: _GLOBAL__sub_I__Z21dbpriv_set_dbio_inputP7(short,  (intXX_t)) (db_io.cc:500)
==72497==    by 0x4005619: ??? (in /libexec/ld-elf.so.1)
==72497==    by 0x40049DA: ??? (in /libexec/ld-elf.so.1)
==72497==    by 0x4002668: ??? (in /libexec/ld-elf.so.1)
==72497==
==72497== HEAP SUMMARY:
==72497==     in use at exit: 75,704 bytes in 7 blocks
==72497==   total heap usage: 7 allocs, 0 frees, 75,704 bytes allocated
==72497==
==72497== LEAK SUMMARY:
==72497==    definitely lost: 0 bytes in 0 blocks
==72497==    indirectly lost: 0 bytes in 0 blocks
==72497==      possibly lost: 0 bytes in 0 blocks
==72497==    still reachable: 75,704 bytes in 7 blocks
==72497==         suppressed: 0 bytes in 0 blocks
==72497== Rerun with --leak-check=full to see details of leaked memory
==72497==
==72497== For counts of detected and suppressed errors, rerun with: -v
==72497== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Segmentation fault
When I run:
$ valgrind --log-file=moocore.log --read-var-info=yes --leak-check=full ./moo
I get (in the log file):
==72542== Memcheck, a memory error detector
==72542== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==72542== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==72542== Command: ./moo
==72542== Parent PID: 63391
==72542==

parse DIE(readdwarf3.c:3607): confused by:
 <2><1ee>: Abbrev Number: 13 (DW_TAG_subrange_type)
     DW_AT_type        : <20b>   
     DW_AT_count       : 0   
parse_type_DIE:
--72542-- WARNING: Serious error when reading debug info
--72542-- When reading debug info from /usr/local/lib/valgrind/memcheck-amd64-freebsd:
--72542-- confused by the above DIE

parse DIE(readdwarf3.c:3607): confused by:
 <2><46a>: Abbrev Number: 24 (DW_TAG_subrange_type)
     DW_AT_type        : <476>   
     DW_AT_count       : 6   
parse_type_DIE:
--72542-- WARNING: Serious error when reading debug info
--72542-- When reading debug info from /usr/local/lib/valgrind/vgpreload_memcheck-amd64-freebsd.so:
--72542-- confused by the above DIE
==72542==
==72542== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==72542==  Bad permissions for mapped region at address 0x6538D08
==72542==    at 0x6538D08: ??? (in /lib/libcxxrt.so.1)
==72542==    by 0x5561E68: __dynamic_cast (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72542==    by 0x55E620F: bool std::has_facet<std::ctype<char> >(std::locale const&) (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72542==    by 0x55D9AD3: std::basic_ios<char, std::char_traits<char> >::_M_cache_locale(std::locale const&) (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72542==    by 0x55D9C4F: std::basic_ios<char, std::char_traits<char> >::init(std::basic_streambuf<char, std::char_traits<char> >*) (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72542==    by 0x55762A0: std::ios_base::Init::Init() (in /usr/local/lib/gcc6/libstdc++.so.6.0.22)
==72542==    by 0x42D79D: __static_initialization_and_destruction_0 (iostream:74)
==72542==    by 0x42D79D: _GLOBAL__sub_I__Z21dbpriv_set_dbio_inputP7(short,  (intXX_t)) (db_io.cc:500)
==72542==    by 0x4005619: ??? (in /libexec/ld-elf.so.1)
==72542==    by 0x40049DA: ??? (in /libexec/ld-elf.so.1)
==72542==    by 0x4002668: ??? (in /libexec/ld-elf.so.1)
==72542==
==72542== HEAP SUMMARY:
==72542==     in use at exit: 75,704 bytes in 7 blocks
==72542==   total heap usage: 7 allocs, 0 frees, 75,704 bytes allocated
==72542==
==72542== LEAK SUMMARY:
==72542==    definitely lost: 0 bytes in 0 blocks
==72542==    indirectly lost: 0 bytes in 0 blocks
==72542==      possibly lost: 0 bytes in 0 blocks
==72542==    still reachable: 75,704 bytes in 7 blocks
==72542==         suppressed: 0 bytes in 0 blocks
==72542== Reachable blocks (those to which a pointer was found) are not shown.
==72542== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==72542==
==72542== For counts of detected and suppressed errors, rerun with: -v
==72542== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
So now, I'm honestly confused. I have no idea what I did wrong and where -- nothings exactly being helpful. Since you'll probably need to look throug it, the source code for my heavily modified copy of stunt is available at https://www.dropbox.com/s/pmfl4g5xpavq77x/stunt.tar.gz?dl=1. It contains all my checksum functions, my RNG modification, as well as the mods I've made for my datatype for infinite precision integers.

Tim van Dijen

unread,
Apr 22, 2018, 2:42:07 PM4/22/18
to Ethin Probst, MOO Talk
Well, without having a look at your source, the common thing with the earlier trace is:
==72542==    by 0x42D79D: _GLOBAL__sub_I__Z21dbpriv_set_dbio_inputP7(short,  (intXX_t)) (db_io.cc:500)

Although earlier it said line 499...
It may not be the line itself, but the parameters that are causing issues..
I’ll try and have a look at your source later.

-Tim

--

Ethin Probst

unread,
Apr 22, 2018, 8:20:09 PM4/22/18
to Tim van Dijen, MOO Talk
Please do. I'm terribly confused on this matter -- searching for
_GLOBAL__sub_I__Z21dbpriv_set_dbio_inputP7 obviously yields no
results, since no one would *ever* name a function that, and simply
searching for dbpriv_set_dbio_input yields the function void
dbpriv_set_dbio_input(FILE * f). Nothing that I know of takes the
parameters that it shows.
Reply all
Reply to author
Forward
0 new messages