From d591919caab32ea06e3450f25ed1f5a819208bde Mon Sep 17 00:00:00 2001 From: viuginick Date: Sat, 1 Jul 2017 04:21:04 +0300 Subject: [PATCH 01/16] inspect memory limit --- bin/rdebug-ide | 9 +++++++- lib/ruby-debug-ide/xml_printer.rb | 38 +++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index 69b2c60..75a535d 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -36,6 +36,12 @@ Usage: rdebug-ide is supposed to be called from RDT, NetBeans, RubyMine, or EOB opts.separator "" opts.separator "Options:" + + ENV['DEBUGGER_MEMORY_LIMIT'] = '10' + opts.on("-m", "--memory-limit LIMIT", "evaluation memory limit in mb (default: 10)") do |limit| + ENV['DEBUGGER_MEMORY_LIMIT'] = limit + end + opts.on("-h", "--host HOST", "Host name used for remote debugging") {|host| options.host = host} opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|port| options.port = port} opts.on("--dispatcher-port PORT", Integer, "Port used for multi-process debugging dispatcher") do |dp| @@ -45,6 +51,7 @@ EOB options.evaluation_timeout = timeout end opts.on('--stop', 'stop when the script is loaded') {options.stop = true} + opts.on("-x", "--trace", "turn on line tracing") {options.tracing = true} opts.on("-l", "--load-mode", "load mode (experimental)") {options.load_mode = true} opts.on("-d", "--debug", "Debug self - prints information for debugging ruby-debug itself") do @@ -154,4 +161,4 @@ if options.attach_mode end else Debugger.debug_program(options) -end +end \ No newline at end of file diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 00f08b5..b6bdfe7 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -1,9 +1,13 @@ require 'stringio' require 'cgi' require 'monitor' +require 'objspace' module Debugger + class MemoryLimitError < StandardError + end + class XmlPrinter # :nodoc: class ExceptionProxy instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$|^instance_variables$|^instance_eval$)/ } @@ -380,15 +384,45 @@ def max_compact_name_size 50 end + def inspect_with_allocation_control(slice, memory_limit) + x = Thread.current + + start_alloc_size = ObjectSpace.memsize_of_all + + trace = TracePoint.new(:c_call, :call) do |tp| + curr_alloc_size = ObjectSpace.memsize_of_all + + if(curr_alloc_size - start_alloc_size > 1e6*memory_limit) + + trace.disable + x.raise MemoryLimitError, "Out of memory: evaluation took longer than 10mb." if x.alive? + end + end + + trace.enable + slice.inspect + trace.disable + rescue MemoryLimitError + return nil + end + + def compact_array_str(value) slice = value[0..10] - compact = slice.inspect + + if defined?(JRUBY_VERSION) + compact = slice.inspect + else + compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) + end + if value.size != slice.size compact[0..compact.size-2] + ", ...]" end compact end + def compact_hash_str(value) slice = value.sort_by { |k, _| k.to_s }[0..5] compact = slice.map { |kv| "#{kv[0]}: #{handle_binary_data(kv[1])}" }.join(", ") @@ -425,4 +459,4 @@ def build_value_attr(escaped_value_str) end -end +end \ No newline at end of file From 045a84c31ea3a2dc6f23b58a9ef14e4267f631dc Mon Sep 17 00:00:00 2001 From: viuginick Date: Sat, 1 Jul 2017 04:24:21 +0300 Subject: [PATCH 02/16] cleanup --- lib/ruby-debug-ide/xml_printer.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index b6bdfe7..34d3c90 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -404,8 +404,7 @@ def inspect_with_allocation_control(slice, memory_limit) trace.disable rescue MemoryLimitError return nil - end - + end def compact_array_str(value) slice = value[0..10] @@ -422,7 +421,6 @@ def compact_array_str(value) compact end - def compact_hash_str(value) slice = value.sort_by { |k, _| k.to_s }[0..5] compact = slice.map { |kv| "#{kv[0]}: #{handle_binary_data(kv[1])}" }.join(", ") From 5d0ac9629cbe7dd3a110f3441db7c98c162afdb1 Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Mon, 3 Jul 2017 11:10:56 +0300 Subject: [PATCH 03/16] Option to disable memory check is added --- bin/rdebug-ide | 4 ++++ lib/ruby-debug-ide/xml_printer.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index 75a535d..b9ea74a 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -42,6 +42,10 @@ EOB ENV['DEBUGGER_MEMORY_LIMIT'] = limit end + ENV['DEBUGGER_MEMORY_LIMIT_DISABLED'] = 'false' + opts.on("--memory-limit-disable", "disables the memory check") {ENV['DEBUGGER_MEMORY_LIMIT_DISABLED'] = 'true'} + + opts.on("-h", "--host HOST", "Host name used for remote debugging") {|host| options.host = host} opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|port| options.port = port} opts.on("--dispatcher-port PORT", Integer, "Port used for multi-process debugging dispatcher") do |dp| diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 34d3c90..17e6dc0 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -409,7 +409,7 @@ def inspect_with_allocation_control(slice, memory_limit) def compact_array_str(value) slice = value[0..10] - if defined?(JRUBY_VERSION) + if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT_DISABLED'] == 'true') compact = slice.inspect else compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) From 96652533a5d55fa0dd5e60e57670b4796fcf6203 Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Mon, 3 Jul 2017 12:19:05 +0300 Subject: [PATCH 04/16] disable memory limit in the same option --- bin/rdebug-ide | 6 +----- lib/ruby-debug-ide/xml_printer.rb | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index b9ea74a..bbfb262 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -38,14 +38,10 @@ EOB opts.separator "Options:" ENV['DEBUGGER_MEMORY_LIMIT'] = '10' - opts.on("-m", "--memory-limit LIMIT", "evaluation memory limit in mb (default: 10)") do |limit| + opts.on("-m", "--memory-limit LIMIT", Integer, "evaluation memory limit in mb (default: 10)") do |limit| ENV['DEBUGGER_MEMORY_LIMIT'] = limit end - ENV['DEBUGGER_MEMORY_LIMIT_DISABLED'] = 'false' - opts.on("--memory-limit-disable", "disables the memory check") {ENV['DEBUGGER_MEMORY_LIMIT_DISABLED'] = 'true'} - - opts.on("-h", "--host HOST", "Host name used for remote debugging") {|host| options.host = host} opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|port| options.port = port} opts.on("--dispatcher-port PORT", Integer, "Port used for multi-process debugging dispatcher") do |dp| diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 17e6dc0..b5380e0 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -409,7 +409,7 @@ def inspect_with_allocation_control(slice, memory_limit) def compact_array_str(value) slice = value[0..10] - if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT_DISABLED'] == 'true') + if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) compact = slice.inspect else compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) From 740c656f53858abbad5284af225027a0850255fb Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 5 Jul 2017 15:13:14 +0300 Subject: [PATCH 05/16] cleanup: formating fix, rename variable, rewrite on "compact = if (...)" --- bin/rdebug-ide | 3 +-- lib/ruby-debug-ide/xml_printer.rb | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index bbfb262..8dcd582 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -51,7 +51,6 @@ EOB options.evaluation_timeout = timeout end opts.on('--stop', 'stop when the script is loaded') {options.stop = true} - opts.on("-x", "--trace", "turn on line tracing") {options.tracing = true} opts.on("-l", "--load-mode", "load mode (experimental)") {options.load_mode = true} opts.on("-d", "--debug", "Debug self - prints information for debugging ruby-debug itself") do @@ -161,4 +160,4 @@ if options.attach_mode end else Debugger.debug_program(options) -end \ No newline at end of file +end diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index b5380e0..eb55d18 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -385,37 +385,38 @@ def max_compact_name_size end def inspect_with_allocation_control(slice, memory_limit) - x = Thread.current + curr_thread = Thread.current start_alloc_size = ObjectSpace.memsize_of_all trace = TracePoint.new(:c_call, :call) do |tp| curr_alloc_size = ObjectSpace.memsize_of_all - if(curr_alloc_size - start_alloc_size > 1e6*memory_limit) + if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) trace.disable - x.raise MemoryLimitError, "Out of memory: evaluation took longer than 10mb." if x.alive? + curr_thread.raise MemoryLimitError, "Out of memory: evaluation took longer than 10mb." if curr_thread.alive? end end trace.enable - slice.inspect - trace.disable - rescue MemoryLimitError + result = slice.inspect + trace.disable + result + rescue MemoryLimitError => e return nil end def compact_array_str(value) slice = value[0..10] + + compact = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) + slice.inspect + else + compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) + end - if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) - compact = slice.inspect - else - compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) - end - - if value.size != slice.size + if compact && value.size != slice.size compact[0..compact.size-2] + ", ...]" end compact @@ -457,4 +458,4 @@ def build_value_attr(escaped_value_str) end -end \ No newline at end of file +end From e5d7d5786adfa084a5141d20509790836ae332cc Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 5 Jul 2017 15:15:08 +0300 Subject: [PATCH 06/16] fixed exeption message --- lib/ruby-debug-ide/xml_printer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index eb55d18..62b41ee 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -395,7 +395,7 @@ def inspect_with_allocation_control(slice, memory_limit) if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) trace.disable - curr_thread.raise MemoryLimitError, "Out of memory: evaluation took longer than 10mb." if curr_thread.alive? + curr_thread.raise MemoryLimitError, "Out of memory: evaluation took longer than #{memory_limit}mb." if curr_thread.alive? end end From 9a9c981a524a71c58381c07708c61a09a4a47d15 Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 5 Jul 2017 16:29:04 +0300 Subject: [PATCH 07/16] memory limit logging added --- lib/ruby-debug-ide/xml_printer.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 62b41ee..98db655 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -45,6 +45,7 @@ def initialize(interface) end def print_msg(*args) + puts 'def print_msg(*args)' msg, *args = args xml_message = CGI.escapeHTML(msg % args) print "#{xml_message}" @@ -384,7 +385,7 @@ def max_compact_name_size 50 end - def inspect_with_allocation_control(slice, memory_limit) + def inspect_with_allocation_control(slice, memory_limit, obj_id) curr_thread = Thread.current start_alloc_size = ObjectSpace.memsize_of_all @@ -395,7 +396,7 @@ def inspect_with_allocation_control(slice, memory_limit) if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) trace.disable - curr_thread.raise MemoryLimitError, "Out of memory: evaluation took longer than #{memory_limit}mb." if curr_thread.alive? + curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect for obj(#{obj_id}) took more than #{memory_limit}mb." if curr_thread.alive? end end @@ -404,6 +405,7 @@ def inspect_with_allocation_control(slice, memory_limit) trace.disable result rescue MemoryLimitError => e + print_msg(e.message) return nil end @@ -413,7 +415,7 @@ def compact_array_str(value) compact = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) slice.inspect else - compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) + compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, value.object_id.to_s) end if compact && value.size != slice.size From cc45f3b347c66bb2ce9b1d45dcbd33dd8d586683 Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 5 Jul 2017 16:44:34 +0300 Subject: [PATCH 08/16] debug output deleted --- lib/ruby-debug-ide/xml_printer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 98db655..1ecea61 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -45,7 +45,6 @@ def initialize(interface) end def print_msg(*args) - puts 'def print_msg(*args)' msg, *args = args xml_message = CGI.escapeHTML(msg % args) print "#{xml_message}" From b15847f8fc588e798fe44ae5383fafbc97ff668b Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 5 Jul 2017 17:02:35 +0300 Subject: [PATCH 09/16] print_msg -> print_debug --- lib/ruby-debug-ide/xml_printer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 1ecea61..3b57da9 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -404,7 +404,7 @@ def inspect_with_allocation_control(slice, memory_limit, obj_id) trace.disable result rescue MemoryLimitError => e - print_msg(e.message) + print_debug(e.message) return nil end From 8328947f3faa96ac789657ec164b97775a2d717f Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Wed, 5 Jul 2017 17:18:24 +0300 Subject: [PATCH 10/16] object_id deleted from the exeption message --- lib/ruby-debug-ide/xml_printer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 3b57da9..61e466a 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -384,7 +384,7 @@ def max_compact_name_size 50 end - def inspect_with_allocation_control(slice, memory_limit, obj_id) + def inspect_with_allocation_control(slice, memory_limit) curr_thread = Thread.current start_alloc_size = ObjectSpace.memsize_of_all @@ -395,7 +395,7 @@ def inspect_with_allocation_control(slice, memory_limit, obj_id) if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) trace.disable - curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect for obj(#{obj_id}) took more than #{memory_limit}mb." if curr_thread.alive? + curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect took more than #{memory_limit}mb." if curr_thread.alive? end end @@ -414,7 +414,7 @@ def compact_array_str(value) compact = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) slice.inspect else - compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, value.object_id.to_s) + compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) end if compact && value.size != slice.size From 7b3a87586cb52decb1d31928a86e1386fd788b04 Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Thu, 6 Jul 2017 14:16:07 +0300 Subject: [PATCH 11/16] Fixes according to the review --- bin/rdebug-ide | 4 ++-- lib/ruby-debug-ide/xml_printer.rb | 38 ++++++++++++++++--------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index 8dcd582..8054766 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -37,8 +37,8 @@ EOB opts.separator "" opts.separator "Options:" - ENV['DEBUGGER_MEMORY_LIMIT'] = '10' - opts.on("-m", "--memory-limit LIMIT", Integer, "evaluation memory limit in mb (default: 10)") do |limit| + ENV['DEBUGGER_MEMORY_LIMIT'] = '1' + opts.on("-m", "--memory-limit LIMIT", Integer, "evaluation memory limit in mb (default: 1)") do |limit| ENV['DEBUGGER_MEMORY_LIMIT'] = limit end diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 61e466a..40a8e61 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -386,27 +386,29 @@ def max_compact_name_size def inspect_with_allocation_control(slice, memory_limit) curr_thread = Thread.current - - start_alloc_size = ObjectSpace.memsize_of_all - - trace = TracePoint.new(:c_call, :call) do |tp| - curr_alloc_size = ObjectSpace.memsize_of_all - - if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) + result = nil + inspect_thread = DebugThread.start { + start_alloc_size = ObjectSpace.memsize_of_all + trace = TracePoint.new(:c_call, :call) do |tp| - trace.disable - curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect took more than #{memory_limit}mb." if curr_thread.alive? - end - end - - trace.enable - result = slice.inspect - trace.disable - result + curr_alloc_size = ObjectSpace.memsize_of_all + start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) + + if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) + curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect took more than #{memory_limit}mb. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" + trace.disable + end + end.enable { + result = slice.inspect + } + } + inspect_thread.join + inspect_thread.kill + return result rescue MemoryLimitError => e print_debug(e.message) return nil - end + end def compact_array_str(value) slice = value[0..10] @@ -420,7 +422,7 @@ def compact_array_str(value) if compact && value.size != slice.size compact[0..compact.size-2] + ", ...]" end - compact + compact end def compact_hash_str(value) From 84f17fd6da4dd172c6eb88ddf84835fce7515b8b Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Fri, 7 Jul 2017 16:30:13 +0300 Subject: [PATCH 12/16] timelimit for inspect evaluation added --- bin/rdebug-ide | 4 ++-- lib/ruby-debug-ide/xml_printer.rb | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index 8054766..8dcd582 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -37,8 +37,8 @@ EOB opts.separator "" opts.separator "Options:" - ENV['DEBUGGER_MEMORY_LIMIT'] = '1' - opts.on("-m", "--memory-limit LIMIT", Integer, "evaluation memory limit in mb (default: 1)") do |limit| + ENV['DEBUGGER_MEMORY_LIMIT'] = '10' + opts.on("-m", "--memory-limit LIMIT", Integer, "evaluation memory limit in mb (default: 10)") do |limit| ENV['DEBUGGER_MEMORY_LIMIT'] = limit end diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 40a8e61..89830aa 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -8,6 +8,9 @@ module Debugger class MemoryLimitError < StandardError end + class TimeLimitError < StandardError + end + class XmlPrinter # :nodoc: class ExceptionProxy instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$|^instance_variables$|^instance_eval$)/ } @@ -389,9 +392,19 @@ def inspect_with_allocation_control(slice, memory_limit) result = nil inspect_thread = DebugThread.start { start_alloc_size = ObjectSpace.memsize_of_all + start_time = Time.now + max_time = Debugger.evaluation_timeout + trace = TracePoint.new(:c_call, :call) do |tp| curr_alloc_size = ObjectSpace.memsize_of_all + curr_time = Time.now + + if(curr_time - start_time > max_time) + curr_thread.raise TimeLimitError, "Timeout: evaluation of inspect took longer than #{max_time}sec. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" + trace.disable + end + start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) @@ -405,7 +418,7 @@ def inspect_with_allocation_control(slice, memory_limit) inspect_thread.join inspect_thread.kill return result - rescue MemoryLimitError => e + rescue MemoryLimitError, TimeLimitError => e print_debug(e.message) return nil end From f7f64a9af073d5990a2da36464a45ccc0a9323ef Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Fri, 7 Jul 2017 19:15:12 +0300 Subject: [PATCH 13/16] The handler is called with a probability of 25% --- lib/ruby-debug-ide/xml_printer.rb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 89830aa..a751ef3 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -397,19 +397,21 @@ def inspect_with_allocation_control(slice, memory_limit) trace = TracePoint.new(:c_call, :call) do |tp| - curr_alloc_size = ObjectSpace.memsize_of_all - curr_time = Time.now - - if(curr_time - start_time > max_time) - curr_thread.raise TimeLimitError, "Timeout: evaluation of inspect took longer than #{max_time}sec. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" - trace.disable - end + if(rand > 0.75) + curr_alloc_size = ObjectSpace.memsize_of_all + curr_time = Time.now + + if(curr_time - start_time > max_time) + curr_thread.raise TimeLimitError, "Timeout: evaluation of inspect took longer than #{max_time}sec. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" + trace.disable + end - start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) - - if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) - curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect took more than #{memory_limit}mb. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" - trace.disable + start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) + + if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) + curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect took more than #{memory_limit}mb. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" + trace.disable + end end end.enable { result = slice.inspect From 57940ab63117cc1ff39db31e39af44430d28766f Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Fri, 7 Jul 2017 19:59:21 +0300 Subject: [PATCH 14/16] cleanup --- lib/ruby-debug-ide/xml_printer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index a751ef3..daa2f1e 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -431,7 +431,7 @@ def compact_array_str(value) compact = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) slice.inspect else - compact = inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) + inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) end if compact && value.size != slice.size From bc8e07c94cee7191464f3ad972edd19159b8aaac Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Tue, 18 Jul 2017 17:01:34 +0300 Subject: [PATCH 15/16] inspect_with_alloc_control -> exec_with_... and heuristic break for variable inspect --- bin/rdebug-ide | 5 ++ lib/ruby-debug-ide/xml_printer.rb | 105 ++++++++++++++++++------------ 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/bin/rdebug-ide b/bin/rdebug-ide index 8dcd582..34d563d 100755 --- a/bin/rdebug-ide +++ b/bin/rdebug-ide @@ -42,6 +42,11 @@ EOB ENV['DEBUGGER_MEMORY_LIMIT'] = limit end + ENV['INSPECT_TIME_LIMIT'] = '100' + opts.on("-t", "--time-limit LIMIT", Integer, "evaluation time limit in milliseconds (default: 100)") do |limit| + ENV['INSPECT_TIME_LIMIT'] = limit + end + opts.on("-h", "--host HOST", "Host name used for remote debugging") {|host| options.host = host} opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|port| options.port = port} opts.on("--dispatcher-port PORT", Integer, "Port used for multi-process debugging dispatcher") do |dp| diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index daa2f1e..3bcf896 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -6,10 +6,24 @@ module Debugger class MemoryLimitError < StandardError + attr_reader :message + attr_reader :backtrace + + def initialize(message, backtrace) + @message = message + @backtrace = backtrace + end end class TimeLimitError < StandardError - end + attr_reader :message + attr_reader :backtrace + + def initialize(message, backtrace = '') + @message = message + @backtrace = backtrace + end + end class XmlPrinter # :nodoc: class ExceptionProxy @@ -144,7 +158,46 @@ def print_string(string) print_variable('encoding', string.encoding, 'instance') if string.respond_to?('encoding') end end - + + def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, flag) + curr_thread = Thread.current + result = nil + inspect_thread = DebugThread.start { + start_alloc_size = ObjectSpace.memsize_of_all + start_time = Time.now.to_f + + trace = TracePoint.new(:c_call, :call) do |tp| + + if(rand > 0.75) + curr_alloc_size = ObjectSpace.memsize_of_all + curr_time = Time.now.to_f + + if((curr_time - start_time) * 1e3 > time_limit) + curr_thread.raise TimeLimitError.new("Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.", "#{caller.map{|l| "\t#{l}"}.join("\n")}") + inspect_thread.kill + end + + start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) + + if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) + curr_thread.raise MemoryLimitError.new("Out of memory: evaluation of #{exec_method} took more than #{memory_limit}mb.", "#{caller.map{|l| "\t#{l}"}.join("\n")}") + inspect_thread.kill + end + end + end.enable { + result = value.send exec_method + } + } + inspect_thread.join + inspect_thread.kill + return result + rescue MemoryLimitError, TimeLimitError => e + print_debug(e.message + "\n" + e.backtrace) + + return nil if flag + return e.message + end + def print_variable(name, value, kind) name = name.to_s if value.nil? @@ -164,7 +217,13 @@ def print_variable(name, value, kind) value_str = value else has_children = !value.instance_variables.empty? || !value.class.class_variables.empty? - value_str = value.to_s || 'nil' rescue "<#to_s method raised exception: #{$!}>" + + value_str = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) + value.to_s || 'nil' rescue "<#to_s method raised exception: #{$!}>" + else + exec_with_allocation_control(value, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :to_s, false) || 'nil' rescue "<#to_s method raised exception: #{$!}>" + end + unless value_str.is_a?(String) value_str = "ERROR: #{value.class}.to_s method returns #{value_str.class}. Should return String." end @@ -387,51 +446,13 @@ def max_compact_name_size 50 end - def inspect_with_allocation_control(slice, memory_limit) - curr_thread = Thread.current - result = nil - inspect_thread = DebugThread.start { - start_alloc_size = ObjectSpace.memsize_of_all - start_time = Time.now - max_time = Debugger.evaluation_timeout - - trace = TracePoint.new(:c_call, :call) do |tp| - - if(rand > 0.75) - curr_alloc_size = ObjectSpace.memsize_of_all - curr_time = Time.now - - if(curr_time - start_time > max_time) - curr_thread.raise TimeLimitError, "Timeout: evaluation of inspect took longer than #{max_time}sec. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" - trace.disable - end - - start_alloc_size = curr_alloc_size if (curr_alloc_size < start_alloc_size) - - if(curr_alloc_size - start_alloc_size > 1e6 * memory_limit) - curr_thread.raise MemoryLimitError, "Out of memory: evaluation of inspect took more than #{memory_limit}mb. \n#{caller.map{|l| "\t#{l}"}.join("\n")}" - trace.disable - end - end - end.enable { - result = slice.inspect - } - } - inspect_thread.join - inspect_thread.kill - return result - rescue MemoryLimitError, TimeLimitError => e - print_debug(e.message) - return nil - end - def compact_array_str(value) slice = value[0..10] compact = if (defined?(JRUBY_VERSION) || ENV['DEBUGGER_MEMORY_LIMIT'].to_i <= 0) slice.inspect else - inspect_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i) + exec_with_allocation_control(slice, ENV['DEBUGGER_MEMORY_LIMIT'].to_i, ENV['INSPECT_TIME_LIMIT'].to_i, :inspect, true) end if compact && value.size != slice.size From 0b806187639219959f6f498ac3a3bce473b47bbd Mon Sep 17 00:00:00 2001 From: Viuginov Nickolay Date: Tue, 25 Jul 2017 14:55:37 +0300 Subject: [PATCH 16/16] fixes according to reiew --- lib/ruby-debug-ide/xml_printer.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/ruby-debug-ide/xml_printer.rb b/lib/ruby-debug-ide/xml_printer.rb index 3bcf896..0b0e607 100644 --- a/lib/ruby-debug-ide/xml_printer.rb +++ b/lib/ruby-debug-ide/xml_printer.rb @@ -9,7 +9,7 @@ class MemoryLimitError < StandardError attr_reader :message attr_reader :backtrace - def initialize(message, backtrace) + def initialize(message, backtrace = '') @message = message @backtrace = backtrace end @@ -159,7 +159,7 @@ def print_string(string) end end - def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, flag) + def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, return_message_if_overflow) curr_thread = Thread.current result = nil inspect_thread = DebugThread.start { @@ -194,8 +194,7 @@ def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, f rescue MemoryLimitError, TimeLimitError => e print_debug(e.message + "\n" + e.backtrace) - return nil if flag - return e.message + return return_message_if_overflow ? e.message : nil end def print_variable(name, value, kind)