/*
 * Copyright (C) 2014 PerfectCarl - https://github.com/PerfectCarl/vala-stacktrace
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* History
 *  - 1.2 :
 *        . Include elementary.cmake
 *  - 1.1 :
 *        . Support debugging in multiple .so
 *  - 1.0 : Oct, 18 2014
 *        . initial version
 *        . Header not displaying an error for user created stacktrace
 *        . Change spacing
 */

public class Stacktrace {
    [CCode (cname = "pw_dw_resolve_addr", cheader_filename = "dwresolve.h")]
    private extern static int dw_resolve_addr (string module_path, uint64 address, [CCode (array_length = false)] char[] out_buf, size_t out_len);

    public class Frame {
        // Address used by addr2line
        public string address  { get;private set;default = "";}

        public string line { get;private set;default = "";}

        public string line_number { get;private set;default = "";}

        public string file_path { get;private set;default = "";}

        public string file_short_path { get;private set;default = "";}

        public string elf_path { get;private set;default = "";}

        public string function { get;private set;default = "";}

        public Frame (string address, string line, string function, string file_path, string file_short_path, string elf_path) {
            this._address = address;
            this._line = line;

            this._file_path = file_path;
            this._file_short_path = file_short_path;
            this._elf_path = elf_path;
            this._function = function;
            this.line_number = extract_line (line);
        }

        public string to_string () {
            var result = line;
            if (result == "")
                result = " C library at address [" + address + "]";
            return result + " [" + address + "]";
        }

    }

    public enum Style {
        RESET = 0,
        BRIGHT = 1,
        DIM = 2,
        UNDERLINE = 3,
        BLINK = 4,
        REVERSE = 7,
        HIDDEN = 8
    }

    public enum CriticalHandler {
        IGNORE,
        PRINT_STACKTRACE,
        CRASH
    }

    public enum Color {
        BLACK = 0,
        RED = 1,
        GREEN = 2,
        YELLOW = 3,
        BLUE = 4,
        MAGENTA = 5,
        CYAN = 6,
        WHITE = 7
    }

    public Gee.ArrayList<Frame> _frames = new Gee.ArrayList<Frame>();

    private class AddressInfo {
        public string module { get; set; default = ""; }
        public uint64 address { get; set; default = 0; }
    }

    private Frame first_vala = null;

    private int max_file_name_length = 0;

    private int max_line_number_length = 0;

    private bool is_all_function_name_blank = true;

    private bool is_all_file_name_blank = true;

    private ProcessSignal sig;

    private int extra_skipped_frames_count = 0;

    public static bool enabled { get;set;default = true;}

    public static bool hide_installed_libraries { get;set;default = false;}

    public static Color default_highlight_color { get;set;default = Color.WHITE;}

    public static Color default_error_background { get;set;default = Color.RED;}

    public Color highlight_color { get;set;default = Color.WHITE;}

    public Color error_background { get;set;default = Color.RED;}

    public Gee.ArrayList<Frame> frames {
        get {
            return _frames;
        }
    }

    public Stacktrace (GLib.ProcessSignal sig = GLib.ProcessSignal.TTOU, int extra_skipped_frames_count = 0) {
        this.sig = sig;
        this.extra_skipped_frames_count = extra_skipped_frames_count;
        // The stacktrace is used likely to understand the inner
        // working of the app so we displays everything.
        if (is_custom) {
            hide_installed_libraries = false;
            error_background = Color.BLUE;
        } else {
            error_background = default_error_background;
            highlight_color = default_highlight_color;
        }
        create_stacktrace ();
    }

    public static void @throw (Error error) throws Error {
        Stacktrace stack = new Stacktrace (ProcessSignal.TTOU, 1);
        stack.print ();
        throw error;
    }

    private string get_module_name () {
        var path = new char[1024];
        Posix.readlink ("/proc/self/exe", path);
        string result = (string) path;
        return result;
    }

