Skip to content

Commit 4e361d1

Browse files
committed
Category specific runtime callback
1 parent 5da5055 commit 4e361d1

File tree

3 files changed

+466
-57
lines changed

3 files changed

+466
-57
lines changed

debug_runtime_callback.rb

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
require 'English'
2+
require 'json'
3+
require 'webrick'
4+
require 'fiddle'
5+
require 'datadog'
6+
7+
# Simple HTTP server to capture crash reports
8+
def start_crash_server(port)
9+
server = WEBrick::HTTPServer.new(
10+
Port: port,
11+
Logger: WEBrick::Log.new(File.open(File::NULL, 'w')),
12+
AccessLog: []
13+
)
14+
15+
crash_report = nil
16+
17+
server.mount_proc '/' do |req, res|
18+
if req.request_method == 'POST'
19+
body = req.body
20+
crash_report = JSON.parse(body) rescue body
21+
puts '=== CRASH REPORT RECEIVED ==='
22+
puts JSON.pretty_generate(crash_report) if crash_report.is_a?(Hash)
23+
puts '=== END CRASH REPORT ==='
24+
end
25+
res.body = '{}'
26+
end
27+
28+
Thread.new { server.start }
29+
[server, proc { crash_report }]
30+
end
31+
32+
# Deep nested Ruby functions to create a complex runtime stack
33+
class ComplexStackBuilder
34+
attr_accessor :counter, :data_store
35+
36+
def initialize
37+
@counter = 0
38+
@data_store = {}
39+
end
40+
41+
def increment_and_store(key, value)
42+
@counter += 1
43+
@data_store[key] = value
44+
end
45+
end
46+
47+
def database_layer_query(query_id)
48+
builder = ComplexStackBuilder.new
49+
builder.increment_and_store("query_#{query_id}", "SELECT * FROM users WHERE id = #{query_id}")
50+
yield builder if block_given?
51+
builder
52+
end
53+
54+
def orm_layer_find(model_id)
55+
puts "ORM Layer: Finding model #{model_id}"
56+
database_layer_query(model_id) do |builder|
57+
builder.increment_and_store('orm_operation', 'find')
58+
service_layer_process(builder)
59+
end
60+
end
61+
62+
def service_layer_process(builder)
63+
puts 'Service Layer: Processing business logic'
64+
builder.increment_and_store('service_action', 'user_authentication')
65+
controller_layer_handle(builder)
66+
end
67+
68+
def controller_layer_handle(builder)
69+
puts 'Controller Layer: Handling HTTP request'
70+
builder.increment_and_store('http_method', 'POST')
71+
middleware_stack_authenticate(builder)
72+
end
73+
74+
def middleware_stack_authenticate(builder)
75+
puts 'Middleware: Authentication check'
76+
builder.increment_and_store('auth_status', 'validating')
77+
middleware_stack_authorize(builder)
78+
end
79+
80+
def middleware_stack_authorize(builder)
81+
puts 'Middleware: Authorization check'
82+
builder.increment_and_store('auth_permissions', ['read', 'write'])
83+
middleware_stack_logging(builder)
84+
end
85+
86+
def middleware_stack_logging(builder)
87+
puts 'Middleware: Request logging'
88+
builder.increment_and_store('request_id', "req_#{rand(10000)}")
89+
application_layer_router(builder)
90+
end
91+
92+
def application_layer_router(builder)
93+
puts 'Application Router: Route resolution'
94+
builder.increment_and_store('route', '/api/v1/users')
95+
application_layer_dispatcher(builder)
96+
end
97+
98+
def application_layer_dispatcher(builder)
99+
puts 'Application Dispatcher: Request dispatch'
100+
builder.increment_and_store('dispatch_time', Time.now.to_f)
101+
framework_layer_request_handler(builder)
102+
end
103+
104+
def framework_layer_request_handler(builder)
105+
puts 'Framework: Request handler initialization'
106+
builder.increment_and_store('handler_type', 'RestfulHandler')
107+
framework_layer_response_builder(builder)
108+
end
109+
110+
def framework_layer_response_builder(builder)
111+
puts 'Framework: Response builder setup'
112+
builder.increment_and_store('response_format', 'json')
113+
business_logic_user_service(builder)
114+
end
115+
116+
def business_logic_user_service(builder)
117+
puts 'Business Logic: User service operations'
118+
builder.increment_and_store('user_operation', 'profile_update')
119+
business_logic_validation(builder)
120+
end
121+
122+
def business_logic_validation(builder)
123+
puts 'Business Logic: Input validation'
124+
builder.increment_and_store('validation_rules', ['email_format', 'password_strength'])
125+
business_logic_transformation(builder)
126+
end
127+
128+
def business_logic_transformation(builder)
129+
puts 'Business Logic: Data transformation'
130+
builder.increment_and_store('transform_operations', ['normalize', 'sanitize'])
131+
data_access_layer_connection(builder)
132+
end
133+
134+
def data_access_layer_connection(builder)
135+
puts 'Data Access: Database connection setup'
136+
builder.increment_and_store('db_connection', 'postgresql://localhost:5432')
137+
data_access_layer_transaction(builder)
138+
end
139+
140+
def data_access_layer_transaction(builder)
141+
puts 'Data Access: Transaction management'
142+
builder.increment_and_store('transaction_id', "tx_#{rand(100000)}")
143+
data_access_layer_query_execution(builder)
144+
end
145+
146+
def data_access_layer_query_execution(builder)
147+
puts 'Data Access: Query execution'
148+
builder.increment_and_store('query_execution_plan', 'index_scan')
149+
caching_layer_check(builder)
150+
end
151+
152+
def caching_layer_check(builder)
153+
puts 'Caching Layer: Cache lookup'
154+
builder.increment_and_store('cache_key', "user_profile_#{builder.counter}")
155+
caching_layer_miss(builder)
156+
end
157+
158+
def caching_layer_miss(builder)
159+
puts 'Caching Layer: Cache miss, fetching from source'
160+
builder.increment_and_store('cache_miss', true)
161+
serialization_layer_encode(builder)
162+
end
163+
164+
def serialization_layer_encode(builder)
165+
puts 'Serialization: Data encoding'
166+
builder.increment_and_store('encoding_format', 'utf-8')
167+
serialization_layer_compress(builder)
168+
end
169+
170+
def serialization_layer_compress(builder)
171+
puts 'Serialization: Data compression'
172+
builder.increment_and_store('compression', 'gzip')
173+
network_layer_prepare(builder)
174+
end
175+
176+
def network_layer_prepare(builder)
177+
puts 'Network Layer: Connection preparation'
178+
builder.increment_and_store('network_protocol', 'tcp')
179+
network_layer_send(builder)
180+
end
181+
182+
def network_layer_send(builder)
183+
puts 'Network Layer: Data transmission'
184+
builder.increment_and_store('transmission_size', builder.data_store.to_s.length)
185+
security_layer_encrypt(builder)
186+
end
187+
188+
def security_layer_encrypt(builder)
189+
puts 'Security Layer: Data encryption'
190+
builder.increment_and_store('encryption_algorithm', 'AES-256')
191+
security_layer_sign(builder)
192+
end
193+
194+
def security_layer_sign(builder)
195+
puts 'Security Layer: Digital signature'
196+
builder.increment_and_store('signature_algorithm', 'RSA-SHA256')
197+
monitoring_layer_metrics(builder)
198+
end
199+
200+
def monitoring_layer_metrics(builder)
201+
puts 'Monitoring: Collecting metrics'
202+
builder.increment_and_store('metrics_collected', ['response_time', 'memory_usage'])
203+
monitoring_layer_alerts(builder)
204+
end
205+
206+
def monitoring_layer_alerts(builder)
207+
puts 'Monitoring: Alert system check'
208+
builder.increment_and_store('alert_rules', ['high_cpu', 'memory_threshold'])
209+
final_crash_point(builder)
210+
end
211+
212+
def final_crash_point(builder)
213+
puts 'Final Layer: About to crash with complex stack'
214+
puts "Stack depth achieved: #{builder.counter} operations"
215+
puts "Data store size: #{builder.data_store.size} entries"
216+
217+
# Multiple complex operations before crash
218+
complex_array = Array.new(1000) { |i| "item_#{i}" }
219+
complex_hash = Hash[complex_array.map { |item| [item, rand(1000)] }]
220+
221+
# Some string operations
222+
complex_string = complex_hash.keys.join(',') * 10
223+
224+
# Nested data structure
225+
nested_data = {
226+
level1: {
227+
level2: {
228+
level3: {
229+
data: complex_hash,
230+
metadata: builder.data_store,
231+
processing_info: {
232+
start_time: Time.now.to_f,
233+
operations: builder.counter,
234+
final_string_length: complex_string.length
235+
}
236+
}
237+
}
238+
}
239+
}
240+
241+
puts "Final nested data structure created with #{nested_data.to_s.length} characters"
242+
243+
4.times do |i|
244+
Fiddle.free(42)
245+
end
246+
end
247+
248+
def main_crash_test
249+
puts 'Starting crash test with deeply nested functions and complex operations'
250+
puts 'This will create a runtime stack with 25+ levels of function calls'
251+
orm_layer_find(12345)
252+
end
253+
254+
# Main test
255+
puts 'Starting standalone crashtracker test...'
256+
257+
# Start server
258+
server, get_crash_report = start_crash_server(9999)
259+
sleep 0.1 # Let server start
260+
261+
puts 'Forking process to test crashtracker...'
262+
263+
pid = fork do
264+
begin
265+
puts 'Child process started'
266+
267+
# Configure crashtracker
268+
Datadog.configure do |c|
269+
c.agent.host = '127.0.0.1'
270+
c.agent.port = 9999
271+
end
272+
273+
puts 'Crashtracker configured, starting crash test...'
274+
275+
# Call our nested function that will crash
276+
main_crash_test
277+
278+
rescue => e
279+
puts "Unexpected error in child: #{e}"
280+
exit 1
281+
end
282+
end
283+
284+
# Wait for child process
285+
Process.wait(pid)
286+
puts "Child process finished with status: #{$CHILD_STATUS.exitstatus}"
287+
288+
# Give server time to receive the crash report
289+
sleep 1
290+
291+
# Get and save the crash report
292+
crash_report = get_crash_report.call
293+
if crash_report
294+
# Write full crash report to tmp file
295+
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
296+
crash_file = "/tmp/crashtracker_report_#{timestamp}.json"
297+
File.write(crash_file, JSON.pretty_generate(crash_report))
298+
puts '\n=== CRASH REPORT SAVED ==='
299+
puts "Full crash report written to: #{crash_file}"
300+
301+
puts '\n=== RUNTIME STACK ANALYSIS ==='
302+
if crash_report.is_a?(Hash) && crash_report.dig('payload', 0, 'message')
303+
message = JSON.parse(crash_report.dig('payload', 0, 'message'))
304+
runtime_stack = message['experimental']['runtime_stack']
305+
if runtime_stack
306+
puts "Runtime stack format: #{runtime_stack['format']}"
307+
puts "Number of frames captured: #{runtime_stack['frames']&.length || 0}"
308+
puts '\nStack frames:'
309+
runtime_stack['frames']&.each_with_index do |frame, i|
310+
puts " [#{i}] #{frame['function']} @ #{frame['file']}:#{frame['line']}"
311+
end
312+
313+
runtime_stack_file = "/tmp/runtime_stack_#{timestamp}.json"
314+
File.write(runtime_stack_file, JSON.pretty_generate(runtime_stack))
315+
puts "\nRuntime stack saved to: #{runtime_stack_file}"
316+
else
317+
puts 'No runtime_stack found in crash report'
318+
end
319+
else
320+
puts 'Could not parse crash report structure'
321+
end
322+
else
323+
puts 'No crash report received'
324+
end
325+
326+
# Cleanup
327+
server.shutdown
328+
puts '\nTest complete.'

0 commit comments

Comments
 (0)