mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 02:50:00 +08:00
SECURITY: remove admin memory diagnostics routes
This commit is contained in:
parent
2ff2c49908
commit
0e9841b995
|
@ -1,42 +0,0 @@
|
|||
require_dependency 'memory_diagnostics'
|
||||
|
||||
class Admin::DiagnosticsController < Admin::AdminController
|
||||
layout false
|
||||
skip_before_action :check_xhr
|
||||
|
||||
def memory_stats
|
||||
text = nil
|
||||
|
||||
if params.key?(:diff)
|
||||
if !MemoryDiagnostics.snapshot_exists?
|
||||
text = "No initial snapshot exists"
|
||||
else
|
||||
text = MemoryDiagnostics.compare
|
||||
end
|
||||
elsif params.key?(:snapshot)
|
||||
MemoryDiagnostics.snapshot_current_process
|
||||
text = "Writing snapshot to: #{MemoryDiagnostics.snapshot_filename}\n\nTo get a diff use ?diff=1"
|
||||
else
|
||||
text = MemoryDiagnostics.memory_report(class_report: params.key?(:full))
|
||||
end
|
||||
|
||||
render plain: text
|
||||
end
|
||||
|
||||
def dump_heap
|
||||
begin
|
||||
# ruby 2.1
|
||||
GC.start(full_mark: true)
|
||||
require 'objspace'
|
||||
|
||||
io = File.open("discourse-heap-#{SecureRandom.hex(3)}.json", 'w')
|
||||
ObjectSpace.dump_all(output: io)
|
||||
io.close
|
||||
|
||||
render plain: "HEAP DUMP:\n#{io.path}"
|
||||
rescue
|
||||
render plain: "HEAP DUMP:\nnot supported"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -290,9 +290,6 @@ Discourse::Application.routes.draw do
|
|||
post "preview" => "badges#preview"
|
||||
end
|
||||
end
|
||||
|
||||
get "memory_stats" => "diagnostics#memory_stats", constraints: AdminConstraint.new
|
||||
get "dump_heap" => "diagnostics#dump_heap", constraints: AdminConstraint.new
|
||||
end # admin namespace
|
||||
|
||||
get "email_preferences" => "email#preferences_redirect", :as => "email_preferences_redirect"
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
module MemoryDiagnostics
|
||||
|
||||
def self.snapshot_exists?
|
||||
File.exists?(snapshot_filename)
|
||||
end
|
||||
|
||||
def self.compare(from = nil, to = nil)
|
||||
|
||||
from ||= snapshot_filename
|
||||
if !to
|
||||
filename = snapshot_filename + ".new"
|
||||
snapshot_current_process(filename)
|
||||
to = filename
|
||||
end
|
||||
|
||||
from = Marshal::load(IO.binread(from));
|
||||
to = Marshal::load(IO.binread(to));
|
||||
|
||||
diff = from - to
|
||||
|
||||
require 'objspace'
|
||||
diff = diff.map do |id|
|
||||
ObjectSpace._id2ref(id) rescue nil
|
||||
end
|
||||
diff.compact!
|
||||
|
||||
report = "#{diff.length} objects have leaked\n"
|
||||
|
||||
report << "Summary:\n"
|
||||
|
||||
summary = {}
|
||||
diff.each do |obj|
|
||||
begin
|
||||
summary[obj.class] ||= 0
|
||||
summary[obj.class] += 1
|
||||
rescue
|
||||
# don't care
|
||||
end
|
||||
end
|
||||
|
||||
report << summary.sort { |a, b| b[1] <=> a[1] }[0..50].map { |k, v|
|
||||
"#{k}: #{v}"
|
||||
}.join("\n")
|
||||
|
||||
report << "\n\nSample Items:\n"
|
||||
|
||||
diff[0..5000].each do |v|
|
||||
report << "#{v.class}: #{String === v ? v[0..300] : (40 + ObjectSpace.memsize_of(v)).to_s + " bytes"}\n" rescue nil
|
||||
end
|
||||
|
||||
report
|
||||
end
|
||||
|
||||
def self.snapshot_path
|
||||
"#{Rails.root}/tmp/mem_snapshots"
|
||||
end
|
||||
|
||||
def self.snapshot_filename
|
||||
"#{snapshot_path}/#{Process.pid}.snapshot"
|
||||
end
|
||||
|
||||
def self.snapshot_current_process(filename = nil)
|
||||
filename ||= snapshot_filename
|
||||
pid = fork do
|
||||
snapshot(filename)
|
||||
end
|
||||
|
||||
Process.wait(pid)
|
||||
end
|
||||
|
||||
def self.snapshot(filename)
|
||||
require 'objspace'
|
||||
FileUtils.mkdir_p snapshot_path
|
||||
object_ids = []
|
||||
|
||||
full_gc
|
||||
|
||||
ObjectSpace.each_object do |o|
|
||||
begin
|
||||
object_ids << o.object_id
|
||||
rescue
|
||||
# skip
|
||||
end
|
||||
end
|
||||
|
||||
IO.binwrite(filename, Marshal::dump(object_ids))
|
||||
end
|
||||
|
||||
def self.memory_report(opts = {})
|
||||
begin
|
||||
# ruby 2.1
|
||||
GC.start(full_mark: true)
|
||||
rescue
|
||||
GC.start
|
||||
end
|
||||
|
||||
classes = {}
|
||||
large_objects = []
|
||||
|
||||
if opts[:class_report]
|
||||
require 'objspace'
|
||||
ObjectSpace.each_object do |o|
|
||||
begin
|
||||
classes[o.class] ||= 0
|
||||
classes[o.class] += 1
|
||||
if (size = ObjectSpace.memsize_of(o)) > 200
|
||||
large_objects << [size, o]
|
||||
end
|
||||
rescue
|
||||
# all sorts of stuff can happen here BasicObject etc.
|
||||
classes[:unknown] ||= 0
|
||||
classes[:unknown] += 1
|
||||
end
|
||||
end
|
||||
classes = classes.sort { |a, b| b[1] <=> a[1] }[0..40].map { |klass, count| "#{klass}: #{count}" }
|
||||
|
||||
classes << "\nLarge Objects (#{large_objects.length} larger than 200 bytes total size #{large_objects.map { |x, _| x }.sum}):\n"
|
||||
|
||||
classes += large_objects.sort { |a, b| b[0] <=> a[0] }[0..800].map do |size, object|
|
||||
rval = "#{object.class}: size #{size}"
|
||||
rval << " " << object.to_s[0..500].gsub("\n", "") if (String === object) || (Regexp === object)
|
||||
rval << "\n"
|
||||
rval
|
||||
end
|
||||
end
|
||||
|
||||
stats = GC.stat.map { |k, v| "#{k}: #{v}" }
|
||||
counts = ObjectSpace.count_objects.sort { |a, b| b[1] <=> a[1] }.map { |k, v| "#{k}: #{v}" }
|
||||
|
||||
<<TEXT
|
||||
#{`hostname`.strip} pid:#{Process.pid} #{`cat /proc/#{Process.pid}/cmdline`.strip.gsub(/[^a-z1-9\/]/i, ' ')}
|
||||
|
||||
GC STATS:
|
||||
#{stats.join("\n")}
|
||||
|
||||
Objects:
|
||||
#{counts.join("\n")}
|
||||
|
||||
Process Info:
|
||||
#{`cat /proc/#{Process.pid}/status`}
|
||||
|
||||
Classes:
|
||||
#{classes.length > 0 ? classes.join("\n") : "Class report omitted use ?full=1 to include it"}
|
||||
|
||||
TEXT
|
||||
|
||||
end
|
||||
|
||||
def self.full_gc
|
||||
# gc start may not collect everything
|
||||
GC.start while new_count = decreased_count(new_count)
|
||||
end
|
||||
|
||||
def self.decreased_count(old)
|
||||
count = count_objects
|
||||
if !old || count < old
|
||||
count
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.count_objects
|
||||
i = 0
|
||||
ObjectSpace.each_object do |obj|
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user