    // TODO CARL convert this piece of code to vala conventions
    public static string get_relative_path (string p_fullDestinationPath, string p_startPath) {

        string[] l_startPathParts = p_startPath.split ("/");
        string[] l_destinationPathParts = p_fullDestinationPath.split ("/");

        int l_sameCounter = 0;
        while ((l_sameCounter < l_startPathParts.length) &&
               (l_sameCounter < l_destinationPathParts.length) &&
               l_startPathParts[l_sameCounter] == l_destinationPathParts[l_sameCounter]) {
            l_sameCounter++;
        }

        if (l_sameCounter == 0) {
            return p_fullDestinationPath;             // There is no relative link.
        }

        StringBuilder l_builder = new StringBuilder ();
        for (int i = l_sameCounter ; i < l_startPathParts.length ; i++) {
            l_builder.append ("../");
        }

        for (int i = l_sameCounter ; i < l_destinationPathParts.length ; i++) {
            l_builder.append (l_destinationPathParts[i] + "/");
        }

        // CARL l_builder.Length--;
        // Remove the last /
        var result = l_builder.str;
        result = result.substring (0, result.length - 1);
        return result;
    }

    private string extract_short_file_path (string file_path) {
        var path = Environment.get_current_dir ();
        /*var i = file_path.index_of ( path );
           if( i>=0 )
            return file_path.substring ( path.length, file_path.length - path.length );
           return file_path; */
        var result = get_relative_path (file_path, path);
        return result;
    }

    public bool is_custom {
        get {
            return sig == ProcessSignal.TTOU;
        }
    }
    // input : '/home/cran/Documents/Projects/elementary/noise/instant-beta/build/core/libnoise-core.so.0(noise_job_repository_create_job+0x309) [0x7ff60a021e69]'
    // ouput: 0x309
    private int extract_base_address (string line) {
        int result = 0 ;
        var start = line.last_index_of ("+");
        if (start >= 0) {
            var end = line.last_index_of (")");
            if( end > start ) {
                var text = line.substring (start+3,end-start-3) ;
                text.scanf("%x",  &result);
            }
        }
        return result ;
    }

    private void process_info_for_file (string full_line, string str ) {
        func = "" ;
        file_path = "";
        short_file_path = "";
        l = "";
        file_line = "";
        func_line = "";

        var lines = full_line.split ("\n");

        if (lines.length > 0)
            func_line = lines[0];

        if (lines.length > 1)
            file_line = lines[1];
        if (file_line == "??:0" || file_line == "??:?")
            file_line = "";
        func = extract_function_name (str);

        file_path = "";
        short_file_path = "";
        l = "";
        if (file_line != "") {
            if (func == "")
                func = extract_function_name_from_line (func_line);
            file_path = extract_file_path (file_line);
            short_file_path = extract_short_file_path (file_path);
            l = extract_line (file_line);
        }
    }

    string func = "" ;
    string file_path = "";
    string short_file_path = "";
    string l = "";
    string file_line = "";
    string func_line = "";

    private uint64 parse_hex64 (string text) {
        var s = text.strip ();
        if (s.has_prefix ("0x") || s.has_prefix ("0X"))
            s = s.substring (2);

        uint64 result = 0;
        for (int i = 0; i < s.length; i++) {
            unichar c = s.get_char (i);
            uint64 digit;

            if (c >= '0' && c <= '9')
                digit = c - '0';
            else if (c >= 'a' && c <= 'f')
                digit = c - 'a' + 10;
            else if (c >= 'A' && c <= 'F')
                digit = c - 'A' + 10;
            else
                break;

            result = (result << 4) + digit;
        }

        return result;
    }

    private string format_hex64 (uint64 value) {
        return ("0x%" + uint64.FORMAT_MODIFIER + "x").printf (value);
    }

    private string format_unwind_word (Unwind.Word value) {
        return format_hex64 ((uint64) value);
    }

    private string[] split_whitespace (string text) {
        try {
            var whitespace = new Regex ("\\s+");
            return whitespace.split (text.strip ());
        } catch (RegexError e) {
            return text.split (" ");
        }
    }

    private string[] string_list_to_array (Gee.ArrayList<string> list) {
        string[] result = new string[list.size];
        for (int i = 0; i < list.size; i++)
            result[i] = list[i];
        return result;
    }

