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
|
# frozen_string_literal: true
require 'fast_spec_helper'
require 'tmpdir'
RSpec.describe Gitlab::Memory::Jemalloc do
let(:outdir) { Dir.mktmpdir }
let(:tmp_outdir) { Dir.mktmpdir }
after do
FileUtils.rm_f(outdir)
FileUtils.rm_f(tmp_outdir)
end
context 'when jemalloc is loaded' do
let(:fiddle_func) { instance_double(::Fiddle::Function) }
context 'with JSON format' do
let(:format) { :json }
let(:output) { '{"a": 24}' }
before do
stub_stats_call(output, 'J')
end
describe '.stats' do
it 'returns stats JSON' do
expect(described_class.stats(format: format)).to eq(output)
end
end
describe '.dump_stats' do
it 'writes stats JSON file' do
file_path = described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
file = Dir.entries(outdir).find { |e| e.match(/jemalloc_stats\.#{$$}\.\d+\.json$/) }
expect(file).not_to be_nil
expect(file_path).to eq(File.join(outdir, file))
expect(File.read(file_path)).to eq(output)
end
end
end
context 'with text format' do
let(:format) { :text }
let(:output) { 'stats' }
before do
stub_stats_call(output)
end
describe '.stats' do
it 'returns a text report' do
expect(described_class.stats(format: format)).to eq(output)
end
end
describe '.dump_stats' do
shared_examples 'writes stats text file' do |filename_label, filename_pattern|
it do
described_class.dump_stats(
path: outdir, tmp_dir: tmp_outdir, format: format, filename_label: filename_label)
file = Dir.entries(outdir).find { |e| e.match(filename_pattern) }
expect(file).not_to be_nil
expect(File.read(File.join(outdir, file))).to eq(output)
end
end
context 'when custom filename label is passed' do
include_examples 'writes stats text file', 'puma_0', /jemalloc_stats\.#{$$}\.puma_0\.\d+\.txt$/
end
context 'when custom filename label is not passed' do
include_examples 'writes stats text file', nil, /jemalloc_stats\.#{$$}\.\d+\.txt$/
end
end
end
context 'with unsupported format' do
let(:format) { 'unsupported' }
describe '.stats' do
it 'raises an error' do
expect do
described_class.stats(format: format)
end.to raise_error(/format must be one of/)
end
end
describe '.dump_stats' do
it 'raises an error' do
expect do
described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
end.to raise_error(/format must be one of/)
end
end
end
end
context 'when jemalloc is not loaded' do
before do
expect(::Fiddle::Handle).to receive(:sym).and_raise(Fiddle::DLError)
end
describe '.stats' do
it 'returns nil' do
expect(described_class.stats).to be_nil
end
end
describe '.dump_stats' do
it 'does nothing' do
stub_env('LD_PRELOAD', nil)
described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir)
expect(Dir.empty?(outdir)).to be(true)
end
end
end
def stub_stats_call(output, expected_options = '')
# Stub function pointer to stats call.
func_pointer = Fiddle::Pointer.new(0xd34db33f)
expect(::Fiddle::Handle).to receive(:sym).with('malloc_stats_print').and_return(func_pointer)
# Stub actual function call.
expect(::Fiddle::Function).to receive(:new)
.with(func_pointer, anything, anything)
.and_return(fiddle_func)
expect(fiddle_func).to receive(:call).with(anything, nil, expected_options) do |callback, _, options|
callback.call(nil, output)
end
end
end
|