Class: NewRelic::Agent::ThreadProfile

Inherits:
Object
  • Object
show all
Defined in:
lib/new_relic/agent/thread_profiler.rb

Defined Under Namespace

Classes: Node

Constant Summary collapse

THREAD_PROFILER_NODES =
20_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(profile_id, duration, interval, profile_agent_code) ⇒ ThreadProfile

Returns a new instance of ThreadProfile.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/new_relic/agent/thread_profiler.rb', line 102

def initialize(profile_id, duration, interval, profile_agent_code)
  @profile_id = profile_id
  @profile_agent_code = profile_agent_code

  @worker_loop = NewRelic::Agent::WorkerLoop.new(:duration => duration)
  @interval = interval
  @finished = false

  @traces = {
    :agent => [],
    :background => [],
    :other => [],
    :request => []
  }
  @flattened_nodes = []

  @poll_count = 0
  @sample_count = 0
end

Instance Attribute Details

#intervalObject (readonly)

Returns the value of attribute interval.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def interval
  @interval
end

#poll_countObject (readonly)

Returns the value of attribute poll_count.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def poll_count
  @poll_count
end

#profile_agent_codeObject (readonly)

Returns the value of attribute profile_agent_code.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def profile_agent_code
  @profile_agent_code
end

#profile_idObject (readonly)

Returns the value of attribute profile_id.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def profile_id
  @profile_id
end

#sample_countObject (readonly)

Returns the value of attribute sample_count.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def sample_count
  @sample_count
end

#start_timeObject (readonly)

Returns the value of attribute start_time.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def start_time
  @start_time
end

#stop_timeObject (readonly)

Returns the value of attribute stop_time.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def stop_time
  @stop_time
end

#tracesObject (readonly)

Returns the value of attribute traces.



96
97
98
# File 'lib/new_relic/agent/thread_profiler.rb', line 96

def traces
  @traces
end

Class Method Details

.compress(json) ⇒ Object



226
227
228
# File 'lib/new_relic/agent/thread_profiler.rb', line 226

def self.compress(json)
  compressed = Base64.encode64(Zlib::Deflate.deflate(json, Zlib::DEFAULT_COMPRESSION))
end

.flattened_nodes(nodes) ⇒ Object



222
223
224
# File 'lib/new_relic/agent/thread_profiler.rb', line 222

def self.flattened_nodes(nodes)
  nodes.map { |n| [n, flattened_nodes(n.children)] }.flatten
end

.parse_backtrace(trace) ⇒ Object



230
231
232
233
234
235
# File 'lib/new_relic/agent/thread_profiler.rb', line 230

def self.parse_backtrace(trace)
  trace.map do |line|
    line =~ /(.*)\:(\d+)\:in `(.*)'/
    { :method => $3, :line_no => $2.to_i, :file => $1 }
  end
end

Instance Method Details

#aggregate(trace, trees = , parent = nil) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/new_relic/agent/thread_profiler.rb', line 152

def aggregate(trace, trees=@traces[:request], parent=nil)
  return nil if trace.nil? || trace.empty?
  node = Node.new(trace.last)
  existing = trees.find {|n| n == node}

  if existing.nil?
    existing = node
    @flattened_nodes << node
  end

  if parent
    parent.add_child(node)
  else
    trees << node unless trees.include? node
  end

  existing.runnable_count += 1
  aggregate(trace[0..-2], existing.children, existing)

  existing
end

#finished?Boolean

Returns:

  • (Boolean)


208
209
210
# File 'lib/new_relic/agent/thread_profiler.rb', line 208

def finished?
  @finished
end

#logObject



290
291
292
# File 'lib/new_relic/agent/thread_profiler.rb', line 290

def log
  NewRelic::Agent.logger
end

#mark_doneObject



212
213
214
215
# File 'lib/new_relic/agent/thread_profiler.rb', line 212

def mark_done
  @finished = true
  @stop_time = now_in_millis
end

#mark_for_pruning(nodes, count_to_keep) ⇒ Object



217
218
219
220
# File 'lib/new_relic/agent/thread_profiler.rb', line 217

def mark_for_pruning(nodes, count_to_keep)
  to_prune = nodes[count_to_keep..-1] || []
  to_prune.each { |n| n.to_prune = true }
end

#now_in_millisObject



204
205
206
# File 'lib/new_relic/agent/thread_profiler.rb', line 204

def now_in_millis
  Time.now.to_f * 1_000
end

#prune!(count_to_keep) ⇒ Object



174
175
176
177
178
179
180
181
182
183
# File 'lib/new_relic/agent/thread_profiler.rb', line 174

def prune!(count_to_keep)
  @flattened_nodes.sort!(&:order_for_pruning)

  NewRelic::Agent.instance.stats_engine.
    record_supportability_metrics_count(@flattened_nodes.size, "ThreadProfiler/NodeCount")

  mark_for_pruning(@flattened_nodes, count_to_keep)

  traces.each { |_, nodes| Node.prune!(nodes) }
end

#runObject



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/new_relic/agent/thread_profiler.rb', line 122

def run
  Thread.new('Thread Profiler') do
    @start_time = now_in_millis

    @worker_loop.run(@interval) do
      NewRelic::Agent.instance.stats_engine.
        record_supportability_metrics_timed("ThreadProfiler/PollingTime") do

        @poll_count += 1
        Thread.list.each do |t|
          @sample_count += 1

          bucket = Thread.bucket_thread(t, @profile_agent_code)
          backtrace = Thread.scrub_backtrace(t, @profile_agent_code)
          aggregate(backtrace, @traces[bucket]) unless bucket == :ignore
        end
      end
    end

    mark_done
    log.debug("Finished thread profile. Will send with next harvest.")
  end
end

#stopObject



146
147
148
149
150
# File 'lib/new_relic/agent/thread_profiler.rb', line 146

def stop
  @worker_loop.stop
  mark_done
  log.debug("Stopping thread profile.")
end

#to_compressed_arrayObject



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/new_relic/agent/thread_profiler.rb', line 187

def to_compressed_array
  prune!(THREAD_PROFILER_NODES)

  traces = {
    "OTHER" => @traces[:other].map{|t| t.to_array },
    "REQUEST" => @traces[:request].map{|t| t.to_array },
    "AGENT" => @traces[:agent].map{|t| t.to_array },
    "BACKGROUND" => @traces[:background].map{|t| t.to_array }
  }

  [[@profile_id,
    @start_time.to_f, @stop_time.to_f,
    @poll_count, 
    ThreadProfile.compress(JSON.dump(traces)),
    @sample_count, 0]]
end