    private AddressInfo resolve_runtime_address (string runtime_address) throws Error {
        uint64 runtime = parse_hex64 (runtime_address);
        string maps;
        FileUtils.get_contents ("/proc/self/maps", out maps);

        foreach (string line in maps.split ("\n")) {
            string stripped_line = line.strip ();
            if (stripped_line == "")
                continue;

            string[] parts = split_whitespace (stripped_line);
            if (parts.length < 5)
                continue;

            string[] range = parts[0].split ("-");
            if (range.length != 2)
                continue;

            uint64 start = parse_hex64 (range[0]);
            uint64 end = parse_hex64 (range[1]);
            uint64 file_offset = parse_hex64 (parts[2]);

            if (runtime < start || runtime >= end)
                continue;

            var info = new AddressInfo ();
            uint64 load_bias = start - file_offset;
            info.address = runtime - load_bias;
            info.module = parts.length >= 6 ? parts[5] : "";
            if (info.module == "" || info.module.has_prefix ("["))
                info.module = get_module_name ();
            return info;
        }

        var fallback = new AddressInfo ();
        fallback.module = get_module_name ();
        fallback.address = runtime;
        return fallback;
    }

    private string module_for_runtime_address (string runtime_address) {
        try {
            return resolve_runtime_address (runtime_address).module;
        } catch (Error e) {
            return get_module_name ();
        }
    }

    private string[] collect_unwind_symbols (int frame_count) {
        var result = new Gee.ArrayList<string> ();
        Unwind.Context context = Unwind.Context ();
        Unwind.Cursor cursor;

        if (Unwind.Cursor.init_local (out cursor, context) < 0)
            return string_list_to_array (result);

        for (int i = 0; i < frame_count; i++) {
            Unwind.Word ip;
            if (cursor.get_reg ((Unwind.Regnum) Unwind.FrameRegnum.IP, out ip) < 0)
                break;

            if ((uint64) ip != 0) {
                char[] name = new char[512];
                Unwind.Word offset;
                string function = "";

                if (cursor.get_proc_name (name, out offset) == 0)
                    function = ((string) name).strip ();

                if (function == "")
                    function = "??";

                string address = format_unwind_word (ip);
                string module = module_for_runtime_address (address);
                result.add ("%s(%s+%s) [%s]".printf (
                    module,
                    function,
                    format_unwind_word (offset),
                    address
                ));
            }

            if (cursor.step () <= 0)
                break;
        }

        return string_list_to_array (result);
    }

    private void process_info_from_lib (string file_path, string str) {
        var cmd2 = "nm %s".printf(file_path) ;
        var addr1_s = execute_command_sync_get_output (cmd2) ;
        MatchInfo info ;
        try {
            Regex regex = new Regex ("\\n[^ ]* T "+func);

            if( regex.match (addr1_s, 0, out info) )
            {
                while( info.matches() ){
                    var lll = info.fetch(0) ;
                    //stdout.printf ( "lll '%s'\n", lll ) ;
                    addr1_s = lll.substring(0, lll.index_of(" ")) ;
                    info.next();
                }
            }
        } catch (RegexError e)
        {
            critical( "Error while processing regex %s", e.message ) ;
        }
        //stdout.printf ("addr1_s %s\n", addr1_s) ;
        int addr1 = 0 ;
        addr1_s.scanf("%x",  &addr1);
        if( addr1 != 0 ) {
            int addr2 = extract_base_address (str) ;
            string addr3 = "%#08x".printf (addr1+addr2);
            var new_full_line = process_line (file_path, addr3);
            process_info_for_file (new_full_line, str ) ;
        }
    }

    private void create_stacktrace () {
        int frame_count = 100;
        int skipped_frames_count = 4;
        // Stacktrace not due to a crash
        if (is_custom)
            skipped_frames_count = 1;
        skipped_frames_count += extra_skipped_frames_count;

        _frames.clear ();
        first_vala = null;
        max_file_name_length = 0;
        is_all_function_name_blank = true;
        is_all_file_name_blank = true;

        string[] strings = collect_unwind_symbols (frame_count);
        int size = strings.length;

        // First ones are the handler
        for (int i = skipped_frames_count ; i < size ; i++) {
            string str = strings[i];
            var addr = extract_address (str);
            var elf_path = extract_file_path_from (str);

            var full_line = process_runtime_address (addr);
            process_info_for_file( full_line, str) ;
            if (file_line == "") {
                file_path = elf_path;

            }
            if( file_path.has_suffix(".so.0")) {
                process_info_from_lib (file_path, str) ;
            }

            //stdout.printf ("Building %d \n  . addr: [%s]\n  . full_line: '%s'\n  . file_line: '%s'\n  . func_line: '%s'\n  . str : '%s'\n  . func: '%s'\n  . file: '%s'\n  . line: '%s'\n",
             //i, addr, full_line, file_line, func_line, str, func, file_path, l);

            if (func != "" && file_path.has_suffix (".vala") && is_all_function_name_blank)
                is_all_function_name_blank = false;

            if (short_file_path != "" && is_all_file_name_blank)
                is_all_file_name_blank = false;

            var frame = new Frame (addr, file_line, func, file_path, short_file_path, elf_path);

            if (first_vala == null && file_path.has_suffix (".vala"))
                first_vala = frame;

            if (short_file_path.length > max_file_name_length)
                max_file_name_length = short_file_path.length;
            if (l.length > max_line_number_length)
                max_line_number_length = l.length;
            _frames.add (frame);
        }
    }

