Skip to content

Commit

Permalink
Merge pull request #2010 from DataDog/ivoanjo/rfc-enable-core-dumps-h…
Browse files Browse the repository at this point in the history
…elper

RFC: Add helper to easily enable core dumps
  • Loading branch information
ivoanjo authored May 13, 2022
2 parents cf3cbc5 + 8c24368 commit 80e443a
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 0 deletions.
50 changes: 50 additions & 0 deletions lib/datadog/kit/enable_core_dumps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# typed: ignore

module Datadog
module Kit
# This helper is used to enable core dumps for the current Ruby app. This is useful when debugging native-level
# crashes.
#
# It can be enabled simply by adding `require 'datadog/kit/enable_core_dumps'` to start of the app.
module EnableCoreDumps
def self.call
current_size, maximum_size = Process.getrlimit(:CORE)
core_pattern =
begin
File.read('/proc/sys/kernel/core_pattern').strip
rescue
'(Could not open /proc/sys/kernel/core_pattern)'
end

if maximum_size <= 0
Kernel.warn("[DDTRACE] Could not enable core dumps on crash, maximum size is #{maximum_size} (disabled).")
return
elsif maximum_size == current_size
Kernel.warn('[DDTRACE] Core dumps already enabled, nothing to do!')
return
end

begin
Process.setrlimit(:CORE, maximum_size)
rescue => e
Kernel.warn(
"[DDTRACE] Failed to enable core dumps. Cause: #{e.class.name} #{e.message} " \
"Location: #{Array(e.backtrace).first}"
)
return
end

if current_size == 0
Kernel.warn("[DDTRACE] Enabled core dumps. Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'")
else
Kernel.warn(
"[DDTRACE] Raised core dump limit. Old size: #{current_size} " \
"Maximum size: #{maximum_size} Output pattern: '#{core_pattern}'"
)
end
end
end
end
end

Datadog::Kit::EnableCoreDumps.call
131 changes: 131 additions & 0 deletions spec/datadog/kit/enable_core_dumps_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# typed: ignore

RSpec.describe 'Datadog::Kit::EnableCoreDumps' do
before do
# Make sure we don't touch the actual real value
allow(Process).to receive(:setrlimit).with(:CORE, anything).at_least(1).time

allow(Kernel).to receive(:warn)
expect(Process).to receive(:getrlimit).with(:CORE).and_return(setrlimit_result).at_least(1).time

# This should only be required with the mocks enabled, to make sure we don't actually affect the running
# Ruby instance
require 'datadog/kit/enable_core_dumps'
end

subject(:enable_core_dumps) { Datadog::Kit::EnableCoreDumps.call }

context 'when core dumps are disabled' do
let(:setrlimit_result) { [0, 0] }

it 'does not change anything' do
expect(Process).to_not receive(:setrlimit)

enable_core_dumps
end

it 'logs a message informing that it could not enable core dumps' do
expect(Kernel).to receive(:warn).with(/Could not enable core dumps/)

enable_core_dumps
end
end

context 'when core dumps were already enabled at the maximum size' do
let(:setrlimit_result) { [1, 1] }

it 'does not change anything' do
expect(Process).to_not receive(:setrlimit)

enable_core_dumps
end

it 'logs a message informing core dumps were already enabled' do
expect(Kernel).to receive(:warn).with(/Core dumps already enabled/)

enable_core_dumps
end
end

context 'when core dumps can be enabled' do
context 'when core dumps were enabled, but the maximum size can be raised' do
let(:setrlimit_result) { [1, 2] }

it 'raises the maximum size' do
expect(Process).to receive(:setrlimit).with(:CORE, 2)

enable_core_dumps
end

it 'logs a message informing it raised the limit' do
expect(Kernel).to receive(:warn).with(/Raised core dump limit/)

enable_core_dumps
end
end

context 'when core dumps were not enabled' do
let(:setrlimit_result) { [0, 1] }

it 'enables core dumps' do
expect(Process).to receive(:setrlimit).with(:CORE, 1)

enable_core_dumps
end

it 'logs a message informing that core dumps were enabled' do
expect(Kernel).to receive(:warn).with(/Enabled core dumps/)

enable_core_dumps
end
end

context 'when core dumps fail to be enabled' do
let(:setrlimit_result) { [0, 1] }

before do
expect(Process).to receive(:setrlimit).with(:CORE, 1).and_raise(StandardError)
end

it 'logs a message informing that core dumps could not be enabled and does not propagate the exception' do
expect(Kernel).to receive(:warn).with(/Failed to enable .* StandardError/)

enable_core_dumps
end
end

context 'when core pattern is available' do
let(:setrlimit_result) { [0, 1] }

before do
expect(File).to receive(:read).with('/proc/sys/kernel/core_pattern').and_return("core-pattern-configured\n")
end

it 'logs a message including the core pattern' do
expect(Kernel).to receive(:warn).with(/core-pattern-configured/)

enable_core_dumps
end
end

context 'when core pattern is not available' do
let(:setrlimit_result) { [0, 1] }

before do
expect(File).to receive(:read).with('/proc/sys/kernel/core_pattern').and_raise(Errno::ENOENT)
end

it 'still enables core dumps' do
expect(Process).to receive(:setrlimit).with(:CORE, 1)

enable_core_dumps
end

it 'logs a message nothing the core pattern is not available' do
expect(Kernel).to receive(:warn).with(/Could not open/)

enable_core_dumps
end
end
end
end

0 comments on commit 80e443a

Please sign in to comment.