solarwinds_apm 5.1.9 → 6.0.0.preV1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +0 -1
- data/ext/oboe_metal/extconf.rb +19 -23
- data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
- data/ext/oboe_metal/src/VERSION +1 -1
- data/ext/oboe_metal/src/oboe_debug.h +1 -0
- data/lib/oboe_metal.rb +116 -80
- data/lib/rails/generators/solarwinds_apm/install_generator.rb +1 -2
- data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +44 -260
- data/lib/solarwinds_apm/api/current_trace_info.rb +148 -0
- data/lib/solarwinds_apm/api/tracing.rb +30 -0
- data/lib/solarwinds_apm/api/transaction_name.rb +57 -0
- data/lib/solarwinds_apm/api.rb +8 -15
- data/lib/solarwinds_apm/base.rb +4 -131
- data/lib/solarwinds_apm/config.rb +128 -175
- data/lib/solarwinds_apm/constants.rb +32 -0
- data/lib/solarwinds_apm/logger.rb +1 -1
- data/lib/solarwinds_apm/noop/context.rb +2 -5
- data/lib/solarwinds_apm/noop/metadata.rb +1 -2
- data/lib/solarwinds_apm/noop/profiling.rb +3 -7
- data/lib/solarwinds_apm/oboe_init_options.rb +71 -33
- data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +204 -0
- data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +163 -0
- data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +92 -0
- data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +72 -0
- data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +330 -0
- data/lib/solarwinds_apm/opentelemetry.rb +8 -0
- data/lib/solarwinds_apm/otel_config.rb +161 -0
- data/lib/solarwinds_apm/{inst → support}/logger_formatter.rb +5 -6
- data/lib/solarwinds_apm/{inst → support}/logging_log_event.rb +3 -6
- data/lib/solarwinds_apm/{inst → support}/lumberjack_formatter.rb +1 -4
- data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +27 -0
- data/lib/solarwinds_apm/support/swomarginalia/LICENSE +20 -0
- data/lib/solarwinds_apm/support/swomarginalia/README.md +41 -0
- data/lib/solarwinds_apm/support/swomarginalia/comment.rb +205 -0
- data/lib/solarwinds_apm/support/swomarginalia/load_swomarginalia.rb +48 -0
- data/lib/solarwinds_apm/support/swomarginalia/railtie.rb +22 -0
- data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +86 -0
- data/lib/solarwinds_apm/support/transaction_cache.rb +24 -0
- data/lib/solarwinds_apm/support/transaction_settings.rb +26 -209
- data/lib/solarwinds_apm/support/transformer.rb +56 -0
- data/lib/solarwinds_apm/support/txn_name_manager.rb +25 -0
- data/lib/solarwinds_apm/support/x_trace_options.rb +42 -26
- data/lib/solarwinds_apm/support.rb +33 -10
- data/lib/solarwinds_apm/support_report.rb +10 -32
- data/lib/solarwinds_apm/thread_local.rb +1 -1
- data/lib/solarwinds_apm/version.rb +4 -4
- data/lib/solarwinds_apm.rb +31 -26
- metadata +76 -121
- data/.dockerignore +0 -5
- data/.gitignore +0 -58
- data/.rubocop.yml +0 -29
- data/.whitesource +0 -22
- data/.yardopts +0 -7
- data/CHANGELOG-appoptics.md +0 -766
- data/CHANGELOG.md +0 -82
- data/CONFIG.md +0 -31
- data/Gemfile +0 -15
- data/README.md +0 -385
- data/bin/solarwinds_apm_config +0 -15
- data/examples/prepend.rb +0 -13
- data/examples/sdk_examples.rb +0 -158
- data/ext/oboe_metal/README.md +0 -69
- data/ext/oboe_metal/extconf_local.rb +0 -75
- data/ext/oboe_metal/lib/.keep +0 -0
- data/ext/oboe_metal/noop/noop.c +0 -8
- data/ext/oboe_metal/src/README.md +0 -6
- data/ext/oboe_metal/src/frames.cc +0 -247
- data/ext/oboe_metal/src/frames.h +0 -40
- data/ext/oboe_metal/src/logging.cc +0 -97
- data/ext/oboe_metal/src/logging.h +0 -34
- data/ext/oboe_metal/src/profiling.cc +0 -435
- data/ext/oboe_metal/src/profiling.h +0 -78
- data/ext/oboe_metal/test/CMakeLists.txt +0 -53
- data/ext/oboe_metal/test/FindGMock.cmake +0 -43
- data/ext/oboe_metal/test/README.md +0 -56
- data/ext/oboe_metal/test/frames_test.cc +0 -164
- data/ext/oboe_metal/test/profiling_test.cc +0 -93
- data/ext/oboe_metal/test/ruby_inc_dir.rb +0 -8
- data/ext/oboe_metal/test/ruby_prefix.rb +0 -8
- data/ext/oboe_metal/test/ruby_test_helper.rb +0 -67
- data/ext/oboe_metal/test/test.h +0 -11
- data/ext/oboe_metal/test/test_main.cc +0 -32
- data/init.rb +0 -4
- data/lib/solarwinds_apm/api/layerinit.rb +0 -41
- data/lib/solarwinds_apm/api/logging.rb +0 -356
- data/lib/solarwinds_apm/api/memcache.rb +0 -37
- data/lib/solarwinds_apm/api/metrics.rb +0 -63
- data/lib/solarwinds_apm/api/util.rb +0 -98
- data/lib/solarwinds_apm/frameworks/grape.rb +0 -96
- data/lib/solarwinds_apm/frameworks/padrino.rb +0 -78
- data/lib/solarwinds_apm/frameworks/rails/inst/action_controller.rb +0 -100
- data/lib/solarwinds_apm/frameworks/rails/inst/action_controller5.rb +0 -50
- data/lib/solarwinds_apm/frameworks/rails/inst/action_controller_api.rb +0 -50
- data/lib/solarwinds_apm/frameworks/rails/inst/action_view.rb +0 -88
- data/lib/solarwinds_apm/frameworks/rails/inst/active_record.rb +0 -26
- data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +0 -29
- data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +0 -22
- data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +0 -103
- data/lib/solarwinds_apm/frameworks/rails/inst/logger_formatters.rb +0 -14
- data/lib/solarwinds_apm/frameworks/rails.rb +0 -100
- data/lib/solarwinds_apm/frameworks/sinatra.rb +0 -96
- data/lib/solarwinds_apm/inst/bunny-client.rb +0 -157
- data/lib/solarwinds_apm/inst/bunny-consumer.rb +0 -102
- data/lib/solarwinds_apm/inst/curb.rb +0 -289
- data/lib/solarwinds_apm/inst/dalli.rb +0 -89
- data/lib/solarwinds_apm/inst/delayed_job.rb +0 -100
- data/lib/solarwinds_apm/inst/excon.rb +0 -113
- data/lib/solarwinds_apm/inst/faraday.rb +0 -96
- data/lib/solarwinds_apm/inst/graphql.rb +0 -206
- data/lib/solarwinds_apm/inst/grpc_client.rb +0 -147
- data/lib/solarwinds_apm/inst/grpc_server.rb +0 -119
- data/lib/solarwinds_apm/inst/httpclient.rb +0 -182
- data/lib/solarwinds_apm/inst/memcached.rb +0 -86
- data/lib/solarwinds_apm/inst/mongo.rb +0 -246
- data/lib/solarwinds_apm/inst/mongo2.rb +0 -225
- data/lib/solarwinds_apm/inst/moped.rb +0 -466
- data/lib/solarwinds_apm/inst/net_http.rb +0 -60
- data/lib/solarwinds_apm/inst/rack.rb +0 -223
- data/lib/solarwinds_apm/inst/rack_cache.rb +0 -35
- data/lib/solarwinds_apm/inst/redis.rb +0 -280
- data/lib/solarwinds_apm/inst/redis_v4.rb +0 -273
- data/lib/solarwinds_apm/inst/resque.rb +0 -129
- data/lib/solarwinds_apm/inst/rest-client.rb +0 -43
- data/lib/solarwinds_apm/inst/sequel.rb +0 -241
- data/lib/solarwinds_apm/inst/sidekiq-client.rb +0 -63
- data/lib/solarwinds_apm/inst/sidekiq-worker.rb +0 -64
- data/lib/solarwinds_apm/inst/typhoeus.rb +0 -90
- data/lib/solarwinds_apm/instrumentation.rb +0 -22
- data/lib/solarwinds_apm/loading.rb +0 -65
- data/lib/solarwinds_apm/ruby.rb +0 -35
- data/lib/solarwinds_apm/sdk/current_trace_info.rb +0 -123
- data/lib/solarwinds_apm/sdk/custom_metrics.rb +0 -94
- data/lib/solarwinds_apm/sdk/logging.rb +0 -37
- data/lib/solarwinds_apm/sdk/trace_context_headers.rb +0 -69
- data/lib/solarwinds_apm/sdk/tracing.rb +0 -432
- data/lib/solarwinds_apm/support/profiling.rb +0 -25
- data/lib/solarwinds_apm/support/trace_context.rb +0 -53
- data/lib/solarwinds_apm/support/trace_state.rb +0 -69
- data/lib/solarwinds_apm/support/trace_string.rb +0 -89
- data/lib/solarwinds_apm/support/transaction_metrics.rb +0 -67
- data/lib/solarwinds_apm/test.rb +0 -165
- data/lib/solarwinds_apm/util.rb +0 -426
- data/log/.keep +0 -0
- data/log/postgresql/.keep +0 -0
- data/solarwinds_apm.gemspec +0 -55
- data/yardoc_frontpage.md +0 -24
@@ -1,97 +0,0 @@
|
|
1
|
-
// Copyright (c) 2021 SolarWinds, LLC.
|
2
|
-
// All rights reserved.
|
3
|
-
|
4
|
-
#include "logging.h"
|
5
|
-
|
6
|
-
using namespace std;
|
7
|
-
|
8
|
-
const string Logging::profiling = "profiling";
|
9
|
-
const string Logging::ruby = "ruby";
|
10
|
-
const string Logging::entry = "entry";
|
11
|
-
const string Logging::info = "info";
|
12
|
-
const string Logging::exit = "exit";
|
13
|
-
|
14
|
-
Event *Logging::createEvent(Metadata &md, string &prof_op_id, bool entry_event) {
|
15
|
-
// startTrace does not add "Edge", for profiling we need to keep track of edges
|
16
|
-
// separately from the main trace metadata
|
17
|
-
|
18
|
-
oboe_metadata_t *md_t = md.metadata();
|
19
|
-
Event *event = Event::startTrace(md_t);
|
20
|
-
|
21
|
-
if (entry_event) {
|
22
|
-
event->addSpanRef(md_t);
|
23
|
-
} else {
|
24
|
-
event->addProfileEdge(prof_op_id);
|
25
|
-
event->addContextOpId(md_t);
|
26
|
-
}
|
27
|
-
prof_op_id.assign(event->opIdString());
|
28
|
-
|
29
|
-
return event;
|
30
|
-
}
|
31
|
-
|
32
|
-
bool Logging::log_profile_entry(Metadata &md, string &prof_op_id, pid_t tid, long interval) {
|
33
|
-
Event *event = Logging::createEvent(md, prof_op_id, true);
|
34
|
-
event->addInfo((char *)"Label", Logging::entry);
|
35
|
-
event->addInfo((char *)"Language", Logging::ruby);
|
36
|
-
event->addInfo((char *)"TID", (long)tid);
|
37
|
-
event->addInfo((char *)"Interval", interval);
|
38
|
-
|
39
|
-
struct timeval tv;
|
40
|
-
struct timezone *tz = NULL;
|
41
|
-
gettimeofday(&tv, tz);
|
42
|
-
event->addInfo((char *)"Timestamp_u", (long)tv.tv_sec * 1000000 + (long)tv.tv_usec);
|
43
|
-
|
44
|
-
return Logging::log_profile_event(event);
|
45
|
-
}
|
46
|
-
|
47
|
-
bool Logging::log_profile_exit(Metadata &md, string &prof_op_id, pid_t tid,
|
48
|
-
long *omitted, int num_omitted) {
|
49
|
-
Event *event = Logging::createEvent(md, prof_op_id);
|
50
|
-
event->addInfo((char *)"Label", Logging::exit);
|
51
|
-
event->addInfo((char *)"TID", (long)tid);
|
52
|
-
event->addInfo((char *)"SnapshotsOmitted", omitted, num_omitted);
|
53
|
-
|
54
|
-
struct timeval tv;
|
55
|
-
struct timezone *tz = NULL;
|
56
|
-
gettimeofday(&tv, tz);
|
57
|
-
event->addInfo((char *)"Timestamp_u", (long)tv.tv_sec * 1000000 + (long)tv.tv_usec);
|
58
|
-
|
59
|
-
return Logging::log_profile_event(event);
|
60
|
-
}
|
61
|
-
|
62
|
-
bool Logging::log_profile_snapshot(Metadata &md,
|
63
|
-
string &prof_op_id,
|
64
|
-
long timestamp,
|
65
|
-
std::vector<FrameData> const &new_frames,
|
66
|
-
long exited_frames,
|
67
|
-
long total_frames,
|
68
|
-
long *omitted,
|
69
|
-
int num_omitted,
|
70
|
-
pid_t tid) {
|
71
|
-
|
72
|
-
Event *event = Logging::createEvent(md, prof_op_id);
|
73
|
-
event->addInfo((char *)"Timestamp_u", timestamp);
|
74
|
-
event->addInfo((char *)"Label", Logging::info);
|
75
|
-
|
76
|
-
event->addInfo((char *)"SnapshotsOmitted", omitted, num_omitted);
|
77
|
-
event->addInfo((char *)"NewFrames", new_frames);
|
78
|
-
event->addInfo((char *)"FramesExited", exited_frames);
|
79
|
-
event->addInfo((char *)"FramesCount", total_frames);
|
80
|
-
event->addInfo((char *)"TID", (long)tid);
|
81
|
-
|
82
|
-
return Logging::log_profile_event(event);
|
83
|
-
}
|
84
|
-
|
85
|
-
bool Logging::log_profile_event(Event *event) {
|
86
|
-
event->addInfo((char *)"Spec", Logging::profiling);
|
87
|
-
event->addHostname();
|
88
|
-
event->addInfo((char *)"PID", (long)AO_GETPID());
|
89
|
-
event->addInfo((char *)"X-Trace", event->metadataString());
|
90
|
-
event->sendProfiling();
|
91
|
-
|
92
|
-
// see comment in oboe_api.cpp:
|
93
|
-
// "event needs to be deleted, it is managed by swig %newobject"
|
94
|
-
// !!! It needs to be deleted, I tested it !!!
|
95
|
-
delete event;
|
96
|
-
return true;
|
97
|
-
}
|
@@ -1,34 +0,0 @@
|
|
1
|
-
// Copyright (c) 2021 SolarWinds, LLC.
|
2
|
-
// All rights reserved.
|
3
|
-
|
4
|
-
#ifndef LOGGING_H
|
5
|
-
#define LOGGING_H
|
6
|
-
|
7
|
-
#include "oboe_api.h"
|
8
|
-
|
9
|
-
using namespace std;
|
10
|
-
|
11
|
-
// extern "C" int oboe_gettimeofday(struct timeval *tv);
|
12
|
-
|
13
|
-
class Logging {
|
14
|
-
public:
|
15
|
-
static const string profiling, ruby, entry, info, exit;
|
16
|
-
static bool log_profile_entry(Metadata &md, string &prof_op_id, pid_t tid, long interval);
|
17
|
-
static bool log_profile_exit(Metadata &md, string &prof_op_id, pid_t tid,
|
18
|
-
long *omitted, int num_omitted);
|
19
|
-
static bool log_profile_snapshot(Metadata &md,
|
20
|
-
string &prof_op_id,
|
21
|
-
long timestamp,
|
22
|
-
std::vector<FrameData> const &new_frames,
|
23
|
-
long exited_frames,
|
24
|
-
long total_frames,
|
25
|
-
long *omitted,
|
26
|
-
int num_omitted,
|
27
|
-
pid_t tid);
|
28
|
-
|
29
|
-
private:
|
30
|
-
static Event *createEvent(Metadata &md, string &prof_op_id, bool entry_event = false);
|
31
|
-
static bool log_profile_event(Event *event);
|
32
|
-
};
|
33
|
-
|
34
|
-
#endif //LOGGING_H
|
@@ -1,435 +0,0 @@
|
|
1
|
-
// Copyright (c) 2021 SolarWinds, LLC.
|
2
|
-
// All rights reserved.
|
3
|
-
|
4
|
-
#include "profiling.h"
|
5
|
-
|
6
|
-
#include <ruby/debug.h>
|
7
|
-
#include <signal.h>
|
8
|
-
#include <time.h>
|
9
|
-
|
10
|
-
#include <atomic>
|
11
|
-
#include <unordered_map>
|
12
|
-
#include <vector>
|
13
|
-
|
14
|
-
#include "frames.h"
|
15
|
-
#include "logging.h"
|
16
|
-
#include "oboe_api.h"
|
17
|
-
|
18
|
-
|
19
|
-
#define TIMER_SIG SIGRTMAX // the timer notification signal
|
20
|
-
|
21
|
-
using namespace std;
|
22
|
-
|
23
|
-
static atomic_bool running;
|
24
|
-
atomic_bool profiling_shut_down; // !! can't be static because of tests
|
25
|
-
|
26
|
-
// need to initialize here, hangs if it is done inside the signal handler
|
27
|
-
// these are reused for every snapshot
|
28
|
-
static VALUE frames_buffer[BUF_SIZE];
|
29
|
-
static int lines_buffer[BUF_SIZE];
|
30
|
-
|
31
|
-
|
32
|
-
static long configured_interval = 10; // in milliseconds, initializing in case Ruby forgets to
|
33
|
-
static long current_interval = 10;
|
34
|
-
timer_t timerid;
|
35
|
-
|
36
|
-
typedef struct prof_data {
|
37
|
-
bool running_p = false;
|
38
|
-
Metadata md = Metadata(Context::get());
|
39
|
-
string prof_op_id;
|
40
|
-
|
41
|
-
VALUE prev_frames_buffer[BUF_SIZE];
|
42
|
-
int prev_num = 0;
|
43
|
-
long omitted[BUF_SIZE];
|
44
|
-
int omitted_num = 0;
|
45
|
-
} prof_data_t;
|
46
|
-
|
47
|
-
unordered_map<pid_t, prof_data_t> prof_data_map;
|
48
|
-
|
49
|
-
const string Profiling::string_job_handler = "Profiling::profiler_job_handler()";
|
50
|
-
const string Profiling::string_gc_handler = "Profiling::profiler_gc_handler()";
|
51
|
-
const string Profiling::string_signal_handler = "Profiling::profiler_signal_handler()";
|
52
|
-
const string Profiling::string_stop = "Profiling::profiling_stop()";
|
53
|
-
|
54
|
-
// for debugging only
|
55
|
-
void print_prof_data_map() {
|
56
|
-
pid_t tid = AO_GETTID;
|
57
|
-
Metadata md_str(prof_data_map[tid].md);
|
58
|
-
cout << tid << ", " << prof_data_map[tid].running_p << ", " << prof_data_map[tid].prof_op_id << ", ";
|
59
|
-
cout << md_str.toString() << ", " << prof_data_map[tid].prev_num << ", " << prof_data_map[tid].omitted_num << endl;
|
60
|
-
}
|
61
|
-
|
62
|
-
long ts_now() {
|
63
|
-
struct timeval tv;
|
64
|
-
struct timezone *tz = NULL;
|
65
|
-
gettimeofday(&tv, tz);
|
66
|
-
return (long)tv.tv_sec * 1000000 + (long)tv.tv_usec;
|
67
|
-
}
|
68
|
-
|
69
|
-
// try catch block to be used inside functions that return an int
|
70
|
-
// shuts down profiling and returns -1 on error
|
71
|
-
int Profiling::try_catch_shutdown(std::function<int()> f, const string& fun_name) {
|
72
|
-
try {
|
73
|
-
return f();
|
74
|
-
} catch (const std::exception &e) {
|
75
|
-
string msg = "Exception in " + fun_name + ", can't recover, profiling shutting down";
|
76
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, e.what());
|
77
|
-
OBOE_DEBUG_LOG_HIGH(OBOE_MODULE_RUBY, msg.c_str());
|
78
|
-
Profiling::shut_down();
|
79
|
-
return -1;
|
80
|
-
} catch (...) {
|
81
|
-
string msg = "Exception in " + fun_name + ", can't recover, profiling shutting down";
|
82
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, msg.c_str());
|
83
|
-
Profiling::shut_down();
|
84
|
-
return -1;
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
void Profiling::profiler_record_frames() {
|
89
|
-
pid_t tid = AO_GETTID;
|
90
|
-
long ts = ts_now();
|
91
|
-
|
92
|
-
// check if this thread is being profiled
|
93
|
-
if (prof_data_map[tid].running_p) {
|
94
|
-
// executes in the same thread as rb_postponed_job was called from
|
95
|
-
|
96
|
-
// get the frames
|
97
|
-
// won't overrun frames buffer, because size is set in arg 2
|
98
|
-
int num = rb_profile_frames(0, sizeof(frames_buffer) / sizeof(VALUE), frames_buffer, lines_buffer);
|
99
|
-
|
100
|
-
Profiling::process_snapshot(frames_buffer, num, tid, ts);
|
101
|
-
}
|
102
|
-
|
103
|
-
// add this timestamp as omitted to other running threads that are profiled
|
104
|
-
for (pair<const pid_t, prof_data_t> &ele : prof_data_map) {
|
105
|
-
if (ele.second.running_p && ele.first != tid) {
|
106
|
-
frames_buffer[0] = PR_OTHER_THREAD;
|
107
|
-
Profiling::process_snapshot(frames_buffer, 1, ele.first, ts);
|
108
|
-
}
|
109
|
-
}
|
110
|
-
}
|
111
|
-
|
112
|
-
void Profiling::profiler_record_gc() {
|
113
|
-
pid_t tid = AO_GETTID;
|
114
|
-
long ts = ts_now();
|
115
|
-
|
116
|
-
// check if this thread is being profiled
|
117
|
-
if (prof_data_map[tid].running_p) {
|
118
|
-
frames_buffer[0] = PR_IN_GC;
|
119
|
-
Profiling::process_snapshot(frames_buffer, 1, tid, ts);
|
120
|
-
}
|
121
|
-
|
122
|
-
// add this timestamp as omitted to other running threads that are profiled
|
123
|
-
for (pair<const pid_t, prof_data_t> &ele : prof_data_map) {
|
124
|
-
if (ele.second.running_p && ele.first != tid) {
|
125
|
-
frames_buffer[0] = PR_OTHER_THREAD;
|
126
|
-
Profiling::process_snapshot(frames_buffer, 1, ele.first, ts);
|
127
|
-
}
|
128
|
-
}
|
129
|
-
}
|
130
|
-
|
131
|
-
void Profiling::send_omitted(pid_t tid, long ts) {
|
132
|
-
static vector<FrameData> empty;
|
133
|
-
Logging::log_profile_snapshot(prof_data_map[tid].md,
|
134
|
-
prof_data_map[tid].prof_op_id,
|
135
|
-
ts, // timestamp
|
136
|
-
empty, // <vector> new frames
|
137
|
-
0, // number of exited frames
|
138
|
-
prof_data_map[tid].prev_num, // total number of frames
|
139
|
-
prof_data_map[tid].omitted, // array of timestamps of omitted snapshots
|
140
|
-
prof_data_map[tid].omitted_num, // number of omitted snapshots
|
141
|
-
tid); // thread id
|
142
|
-
|
143
|
-
prof_data_map[tid].omitted_num = 0;
|
144
|
-
}
|
145
|
-
|
146
|
-
void Profiling::process_snapshot(VALUE *frames_buffer, int num, pid_t tid, long ts) {
|
147
|
-
int num_new = 0;
|
148
|
-
int num_exited = 0;
|
149
|
-
vector<FrameData> new_frames;
|
150
|
-
|
151
|
-
num = Frames::remove_garbage(frames_buffer, num);
|
152
|
-
|
153
|
-
// find the number of matching frames from the top
|
154
|
-
int num_match = Frames::num_matching(frames_buffer,
|
155
|
-
num,
|
156
|
-
prof_data_map[tid].prev_frames_buffer,
|
157
|
-
prof_data_map[tid].prev_num);
|
158
|
-
num_new = num - num_match;
|
159
|
-
num_exited = prof_data_map[tid].prev_num - num_match;
|
160
|
-
|
161
|
-
if (num_new == 0 && num_exited == 0) {
|
162
|
-
prof_data_map[tid].omitted[prof_data_map[tid].omitted_num] = ts;
|
163
|
-
prof_data_map[tid].omitted_num++;
|
164
|
-
|
165
|
-
// the omitted buffer can fill up if the interval is small
|
166
|
-
// and the stack doesn't change
|
167
|
-
// We need to send a profiling event with the timestamps when it is full
|
168
|
-
if (prof_data_map[tid].omitted_num >= BUF_SIZE) {
|
169
|
-
Profiling::send_omitted(tid, ts);
|
170
|
-
}
|
171
|
-
return;
|
172
|
-
}
|
173
|
-
|
174
|
-
Frames::collect_frame_data(frames_buffer, num_new, new_frames);
|
175
|
-
|
176
|
-
Logging::log_profile_snapshot(prof_data_map[tid].md,
|
177
|
-
prof_data_map[tid].prof_op_id,
|
178
|
-
ts, // timestamp
|
179
|
-
new_frames, // <vector> new frames
|
180
|
-
num_exited, // number of exited frames
|
181
|
-
num, // total number of frames
|
182
|
-
prof_data_map[tid].omitted, // array of timestamps of omitted snapshots
|
183
|
-
prof_data_map[tid].omitted_num, // number of omitted snapshots
|
184
|
-
tid); // thread id
|
185
|
-
|
186
|
-
prof_data_map[tid].omitted_num = 0;
|
187
|
-
prof_data_map[tid].prev_num = num;
|
188
|
-
for (int i = 0; i < num; ++i)
|
189
|
-
prof_data_map[tid].prev_frames_buffer[i] = frames_buffer[i];
|
190
|
-
}
|
191
|
-
|
192
|
-
void Profiling::profiler_job_handler(void *data) {
|
193
|
-
static atomic_bool in_job_handler{false};
|
194
|
-
|
195
|
-
// atomically replaces the value of the object, returns the value held previously
|
196
|
-
if (in_job_handler.exchange(true)) return;
|
197
|
-
|
198
|
-
try_catch_shutdown([&]() {
|
199
|
-
Profiling::profiler_record_frames();
|
200
|
-
return 0; // block needs an int returned
|
201
|
-
}, Profiling::string_job_handler);
|
202
|
-
|
203
|
-
in_job_handler = false;
|
204
|
-
}
|
205
|
-
|
206
|
-
void Profiling::profiler_gc_handler(void *data) {
|
207
|
-
static atomic_bool in_gc_handler{false};
|
208
|
-
|
209
|
-
// atomically replaces the value of the object, returns the value held previously
|
210
|
-
if (in_gc_handler.exchange(true)) return;
|
211
|
-
|
212
|
-
try_catch_shutdown([]() {
|
213
|
-
Profiling::profiler_record_gc();
|
214
|
-
return 0; // block needs an int returned
|
215
|
-
}, Profiling::string_gc_handler);
|
216
|
-
|
217
|
-
in_gc_handler = false;
|
218
|
-
}
|
219
|
-
|
220
|
-
////////////////////////////////////////////////////////////////////////////////
|
221
|
-
// THIS IS THE SIGNAL HANDLER FUNCTION
|
222
|
-
// ONLY ASYNC-SAFE FUNCTIONS ALLOWED IN HERE (no exception handling !!!)
|
223
|
-
////////////////////////////////////////////////////////////////////////////////
|
224
|
-
extern "C" void profiler_signal_handler(int sigint, siginfo_t *siginfo, void *ucontext) {
|
225
|
-
if (!ruby_native_thread_p()) return;
|
226
|
-
static std::atomic_bool in_signal_handler{false};
|
227
|
-
|
228
|
-
// atomically replaces the value of the object, returns the value held previously
|
229
|
-
// also keeps in_signal_handler lock_free -> async-safe
|
230
|
-
if (in_signal_handler.exchange(true)) return;
|
231
|
-
|
232
|
-
// the following two ruby c-functions are async safe
|
233
|
-
if (rb_during_gc())
|
234
|
-
{
|
235
|
-
rb_postponed_job_register(0, Profiling::profiler_gc_handler, (void *)0);
|
236
|
-
} else {
|
237
|
-
rb_postponed_job_register(0, Profiling::profiler_job_handler, (void *)0);
|
238
|
-
}
|
239
|
-
|
240
|
-
in_signal_handler = false;
|
241
|
-
}
|
242
|
-
|
243
|
-
void Profiling::profiling_start(pid_t tid) {
|
244
|
-
prof_data_map[tid].md = Metadata(Context::get());
|
245
|
-
prof_data_map[tid].prev_num = 0;
|
246
|
-
prof_data_map[tid].omitted_num = 0;
|
247
|
-
prof_data_map[tid].running_p = true;
|
248
|
-
|
249
|
-
Logging::log_profile_entry(prof_data_map[tid].md,
|
250
|
-
prof_data_map[tid].prof_op_id,
|
251
|
-
tid,
|
252
|
-
current_interval);
|
253
|
-
|
254
|
-
if (!running.exchange(true)) {
|
255
|
-
// start timer with interval timer spec
|
256
|
-
struct itimerspec ts;
|
257
|
-
ts.it_interval.tv_sec = 0;
|
258
|
-
ts.it_interval.tv_nsec = current_interval * 1000000;
|
259
|
-
ts.it_value.tv_sec = 0;
|
260
|
-
ts.it_value.tv_nsec = ts.it_interval.tv_nsec;
|
261
|
-
|
262
|
-
// global timer_t timerid points to timer created in Init_profiling
|
263
|
-
if (timer_settime(timerid, 0, &ts, NULL) == -1) {
|
264
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, "timer_settime() failed");
|
265
|
-
shut_down();
|
266
|
-
}
|
267
|
-
}
|
268
|
-
}
|
269
|
-
|
270
|
-
VALUE Profiling::profiling_stop(pid_t tid) {
|
271
|
-
if (!running.exchange(false)) return Qfalse;
|
272
|
-
|
273
|
-
int result = try_catch_shutdown([&]() {
|
274
|
-
// stop the timer, needs both (value and interval) set to 0
|
275
|
-
struct itimerspec ts;
|
276
|
-
ts.it_value.tv_sec = 0;
|
277
|
-
ts.it_value.tv_nsec = 0;
|
278
|
-
ts.it_interval.tv_sec = 0;
|
279
|
-
ts.it_interval.tv_nsec = 0;
|
280
|
-
|
281
|
-
if (timer_settime(timerid, 0, &ts, NULL) == -1) {
|
282
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, "timer_settime() failed");
|
283
|
-
shut_down();
|
284
|
-
}
|
285
|
-
|
286
|
-
Logging::log_profile_exit(prof_data_map[tid].md,
|
287
|
-
prof_data_map[tid].prof_op_id,
|
288
|
-
tid,
|
289
|
-
prof_data_map[tid].omitted,
|
290
|
-
prof_data_map[tid].omitted_num);
|
291
|
-
|
292
|
-
prof_data_map[tid].running_p = false;
|
293
|
-
return 0; // block needs an int returned
|
294
|
-
}, Profiling::string_stop);
|
295
|
-
|
296
|
-
return (result == 0) ? Qtrue : Qfalse;
|
297
|
-
}
|
298
|
-
|
299
|
-
VALUE Profiling::set_interval(VALUE self, VALUE val) {
|
300
|
-
if (!FIXNUM_P(val)) return Qfalse;
|
301
|
-
|
302
|
-
configured_interval = FIX2INT(val);
|
303
|
-
|
304
|
-
return INT2FIX(configured_interval);
|
305
|
-
}
|
306
|
-
|
307
|
-
VALUE Profiling::get_interval() {
|
308
|
-
return INT2FIX(current_interval);
|
309
|
-
}
|
310
|
-
|
311
|
-
VALUE Profiling::profiling_run(VALUE self, VALUE rb_thread_val, VALUE interval) {
|
312
|
-
rb_need_block(); // checks if function is called with a block in Ruby
|
313
|
-
if (profiling_shut_down || OboeProfiling::get_interval() == 0) {
|
314
|
-
return rb_yield(Qundef);
|
315
|
-
}
|
316
|
-
|
317
|
-
if (FIXNUM_P(interval)) configured_interval = FIX2INT(interval);
|
318
|
-
current_interval = max(configured_interval, (long)OboeProfiling::get_interval());
|
319
|
-
|
320
|
-
// !!!!! Can't use try_catch_shutdown() here, MAKES rb_ensure cause a memory leak !!!!!
|
321
|
-
try {
|
322
|
-
pid_t tid = AO_GETTID;
|
323
|
-
profiling_start(tid);
|
324
|
-
rb_ensure(reinterpret_cast<VALUE (*)(...)>(rb_yield), Qundef,
|
325
|
-
reinterpret_cast<VALUE (*)(...)>(profiling_stop), tid);
|
326
|
-
return Qtrue;
|
327
|
-
} catch (const std::exception &e) {
|
328
|
-
string msg = "Exception in Profiling::profiling_run(), can't recover, profiling shutting down";
|
329
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, e.what());
|
330
|
-
OBOE_DEBUG_LOG_HIGH(OBOE_MODULE_RUBY, msg.c_str());
|
331
|
-
shut_down();
|
332
|
-
return Qfalse;
|
333
|
-
} catch (...) {
|
334
|
-
string msg = "Exception in Profiling::profiling_run(), can't recover, profiling shutting down";
|
335
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, msg.c_str());
|
336
|
-
shut_down();
|
337
|
-
return Qfalse;
|
338
|
-
}
|
339
|
-
|
340
|
-
return Qfalse;
|
341
|
-
}
|
342
|
-
|
343
|
-
// in case C++ misbehaves we will stop profiling
|
344
|
-
// to be used when catching exceptions
|
345
|
-
void Profiling::shut_down() {
|
346
|
-
static atomic_bool ending{false};
|
347
|
-
|
348
|
-
if (ending.exchange(true)) return;
|
349
|
-
|
350
|
-
// avoid running any more profiling
|
351
|
-
profiling_shut_down = true;
|
352
|
-
|
353
|
-
// stop all profiling, the last one also stops the timer/signals
|
354
|
-
for (pair<const pid_t, prof_data_t> &ele : prof_data_map) {
|
355
|
-
profiling_stop(ele.first);
|
356
|
-
}
|
357
|
-
}
|
358
|
-
|
359
|
-
VALUE Profiling::getTid() {
|
360
|
-
pid_t tid = AO_GETTID;
|
361
|
-
|
362
|
-
return INT2NUM(tid);
|
363
|
-
}
|
364
|
-
|
365
|
-
static void
|
366
|
-
prof_atfork_prepare(void) {
|
367
|
-
// cout << "Parent getting ready" << endl;
|
368
|
-
}
|
369
|
-
|
370
|
-
static void
|
371
|
-
prof_atfork_parent(void) {
|
372
|
-
// cout << "Parent let child loose" << endl;
|
373
|
-
}
|
374
|
-
|
375
|
-
// make sure new processes have a clean slate for profiling
|
376
|
-
static void
|
377
|
-
prof_atfork_child(void) {
|
378
|
-
// cout << "A child is born" << endl;
|
379
|
-
Frames::clear_cached_frames();
|
380
|
-
prof_data_map.clear();
|
381
|
-
running = false;
|
382
|
-
|
383
|
-
// make sure it has a timer ready, it is a per-process-timer
|
384
|
-
Profiling::create_timer();
|
385
|
-
}
|
386
|
-
|
387
|
-
void Profiling::create_sigaction() {
|
388
|
-
struct sigaction sa;
|
389
|
-
// what happens if there is another action for the same signal?
|
390
|
-
// => last one defined wins!
|
391
|
-
sa.sa_sigaction = profiler_signal_handler;
|
392
|
-
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
393
|
-
sigemptyset(&sa.sa_mask);
|
394
|
-
if (sigaction(TIMER_SIG, &sa, NULL) == -1) {
|
395
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, "sigaction() failed");
|
396
|
-
profiling_shut_down = true; // no profiling without sigaction
|
397
|
-
}
|
398
|
-
}
|
399
|
-
|
400
|
-
void Profiling::create_timer() {
|
401
|
-
struct sigevent sev;
|
402
|
-
|
403
|
-
sev.sigev_value.sival_ptr = &timerid;
|
404
|
-
sev.sigev_notify = SIGEV_SIGNAL; /* Notify via signal */
|
405
|
-
sev.sigev_signo = TIMER_SIG; /* Notify using this signal */
|
406
|
-
|
407
|
-
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
|
408
|
-
OBOE_DEBUG_LOG_ERROR(OBOE_MODULE_RUBY, "timer_create() failed");
|
409
|
-
profiling_shut_down = true; // no profiling without clock
|
410
|
-
}
|
411
|
-
}
|
412
|
-
|
413
|
-
extern "C" void Init_profiling(void) {
|
414
|
-
// assign values to global atomic vars that know about state of profiling
|
415
|
-
running = false;
|
416
|
-
profiling_shut_down = false;
|
417
|
-
|
418
|
-
// prep data structures
|
419
|
-
Profiling::create_sigaction();
|
420
|
-
Profiling::create_timer();
|
421
|
-
Frames::reserve_cached_frames();
|
422
|
-
|
423
|
-
// create Ruby Module: SolarWindsAPM::CProfiler
|
424
|
-
static VALUE rb_mSolarWindsAPM = rb_define_module("SolarWindsAPM");
|
425
|
-
static VALUE rb_mCProfiler = rb_define_module_under(rb_mSolarWindsAPM, "CProfiler");
|
426
|
-
|
427
|
-
rb_define_singleton_method(rb_mCProfiler, "get_interval", reinterpret_cast<VALUE (*)(...)>(Profiling::get_interval), 0);
|
428
|
-
rb_define_singleton_method(rb_mCProfiler, "set_interval", reinterpret_cast<VALUE (*)(...)>(Profiling::set_interval), 1);
|
429
|
-
rb_define_singleton_method(rb_mCProfiler, "run", reinterpret_cast<VALUE (*)(...)>(Profiling::profiling_run), 2);
|
430
|
-
rb_define_singleton_method(rb_mCProfiler, "get_tid", reinterpret_cast<VALUE (*)(...)>(Profiling::getTid), 0);
|
431
|
-
|
432
|
-
pthread_atfork(prof_atfork_prepare,
|
433
|
-
prof_atfork_parent,
|
434
|
-
prof_atfork_child);
|
435
|
-
}
|
@@ -1,78 +0,0 @@
|
|
1
|
-
// Copyright (c) 2021 SolarWinds, LLC.
|
2
|
-
// All rights reserved.
|
3
|
-
|
4
|
-
#ifndef PROFILING_H
|
5
|
-
#define PROFILING_H
|
6
|
-
|
7
|
-
#include <ruby/ruby.h>
|
8
|
-
#include <ruby/debug.h>
|
9
|
-
#include <signal.h>
|
10
|
-
#include <time.h>
|
11
|
-
|
12
|
-
#include <atomic>
|
13
|
-
#include <functional>
|
14
|
-
#include <unordered_map>
|
15
|
-
#include <vector>
|
16
|
-
|
17
|
-
#include "frames.h"
|
18
|
-
#include "logging.h"
|
19
|
-
#include "oboe_api.h"
|
20
|
-
|
21
|
-
#define BUF_SIZE 2048
|
22
|
-
|
23
|
-
// these definitions are based on the assumption that there are no
|
24
|
-
// frames with VALUE == 1 or VALUE == 2 in Ruby
|
25
|
-
// profiling won't blow up if there are, because there is also a check to see
|
26
|
-
// if the stack has size == 1 when assuming what these frames refer to
|
27
|
-
#define PR_OTHER_THREAD 1
|
28
|
-
#define PR_IN_GC 2
|
29
|
-
|
30
|
-
#if !defined(AO_GETTID)
|
31
|
-
#if defined(_WIN32)
|
32
|
-
#define AO_GETTID GetCurrentThreadId
|
33
|
-
#else
|
34
|
-
#include <unistd.h>
|
35
|
-
#include <sys/syscall.h>
|
36
|
-
#ifdef SYS_gettid
|
37
|
-
#define AO_GETTID syscall(SYS_gettid);
|
38
|
-
#endif
|
39
|
-
#endif
|
40
|
-
#endif
|
41
|
-
|
42
|
-
class Profiling {
|
43
|
-
public:
|
44
|
-
static const string string_job_handler, string_gc_handler, string_signal_handler, string_stop;
|
45
|
-
|
46
|
-
static void create_sigaction();
|
47
|
-
static void create_timer();
|
48
|
-
|
49
|
-
static int try_catch_shutdown(std::function<int()>, const string& fun_name);
|
50
|
-
static void profiler_job_handler(void* data);
|
51
|
-
static void profiler_gc_handler(void* data);
|
52
|
-
// This is used when catching an exception
|
53
|
-
static void shut_down();
|
54
|
-
|
55
|
-
// The following are made available to Ruby and have to return VALUE
|
56
|
-
static VALUE profiling_run(VALUE self, VALUE rb_thread_val, VALUE interval);
|
57
|
-
static VALUE get_interval();
|
58
|
-
static VALUE set_interval(VALUE self, VALUE interval);
|
59
|
-
static VALUE getTid();
|
60
|
-
|
61
|
-
private:
|
62
|
-
static void profiling_start(pid_t tid);
|
63
|
-
|
64
|
-
// This is used via rb_ensure and therefore needs VALUE as a return type
|
65
|
-
static VALUE profiling_stop(pid_t tid);
|
66
|
-
|
67
|
-
static void process_snapshot(VALUE* frames_buffer,
|
68
|
-
int num,
|
69
|
-
pid_t tid,
|
70
|
-
long ts);
|
71
|
-
static void profiler_record_frames();
|
72
|
-
static void profiler_record_gc();
|
73
|
-
static void send_omitted(pid_t tid, long ts);
|
74
|
-
};
|
75
|
-
|
76
|
-
extern "C" void Init_profiling(void);
|
77
|
-
|
78
|
-
#endif // PROFILING_H
|
@@ -1,53 +0,0 @@
|
|
1
|
-
cmake_minimum_required(VERSION 3.13)
|
2
|
-
project(test)
|
3
|
-
|
4
|
-
# specify the C++ standard
|
5
|
-
set(CMAKE_CXX_STANDARD 11)
|
6
|
-
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
7
|
-
# set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR}/FindGMock.cmake)
|
8
|
-
|
9
|
-
include(FetchContent)
|
10
|
-
FetchContent_Declare(
|
11
|
-
googletest
|
12
|
-
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
|
13
|
-
# URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip
|
14
|
-
)
|
15
|
-
|
16
|
-
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
17
|
-
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
18
|
-
FetchContent_MakeAvailable(googletest)
|
19
|
-
|
20
|
-
include_directories(
|
21
|
-
${gtest_SOURCE_DIR}/include
|
22
|
-
../src/
|
23
|
-
$ENV{RUBY_INC_DIR}
|
24
|
-
$ENV{RUBY_INC_DIR}/x86_64-linux/
|
25
|
-
)
|
26
|
-
|
27
|
-
link_directories(
|
28
|
-
# /usr/lib/
|
29
|
-
$ENV{RUBY_PREFIX}/lib/
|
30
|
-
../../../lib/
|
31
|
-
../lib
|
32
|
-
)
|
33
|
-
|
34
|
-
enable_testing()
|
35
|
-
set (sources
|
36
|
-
test_main.cc
|
37
|
-
frames_test.cc
|
38
|
-
profiling_test.cc
|
39
|
-
)
|
40
|
-
|
41
|
-
## Link runTests with what we want to test and the GTest and pthread library
|
42
|
-
add_executable(runTests ${sources})
|
43
|
-
target_link_libraries(runTests
|
44
|
-
# ${GTEST_LIBRARIES}
|
45
|
-
gtest
|
46
|
-
solarwinds_apm.so
|
47
|
-
liboboe.so
|
48
|
-
libruby.so
|
49
|
-
pthread
|
50
|
-
)
|
51
|
-
|
52
|
-
include(GoogleTest)
|
53
|
-
gtest_discover_tests(runTests)
|