    private string extract_function_name (string line) {
        if (line == "")
            return "";
        var start = line.index_of ("(");
        if (start >= 0) {
            var end = line.index_of ("+", start);
            if (end >= 0) {
                var result = line.substring (start + 1, end - start - 1);
                return result.strip ();
            }
        }
        return "";
    }

    private string extract_function_name_from_line (string line) {
        return line.strip ();
    }

    private string extract_file_path_from (string str) {
        if (str == "")
            return "";
        /*if( str.index_of("??") >= 0)
            //result = result.substring (4, line.length - 4 );
            stdout.printf ("ERR2?? : %s\n", str ) ; */
        var start = str.index_of ("(");
        if (start >= 0) {
            return str.substring (0, start).strip ();
        }
        return str.strip ();
    }

    private string extract_file_path (string line) {
        var result = line;
        if (result == "")
            return "";
        if (result == "??:0??:0")
            return "";
        // For some reason, the file name can starts with ??:0
        if (result.has_prefix ("??:0"))
            result = result.substring (4, line.length - 4);
        // stdout.printf ("ERR1?? : %s\n", line ) ;
        var start = result.index_of (":");
        if (start >= 0) {
            result = result.substring (0, start);
            return result.strip ();
        }
        return "";
    }

    public static string extract_line (string line) {
        var result = line;
        if (result == "")
            return "";
        if (result.has_prefix ("??:0"))
            result = result.substring (4, line.length - 4);
        var start = result.index_of (":");
        if (start >= 0) {
            result = result.substring (start + 1, line.length - start - 1);
            var end = result.index_of ("(");
            if (end >= 0) {
                result = result.substring (0, end);
            }
            return result.strip ();
        }
        return "";
    }

    private string extract_address (string line) {
        if (line == "")
            return "";
        var start = line.index_of ("[");
        if (start >= 0) {
            var end = line.index_of ("]", start);
            if (end >= 0) {
                var result = line.substring (start + 1, end - start - 1);
                return result.strip ();
            }
        }
        return "";
    }

    private string execute_command_sync_get_output (string cmd) {
        try {
            int exitCode;
            string std_out;
            string std_err;
            Process.spawn_command_line_sync (cmd, out std_out, out std_err, out exitCode);
            if (exitCode != 0 && std_err != "") {
                return std_out;
            }
            return std_out;
        }
        catch (Error e) {
            error ("Error while executing '%s': %s".printf(cmd,e.message));
        }
    }

    // Poor's man demangler. libunwind is another dep
    // TODO : Optimize this
    // module : app
    // address : 0x007f80
    // output : /home/cran/Projects/noise/noise-perf-instant-search/tests/errors.vala:87
    string process_line (string module, string address) {
        char[] resolved = new char[4096];
        if (dw_resolve_addr (module, parse_hex64 (address), resolved, resolved.length) != 0) {
            string result = (string) resolved;
            if (result != "")
                return result;
        }

        /*
         * Old fallback:
         *
         * var cmd = "addr2line -C -f -e %s %s".printf (Shell.quote (module), address);
         * var result = execute_command_sync_get_output (cmd);
         * return result;
         *
         * Keep resolution inside the process via libdw. If libdw cannot resolve
         * the address, report that explicitly instead of spawning addr2line.
         */
        return "libdw konnte Adresse nicht auflösen: %s %s\n??:0\n".printf (module, address);
    }

