aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/util/signalhandler.h
blob: b3c6ec1e5c7ca2ca74dd2e9f428cacfd7dd3d68c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include <vespa/vespalib/stllike/string.h>
#include <csignal>
#include <vector>
#include <atomic>
#include <pthread.h>

namespace vespalib {

/**
 * @brief This class helps you perform simple signal handling.
 *
 * A normal application will typically want to ignore SIGPIPE and
 * perform a (clean) exit on SIGINT/SIGTERM. Caught signals are
 * handled by setting a flag indicating that the signal has been
 * caught. The application itself is responsible for checking which
 * signals have been caught. Different signals are handled by
 * combining a single signal dispatch function with a pattern similar
 * to type-safe enums. All available signal handlers are created
 * during static initialization at program startup.
 **/
class SignalHandler
{
private:
    /**
     * Data structure keeping track of all registered signal handlers.
     **/
    static std::vector<SignalHandler*> _handlers;

    /**
     * The signal handled by this signal handler.
     **/
    int _signal;

    /**
     * State indicating if the signal handled by this signal handler
     * has been caught.
     **/
    std::atomic<int> _gotSignal;

    /**
     * Common signal handler for all caught signals. This method will
     * dispatch the signal to the appropriate static signal handler
     * instance.
     *
     * @param signal the caught signal
     **/
    static void handleSignal(int signal) noexcept;

    /**
     * This method is invoked by the common signal handling method to
     * dispatch the signal to the right static signal handler
     * instance.
     *
     * noinline to ensure consistent number of frames to skip for back-tracing.
     **/
    void gotSignal() noexcept __attribute__((noinline));


    /**
     * Internal async signal-safe function used to dump frame addresses of a signal-interrupted
     * thread to a shared buffer that will be read by the signalling thread.
     *
     * noinline to ensure consistent number of frames to skip for back-tracing.
     */
    static void dump_current_thread_stack_to_shared_state() noexcept __attribute__((noinline));

    /**
     * Create a signal handler for the given signal. This method is
     * private as all signal handlers should be created during
     * initialization.
     **/
    explicit SignalHandler(int signal);

    SignalHandler(const SignalHandler &) = delete;
    SignalHandler &operator=(const SignalHandler &) = delete;

    static SignalHandler USR2;

public:
    static SignalHandler HUP;
    static SignalHandler INT;
    static SignalHandler TERM;
    static SignalHandler CHLD;
    static SignalHandler PIPE;
    static SignalHandler SEGV;
    static SignalHandler ABRT;
    static SignalHandler BUS;
    static SignalHandler ILL;
    static SignalHandler TRAP;
    static SignalHandler FPE;
    static SignalHandler QUIT;
    static SignalHandler USR1;

    /**
     * Start catching this signal
     **/
    void hook();

    /**
     * Ignore this signal
     **/
    void ignore();

    /**
     * Check if this signal has been caught
     *
     * @return true if this signal has been caught
     **/
    bool check() const;

    /**
     * Clear the state indicating if this signal has been caught
     **/
    void clear();

    /**
     * Stop catching this signal
     **/
    void unhook();

    /**
     * Hook in signal handler for cross-thread stack tracing.
     *
     * Must be called at application init time if cross-thread tracing is wanted.
     */
    static void enable_cross_thread_stack_tracing();

    /**
     * Get the stack trace of the current point of execution of the thread referenced
     * by `thread_id`. This may be the same ID as the calling thread, in which case
     * the current call stack is returned. The pthread_t ID must be valid; invoking
     * this function without a valid ID is undefined behavior.
     *
     * Returned format is the same as that of vespalib::getStackTrace().
     *
     * Requires enable_cross_thread_stack_tracing() to have been called prior to creating
     * the thread referenced by `thread_id`.
     *
     * Due to potentially heavy internal synchronization overhead, this is not a function
     * that should be used in any kind of hot code path. Intended for debugging purposes.
     */
    static string get_cross_thread_stack_trace(pthread_t thread_id);

    static void shutdown();
};

} // vespalib