    string process_runtime_address (string runtime_address) {
        try {
            AddressInfo info = resolve_runtime_address (runtime_address);
            return process_line (info.module, format_hex64 (info.address));
        } catch (Error e) {
            return "??\n??:0\n";
        }
    }

    private string get_reset_code () {
        // return get_color_code (Style.RESET, Colors.WHITE, Colors.BLACK);
        return "\x1b[0m";
    }

    private string get_reset_style () {
        return get_color_code (Style.DIM, highlight_color, background_color);
    }

    private string get_color_code (Style attr, Color fg, Color bg = background_color) {
        /* Command is the control command to the terminal */
        if (bg == Color.BLACK)
            return "%c[%d;%dm".printf (0x1B, (int) attr, (int) fg + 30);
        else
            return "%c[%d;%d;%dm".printf (0x1B, (int) attr, (int) fg + 30, (int) bg + 40);
    }

    private string get_signal_name () {
        return sig.to_string ();
    }

    private string get_highlight_code () {
        return get_color_code (Style.BRIGHT, highlight_color);
    }

    private string get_printable_function (Frame frame, int padding = 0) {
        var result = "";
        var is_unknown = false;
        if (frame.function == "") {
            result = "<unknown> " + frame.address;
            is_unknown = true;
        } else {
            var s = "";
            int count = padding - get_signal_name ().length;
            if (padding != 0 && count > 0)
                s = string.nfill (count, ' ');
            result = "'" + frame.function + "'" + s;
        }
        if (is_unknown)
            return result + get_reset_code ();
        else
            return get_highlight_code () + result + get_reset_code ();
    }

    private string get_printable_line_number (Frame frame, bool pad = true) {
        var path = frame.line_number;
        var result = "";
        var color = get_highlight_code ();
        if (path.length >= max_line_number_length || !pad)
            result = color + path + get_reset_style ();
        else {
            result = color + path + get_reset_style ();
            result = string.nfill (max_line_number_length - path.length, ' ') + result;
        }
        return result;
    }

    private string get_printable_file_short_path (Frame frame, bool pad = true) {
        var path = frame.file_short_path;
        var result = "";
        var color = get_highlight_code ();
        if (path.length >= max_file_name_length || !pad)
            result = color + path + get_reset_style ();
        else {
            result = color + path + get_reset_style ();
            result = result + string.nfill (max_file_name_length - path.length, ' ');
        }
        return result;
    }

    Color background_color = Color.BLACK;
    int title_length = 0;

    private string get_printable_title () {
        var c = get_color_code (Style.DIM, highlight_color, background_color);
        var color = get_highlight_code ();

        var result = "" ;

        if( is_custom)
            result = "%sA function was called in %s".printf (
                c,
                get_reset_style ());
        else
            result = "%sAn error occured %s(%s)%s".printf (
                c,
                color,
                get_signal_name (),
                get_reset_style ());

        title_length = get_signal_name ().length;
        return result;
    }

    private string get_reason () {
        // var c = get_reset_code();
        var color = get_highlight_code ();
        if (sig == ProcessSignal.TRAP) {
            return "The reason is likely %san uncaught error%s".printf (
                color, get_reset_code ());
        }
        if (sig == ProcessSignal.ABRT) {
            return "The reason is likely %sa failed assertion (assert...)%s".printf (
                color, get_reset_code ());
        }
        if (sig == ProcessSignal.SEGV) {
            return "The reason is likely %sa null reference being used%s".printf (
                color, get_reset_code ());
        }
        if (sig == ProcessSignal.FPE) {
            return "The reason is likely %san arithmetic exception, such as division by zero%s".printf (
                color, get_reset_code ());
        }
        return "Unknown reason";
    }

    public void print () {
        background_color = error_background;
        var header = "%s%s\n".printf (get_printable_title (),
                                      get_reset_code ());

        if (first_vala != null) {
            header = "%s in %s, line %s in %s\n".printf (
                get_printable_title (),
                get_printable_file_short_path (first_vala, false),
                get_printable_line_number (first_vala, false),
                get_printable_function (first_vala) + get_reset_code ());
            title_length += first_vala.line_number.length +
                            first_vala.function.length +
                            first_vala.file_short_path.length;
        }
        stdout.printf (header);
        background_color = Color.BLACK;
        if( !is_custom) {
            var reason = get_reason ();
            stdout.printf ("   %s.\n", reason);
        }

        // Has the user forgot to compile with -g -X -rdynamic flag ?
        if (is_all_file_name_blank) {
            var advice = "   %sNote%s: no file path and line numbers can be retrieved. Are you sure %syou added -g -X -rdynamic%s to valac command line?\n";
            var color = get_highlight_code ();
            stdout.printf (advice, color, get_reset_code (), color, get_reset_code ());
        }

        // Has the user forgot to compile with rdynamic flag ?
        if (is_all_function_name_blank && !is_all_file_name_blank) {
            var advice = "   %sNote%s: no vala function name can be retrieved. Are you sure %syou added -X -rdynamic%s to valac command line?\n";
            var color = get_highlight_code ();
            stdout.printf (advice, color, get_reset_code (), color, get_reset_code ());
        }

        stdout.printf ("\n");
        int i = 1;
        bool has_displayed_first_vala = false;
        foreach (var frame in _frames) {
            var show_frame = frame.function != "" || frame.file_path.has_suffix (".vala") || frame.file_path.has_suffix (".c");
            if (hide_installed_libraries && has_displayed_first_vala)
                show_frame = show_frame && frame.file_short_path != "";

            // Ignore glib tracing code if displayed before the first vala frame
            if ((frame.function == "g_logv" || frame.function == "g_log") && !has_displayed_first_vala)
                show_frame = false;
            if (show_frame) {
                // #2  ./OtherModule.c      line 80      in 'other_module_do_it'
                // at /home/cran/Projects/noise/noise-perf-instant-search/tests/errors/module/OtherModule.vala:10
                var str = " %s  #%d  %s    line %s in %s\n";
                background_color = Color.BLACK;
                var lead = " ";
                var function_padding = 0;
                if (frame == first_vala) {
                    has_displayed_first_vala = true;
                    lead = "*";
                    background_color = error_background;
                    function_padding = 22;
                }
                var l_number = "";
                if (frame.line_number == "") {
                    str = " %s  #%d  <unknown>  %s in %s\n";
                    var func_name = get_printable_function (frame);
                    var fill_len = int.max (max_file_name_length + max_line_number_length - 1, 0);
                    str = str.printf (
                        lead,
                        i,
                        string.nfill (fill_len, ' '),
                        func_name);
                } else {
                    str = str.printf (
                        lead,
                        i,
                        get_printable_file_short_path (frame),
                        get_printable_line_number (frame),
                        get_printable_function (frame, function_padding));
                    l_number = ":" + frame.line_number;
                }
                stdout.printf (str);
                str = "        at %s%s\n".printf (
                    frame.file_path, l_number);
                stdout.printf (str);
                if (frame.elf_path != "" && frame.elf_path != frame.file_path) {
                    str = "        in %s\n".printf (frame.elf_path);
                    stdout.printf (str);
                }

                i++;
            }
        }
    }

    public static void register_handlers () {
        Log.set_always_fatal (LogLevelFlags.LEVEL_CRITICAL);

        Process.@signal (ProcessSignal.SEGV, handler);
        Process.@signal (ProcessSignal.FPE, handler);
        Process.@signal (ProcessSignal.TRAP, handler);
        if (critical_handling != CriticalHandler.IGNORE)
            Process.@signal (ProcessSignal.ABRT, handler);
    }

    public static void printstack () {
        Stacktrace t = new Stacktrace(ProcessSignal.TTOU,1);
        t.print();
    }

    public static CriticalHandler critical_handling  { get;set;default = CriticalHandler.PRINT_STACKTRACE;}

    /*{
        set {
            _critical_handling = value ;
            if( value == CriticalHandler.CRASH )
            //var variables = Environ.get ();
            //Environ.set_variable (variables, "G_DEBUG", "fatal-criticals" );
            Log.set_always_fatal (LogLevelFlags.LEVEL_CRITICAL);
        }
        get {
        }

       }*/

    public static void handler (int sig) {
        if( !enabled)
            return ;
        Stacktrace stack = new Stacktrace ((ProcessSignal) sig);
        stack.print ();
        if (sig != ProcessSignal.TRAP ||
            (sig == ProcessSignal.TRAP && critical_handling == CriticalHandler.CRASH))
            Process.exit (1);
    }

}
