PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/test/ruby/test_settracefunc.rb

http://github.com/ruby/ruby
Ruby | 2295 lines | 2052 code | 201 blank | 42 comment | 94 complexity | 9a79f88212c4dba914cfc96723390d3e MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. # frozen_string_literal: false
  2. require 'test/unit'
  3. class TestSetTraceFunc < Test::Unit::TestCase
  4. def setup
  5. if defined?(RubyVM)
  6. @original_compile_option = RubyVM::InstructionSequence.compile_option
  7. RubyVM::InstructionSequence.compile_option = {
  8. :trace_instruction => true,
  9. :specialized_instruction => false
  10. }
  11. end
  12. @target_thread = Thread.current
  13. end
  14. def teardown
  15. set_trace_func(nil)
  16. if defined?(RubyVM)
  17. RubyVM::InstructionSequence.compile_option = @original_compile_option
  18. end
  19. @target_thread = nil
  20. end
  21. def target_thread?
  22. Thread.current == @target_thread
  23. end
  24. def test_c_call
  25. events = []
  26. name = "#{self.class}\##{__method__}"
  27. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  28. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  29. 2: events << [event, lineno, mid, klass] if file == name
  30. 3: })
  31. 4: x = 1 + 1
  32. 5: set_trace_func(nil)
  33. EOF
  34. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  35. events.shift)
  36. assert_equal(["line", 4, __method__, self.class],
  37. events.shift)
  38. assert_equal(["c-call", 4, :+, Integer],
  39. events.shift)
  40. assert_equal(["c-return", 4, :+, Integer],
  41. events.shift)
  42. assert_equal(["line", 5, __method__, self.class],
  43. events.shift)
  44. assert_equal(["c-call", 5, :set_trace_func, Kernel],
  45. events.shift)
  46. assert_equal([], events)
  47. end
  48. def test_call
  49. events = []
  50. name = "#{self.class}\##{__method__}"
  51. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  52. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  53. 2: events << [event, lineno, mid, klass] if file == name
  54. 3: })
  55. 4: def add(x, y)
  56. 5: x + y
  57. 6: end
  58. 7: x = add(1, 1)
  59. 8: set_trace_func(nil)
  60. EOF
  61. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  62. events.shift)
  63. assert_equal(["line", 4, __method__, self.class],
  64. events.shift)
  65. assert_equal(["c-call", 4, :method_added, self.class],
  66. events.shift)
  67. assert_equal(["c-return", 4, :method_added, self.class],
  68. events.shift)
  69. assert_equal(["line", 7, __method__, self.class],
  70. events.shift)
  71. assert_equal(["call", 4, :add, self.class],
  72. events.shift)
  73. assert_equal(["line", 5, :add, self.class],
  74. events.shift)
  75. assert_equal(["c-call", 5, :+, Integer],
  76. events.shift)
  77. assert_equal(["c-return", 5, :+, Integer],
  78. events.shift)
  79. assert_equal(["return", 6, :add, self.class],
  80. events.shift)
  81. assert_equal(["line", 8, __method__, self.class],
  82. events.shift)
  83. assert_equal(["c-call", 8, :set_trace_func, Kernel],
  84. events.shift)
  85. assert_equal([], events)
  86. end
  87. def test_class
  88. events = []
  89. name = "#{self.class}\##{__method__}"
  90. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  91. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  92. 2: events << [event, lineno, mid, klass] if file == name
  93. 3: })
  94. 4: class Foo
  95. 5: def bar
  96. 6: end
  97. 7: end
  98. 8: x = Foo.new.bar
  99. 9: set_trace_func(nil)
  100. EOF
  101. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  102. events.shift)
  103. assert_equal(["line", 4, __method__, self.class],
  104. events.shift)
  105. assert_equal(["c-call", 4, :inherited, Class],
  106. events.shift)
  107. assert_equal(["c-return", 4, :inherited, Class],
  108. events.shift)
  109. assert_equal(["class", 4, nil, nil],
  110. events.shift)
  111. assert_equal(["line", 5, nil, nil],
  112. events.shift)
  113. assert_equal(["c-call", 5, :method_added, Module],
  114. events.shift)
  115. assert_equal(["c-return", 5, :method_added, Module],
  116. events.shift)
  117. assert_equal(["end", 7, nil, nil],
  118. events.shift)
  119. assert_equal(["line", 8, __method__, self.class],
  120. events.shift)
  121. assert_equal(["c-call", 8, :new, Class],
  122. events.shift)
  123. assert_equal(["c-call", 8, :initialize, BasicObject],
  124. events.shift)
  125. assert_equal(["c-return", 8, :initialize, BasicObject],
  126. events.shift)
  127. assert_equal(["c-return", 8, :new, Class],
  128. events.shift)
  129. assert_equal(["call", 5, :bar, Foo],
  130. events.shift)
  131. assert_equal(["return", 6, :bar, Foo],
  132. events.shift)
  133. assert_equal(["line", 9, __method__, self.class],
  134. events.shift)
  135. assert_equal(["c-call", 9, :set_trace_func, Kernel],
  136. events.shift)
  137. assert_equal([], events)
  138. self.class.class_eval do
  139. remove_const :Foo
  140. end
  141. end
  142. def test_return # [ruby-dev:38701]
  143. events = []
  144. name = "#{self.class}\##{__method__}"
  145. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  146. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  147. 2: events << [event, lineno, mid, klass] if file == name
  148. 3: })
  149. 4: def meth_return(a)
  150. 5: return if a
  151. 6: return
  152. 7: end
  153. 8: meth_return(true)
  154. 9: meth_return(false)
  155. 10: set_trace_func(nil)
  156. EOF
  157. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  158. events.shift)
  159. assert_equal(["line", 4, __method__, self.class],
  160. events.shift)
  161. assert_equal(["c-call", 4, :method_added, self.class],
  162. events.shift)
  163. assert_equal(["c-return", 4, :method_added, self.class],
  164. events.shift)
  165. assert_equal(["line", 8, __method__, self.class],
  166. events.shift)
  167. assert_equal(["call", 4, :meth_return, self.class],
  168. events.shift)
  169. assert_equal(["line", 5, :meth_return, self.class],
  170. events.shift)
  171. assert_equal(["return", 5, :meth_return, self.class],
  172. events.shift)
  173. assert_equal(["line", 9, :test_return, self.class],
  174. events.shift)
  175. assert_equal(["call", 4, :meth_return, self.class],
  176. events.shift)
  177. assert_equal(["line", 5, :meth_return, self.class],
  178. events.shift)
  179. assert_equal(["return", 7, :meth_return, self.class],
  180. events.shift)
  181. assert_equal(["line", 10, :test_return, self.class],
  182. events.shift)
  183. assert_equal(["c-call", 10, :set_trace_func, Kernel],
  184. events.shift)
  185. assert_equal([], events)
  186. end
  187. def test_return2 # [ruby-core:24463]
  188. events = []
  189. name = "#{self.class}\##{__method__}"
  190. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  191. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  192. 2: events << [event, lineno, mid, klass] if file == name
  193. 3: })
  194. 4: def meth_return2
  195. 5: a = 5
  196. 6: return a
  197. 7: end
  198. 8: meth_return2
  199. 9: set_trace_func(nil)
  200. EOF
  201. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  202. events.shift)
  203. assert_equal(["line", 4, __method__, self.class],
  204. events.shift)
  205. assert_equal(["c-call", 4, :method_added, self.class],
  206. events.shift)
  207. assert_equal(["c-return", 4, :method_added, self.class],
  208. events.shift)
  209. assert_equal(["line", 8, __method__, self.class],
  210. events.shift)
  211. assert_equal(["call", 4, :meth_return2, self.class],
  212. events.shift)
  213. assert_equal(["line", 5, :meth_return2, self.class],
  214. events.shift)
  215. assert_equal(["line", 6, :meth_return2, self.class],
  216. events.shift)
  217. assert_equal(["return", 7, :meth_return2, self.class],
  218. events.shift)
  219. assert_equal(["line", 9, :test_return2, self.class],
  220. events.shift)
  221. assert_equal(["c-call", 9, :set_trace_func, Kernel],
  222. events.shift)
  223. assert_equal([], events)
  224. end
  225. def test_raise
  226. events = []
  227. name = "#{self.class}\##{__method__}"
  228. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  229. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  230. 2: events << [event, lineno, mid, klass] if file == name
  231. 3: })
  232. 4: begin
  233. 5: raise TypeError, "error"
  234. 6: rescue TypeError
  235. 7: end
  236. 8: set_trace_func(nil)
  237. EOF
  238. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  239. events.shift)
  240. assert_equal(["line", 5, __method__, self.class],
  241. events.shift)
  242. assert_equal(["c-call", 5, :raise, Kernel],
  243. events.shift)
  244. assert_equal(["c-call", 5, :exception, Exception],
  245. events.shift)
  246. assert_equal(["c-call", 5, :initialize, Exception],
  247. events.shift)
  248. assert_equal(["c-return", 5, :initialize, Exception],
  249. events.shift)
  250. assert_equal(["c-return", 5, :exception, Exception],
  251. events.shift)
  252. assert_equal(["c-return", 5, :raise, Kernel],
  253. events.shift)
  254. assert_equal(["c-call", 5, :backtrace, Exception],
  255. events.shift)
  256. assert_equal(["c-return", 5, :backtrace, Exception],
  257. events.shift)
  258. assert_equal(["raise", 5, :test_raise, TestSetTraceFunc],
  259. events.shift)
  260. assert_equal(["c-call", 6, :===, Module],
  261. events.shift)
  262. assert_equal(["c-return", 6, :===, Module],
  263. events.shift)
  264. assert_equal(["line", 8, __method__, self.class],
  265. events.shift)
  266. assert_equal(["c-call", 8, :set_trace_func, Kernel],
  267. events.shift)
  268. assert_equal([], events)
  269. end
  270. def test_break # [ruby-core:27606] [Bug #2610]
  271. events = []
  272. name = "#{self.class}\##{__method__}"
  273. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  274. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  275. 2: events << [event, lineno, mid, klass] if file == name
  276. 3: })
  277. 4: [1,2,3].any? {|n| n}
  278. 8: set_trace_func(nil)
  279. EOF
  280. [["c-return", 1, :set_trace_func, Kernel],
  281. ["line", 4, __method__, self.class],
  282. ["c-call", 4, :any?, Array],
  283. ["line", 4, __method__, self.class],
  284. ["c-return", 4, :any?, Array],
  285. ["line", 5, __method__, self.class],
  286. ["c-call", 5, :set_trace_func, Kernel]].each.with_index{|e, i|
  287. assert_equal(e, events.shift, "mismatch on #{i}th trace")
  288. }
  289. end
  290. def test_invalid_proc
  291. assert_raise(TypeError) { set_trace_func(1) }
  292. end
  293. def test_raise_in_trace
  294. set_trace_func proc {raise rescue nil}
  295. assert_equal(42, (raise rescue 42), '[ruby-core:24118]')
  296. end
  297. def test_thread_trace
  298. events = {:set => [], :add => []}
  299. prc = Proc.new { |event, file, lineno, mid, binding, klass|
  300. events[:set] << [event, lineno, mid, klass, :set]
  301. }
  302. prc = prc # suppress warning
  303. prc2 = Proc.new { |event, file, lineno, mid, binding, klass|
  304. events[:add] << [event, lineno, mid, klass, :add]
  305. }
  306. prc2 = prc2 # suppress warning
  307. th = Thread.new do
  308. th = Thread.current
  309. name = "#{self.class}\##{__method__}"
  310. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  311. 1: th.set_trace_func(prc)
  312. 2: th.add_trace_func(prc2)
  313. 3: class ThreadTraceInnerClass
  314. 4: def foo
  315. 5: _x = 1 + 1
  316. 6: end
  317. 7: end
  318. 8: ThreadTraceInnerClass.new.foo
  319. 9: th.set_trace_func(nil)
  320. EOF
  321. end
  322. th.join
  323. [["c-return", 1, :set_trace_func, Thread, :set],
  324. ["line", 2, __method__, self.class, :set],
  325. ["c-call", 2, :add_trace_func, Thread, :set]].each do |e|
  326. assert_equal(e, events[:set].shift)
  327. end
  328. [["c-return", 2, :add_trace_func, Thread],
  329. ["line", 3, __method__, self.class],
  330. ["c-call", 3, :inherited, Class],
  331. ["c-return", 3, :inherited, Class],
  332. ["class", 3, nil, nil],
  333. ["line", 4, nil, nil],
  334. ["c-call", 4, :method_added, Module],
  335. ["c-return", 4, :method_added, Module],
  336. ["end", 7, nil, nil],
  337. ["line", 8, __method__, self.class],
  338. ["c-call", 8, :new, Class],
  339. ["c-call", 8, :initialize, BasicObject],
  340. ["c-return", 8, :initialize, BasicObject],
  341. ["c-return", 8, :new, Class],
  342. ["call", 4, :foo, ThreadTraceInnerClass],
  343. ["line", 5, :foo, ThreadTraceInnerClass],
  344. ["c-call", 5, :+, Integer],
  345. ["c-return", 5, :+, Integer],
  346. ["return", 6, :foo, ThreadTraceInnerClass],
  347. ["line", 9, __method__, self.class],
  348. ["c-call", 9, :set_trace_func, Thread]].each do |e|
  349. [:set, :add].each do |type|
  350. assert_equal(e + [type], events[type].shift)
  351. end
  352. end
  353. assert_equal([], events[:set])
  354. assert_equal([], events[:add])
  355. # cleanup
  356. self.class.class_eval do
  357. remove_const :ThreadTraceInnerClass
  358. end
  359. end
  360. def test_trace_defined_method
  361. events = []
  362. name = "#{self.class}\##{__method__}"
  363. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  364. 1: class FooBar; define_method(:foobar){}; end
  365. 2: fb = FooBar.new
  366. 3: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  367. 4: events << [event, lineno, mid, klass] if file == name
  368. 5: })
  369. 6: fb.foobar
  370. 7: set_trace_func(nil)
  371. EOF
  372. [["c-return", 3, :set_trace_func, Kernel],
  373. ["line", 6, __method__, self.class],
  374. ["call", 1, :foobar, FooBar],
  375. ["return", 6, :foobar, FooBar],
  376. ["line", 7, __method__, self.class],
  377. ["c-call", 7, :set_trace_func, Kernel]].each{|e|
  378. assert_equal(e, events.shift)
  379. }
  380. end
  381. def test_remove_in_trace
  382. bug3921 = '[ruby-dev:42350]'
  383. ok = false
  384. func = lambda{|e, f, l, i, b, k|
  385. set_trace_func(nil)
  386. ok = eval("self", b)
  387. }
  388. set_trace_func(func)
  389. assert_equal(self, ok, bug3921)
  390. end
  391. class << self
  392. define_method(:method_added, Module.method(:method_added))
  393. end
  394. def trace_by_tracepoint *trace_events
  395. events = []
  396. trace = nil
  397. xyzzy = nil
  398. _local_var = :outer
  399. raised_exc = nil
  400. method = :trace_by_tracepoint
  401. _get_data = lambda{|tp|
  402. case tp.event
  403. when :return, :c_return
  404. tp.return_value
  405. when :raise
  406. tp.raised_exception
  407. else
  408. :nothing
  409. end
  410. }
  411. _defined_class = lambda{|tp|
  412. klass = tp.defined_class
  413. begin
  414. # If it is singleton method, then return original class
  415. # to make compatible with set_trace_func().
  416. # This is very ad-hoc hack. I hope I can make more clean test on it.
  417. case klass.inspect
  418. when /Class:TracePoint/; return TracePoint
  419. when /Class:Exception/; return Exception
  420. else klass
  421. end
  422. rescue Exception => e
  423. e
  424. end if klass
  425. }
  426. trace = nil
  427. begin
  428. eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
  429. 1: trace = TracePoint.trace(*trace_events){|tp| next if !target_thread?
  430. 2: events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp)] if tp.path == 'xyzzy'
  431. 3: }
  432. 4: 1.times{|;_local_var| _local_var = :inner
  433. 5: tap{}
  434. 6: }
  435. 7: class XYZZY
  436. 8: _local_var = :XYZZY_outer
  437. 9: def foo
  438. 10: _local_var = :XYZZY_foo
  439. 11: bar
  440. 12: end
  441. 13: def bar
  442. 14: _local_var = :XYZZY_bar
  443. 15: tap{}
  444. 16: end
  445. 17: end
  446. 18: xyzzy = XYZZY.new
  447. 19: xyzzy.foo
  448. 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
  449. 21: trace.disable
  450. EOF
  451. self.class.class_eval{remove_const(:XYZZY)}
  452. ensure
  453. trace.disable if trace&.enabled?
  454. end
  455. answer_events = [
  456. #
  457. [:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
  458. [:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
  459. [:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
  460. [:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
  461. [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
  462. [:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
  463. [:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
  464. [:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
  465. [:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
  466. [:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
  467. [:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
  468. [:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
  469. [:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  470. [:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
  471. [:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
  472. [:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  473. [:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
  474. [:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
  475. [:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  476. [:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  477. [:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
  478. [:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
  479. [:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
  480. [:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
  481. [:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  482. [:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
  483. [:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
  484. [:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
  485. [:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
  486. [:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
  487. [:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
  488. [:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
  489. [:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
  490. [:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
  491. [:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
  492. [:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  493. [:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
  494. [:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
  495. [:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
  496. [:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
  497. [:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
  498. [:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
  499. [:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
  500. [:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
  501. [:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
  502. [:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
  503. [:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
  504. [:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  505. ]
  506. return events, answer_events
  507. end
  508. def test_tracepoint
  509. events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
  510. ms = [events1, answer_events].map{|evs|
  511. evs.map{|e|
  512. "#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
  513. }
  514. }
  515. if false # show all events
  516. printf(" %-60s | %-60s\n", "actual", "expected")
  517. ms[0].zip(ms[1]){|a, b|
  518. printf("%s%-60s | %-60s\n", a==b ? ' ' : '!', a, b)
  519. }
  520. end
  521. mesg = ms[0].zip(ms[1]).map{|a, b|
  522. if a != b
  523. "actual: #{a} <-> expected: #{b}"
  524. end
  525. }.compact.join("\n")
  526. answer_events.zip(events1){|answer, event|
  527. assert_equal answer, event, mesg
  528. }
  529. [:line, :class, :end, :call, :return, :c_call, :c_return, :raise].each{|event|
  530. events1, answer_events = *trace_by_tracepoint(event)
  531. answer_events.find_all{|e| e[0] == event}.zip(events1){|answer_line, event_line|
  532. assert_equal answer_line, event_line
  533. }
  534. }
  535. end
  536. def trace_by_set_trace_func
  537. events = []
  538. trace = nil
  539. trace = trace
  540. xyzzy = nil
  541. xyzzy = xyzzy
  542. _local_var = :outer
  543. method = :trace_by_set_trace_func
  544. raised_exc = nil
  545. eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
  546. 1: set_trace_func(lambda{|event, file, line, id, binding, klass|
  547. 2: events << [event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")] if file == 'xyzzy'
  548. 3: })
  549. 4: 1.times{|;_local_var| _local_var = :inner
  550. 5: tap{}
  551. 6: }
  552. 7: class XYZZY
  553. 8: _local_var = :XYZZY_outer
  554. 9: def foo
  555. 10: _local_var = :XYZZY_foo
  556. 11: bar
  557. 12: end
  558. 13: def bar
  559. 14: _local_var = :XYZZY_bar
  560. 15: tap{}
  561. 16: end
  562. 17: end
  563. 18: xyzzy = XYZZY.new
  564. 19: xyzzy.foo
  565. 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
  566. 21: set_trace_func(nil)
  567. EOF
  568. self.class.class_eval{remove_const(:XYZZY)}
  569. answer_events = [
  570. #
  571. [:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace],
  572. [:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
  573. [:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
  574. [:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
  575. [:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
  576. [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
  577. [:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
  578. [:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
  579. [:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
  580. [:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
  581. [:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
  582. [:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
  583. [:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
  584. [:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  585. [:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
  586. [:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
  587. [:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  588. [:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
  589. [:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
  590. [:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  591. [:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  592. [:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
  593. [:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
  594. [:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
  595. [:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
  596. [:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  597. [:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
  598. [:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
  599. [:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
  600. [:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
  601. [:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
  602. [:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
  603. [:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
  604. [:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
  605. [:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
  606. [:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
  607. [:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  608. [:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
  609. [:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
  610. [:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
  611. [:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
  612. [:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
  613. [:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
  614. [:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
  615. [:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
  616. [:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
  617. [:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
  618. [:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
  619. [:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  620. [:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing],
  621. ]
  622. return events, answer_events
  623. end
  624. def test_set_trace_func
  625. actual_events, expected_events = trace_by_set_trace_func
  626. expected_events.zip(actual_events){|e, a|
  627. a[0] = a[0].to_s.sub('-', '_').to_sym
  628. assert_equal e[0..2], a[0..2], a.inspect
  629. # event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")
  630. assert_equal e[3].nil?, a[3].nil? # klass
  631. assert_equal e[4].nil?, a[4].nil? # id
  632. assert_equal e[6], a[6] # _local_var
  633. }
  634. end
  635. def test_tracepoint_object_id
  636. tps = []
  637. trace = TracePoint.trace(){|tp|
  638. next if !target_thread?
  639. tps << tp
  640. }
  641. tap{}
  642. tap{}
  643. tap{}
  644. trace.disable
  645. # passed tp is unique, `trace' object which is generated by TracePoint.trace
  646. tps.each{|tp|
  647. assert_equal trace, tp
  648. }
  649. end
  650. def test_tracepoint_access_from_outside
  651. tp_store = nil
  652. trace = TracePoint.trace(){|tp|
  653. next if !target_thread?
  654. tp_store = tp
  655. }
  656. tap{}
  657. trace.disable
  658. assert_raise(RuntimeError){tp_store.lineno}
  659. assert_raise(RuntimeError){tp_store.event}
  660. assert_raise(RuntimeError){tp_store.path}
  661. assert_raise(RuntimeError){tp_store.method_id}
  662. assert_raise(RuntimeError){tp_store.defined_class}
  663. assert_raise(RuntimeError){tp_store.binding}
  664. assert_raise(RuntimeError){tp_store.self}
  665. assert_raise(RuntimeError){tp_store.return_value}
  666. assert_raise(RuntimeError){tp_store.raised_exception}
  667. end
  668. def foo
  669. end
  670. def test_tracepoint_enable
  671. ary = []
  672. args = nil
  673. trace = TracePoint.new(:call){|tp|
  674. next if !target_thread?
  675. ary << tp.method_id
  676. }
  677. foo
  678. trace.enable{|*a|
  679. args = a
  680. foo
  681. }
  682. foo
  683. assert_equal([:foo], ary)
  684. assert_equal([], args)
  685. trace = TracePoint.new{}
  686. begin
  687. assert_equal(false, trace.enable)
  688. assert_equal(true, trace.enable)
  689. trace.enable{}
  690. assert_equal(true, trace.enable)
  691. ensure
  692. trace.disable
  693. end
  694. end
  695. def test_tracepoint_disable
  696. ary = []
  697. args = nil
  698. trace = TracePoint.trace(:call){|tp|
  699. next if !target_thread?
  700. ary << tp.method_id
  701. }
  702. foo
  703. trace.disable{|*a|
  704. args = a
  705. foo
  706. }
  707. foo
  708. trace.disable
  709. assert_equal([:foo, :disable, :foo, :disable], ary)
  710. assert_equal([], args)
  711. trace = TracePoint.new{}
  712. trace.enable{
  713. assert_equal(true, trace.disable)
  714. assert_equal(false, trace.disable)
  715. trace.disable{}
  716. assert_equal(false, trace.disable)
  717. }
  718. end
  719. def test_tracepoint_enabled
  720. trace = TracePoint.trace(:call){|tp|
  721. #
  722. }
  723. assert_equal(true, trace.enabled?)
  724. trace.disable{
  725. assert_equal(false, trace.enabled?)
  726. trace.enable{
  727. assert_equal(true, trace.enabled?)
  728. }
  729. }
  730. trace.disable
  731. assert_equal(false, trace.enabled?)
  732. end
  733. def parameter_test(a, b, c)
  734. yield
  735. end
  736. def test_tracepoint_parameters
  737. trace = TracePoint.new(:line, :class, :end, :call, :return, :b_call, :b_return, :c_call, :c_return, :raise){|tp|
  738. next if !target_thread?
  739. next if tp.path != __FILE__
  740. case tp.event
  741. when :call, :return
  742. assert_equal([[:req, :a], [:req, :b], [:req, :c]], tp.parameters)
  743. when :b_call, :b_return
  744. next if tp.parameters == []
  745. if tp.parameters.first == [:opt, :x]
  746. assert_equal([[:opt, :x], [:opt, :y], [:opt, :z]], tp.parameters)
  747. else
  748. assert_equal([[:req, :p], [:req, :q], [:req, :r]], tp.parameters)
  749. end
  750. when :c_call, :c_return
  751. assert_equal([[:req]], tp.parameters) if tp.method_id == :getbyte
  752. when :line, :class, :end, :raise
  753. assert_raise(RuntimeError) { tp.parameters }
  754. end
  755. }
  756. obj = Object.new
  757. trace.enable{
  758. parameter_test(1, 2, 3) {|x, y, z|
  759. }
  760. lambda {|p, q, r| }.call(4, 5, 6)
  761. "".getbyte(0)
  762. class << obj
  763. end
  764. begin
  765. raise
  766. rescue
  767. end
  768. }
  769. end
  770. def method_test_tracepoint_return_value obj
  771. obj
  772. end
  773. def test_tracepoint_return_value
  774. trace = TracePoint.new(:call, :return){|tp|
  775. next if !target_thread?
  776. next if tp.path != __FILE__
  777. case tp.event
  778. when :call
  779. assert_raise(RuntimeError) {tp.return_value}
  780. when :return
  781. assert_equal("xyzzy", tp.return_value)
  782. end
  783. }
  784. trace.enable{
  785. method_test_tracepoint_return_value "xyzzy"
  786. }
  787. end
  788. class XYZZYException < Exception; end
  789. def method_test_tracepoint_raised_exception err
  790. raise err
  791. end
  792. def test_tracepoint_raised_exception
  793. trace = TracePoint.new(:call, :return, :raise){|tp|
  794. next if !target_thread?
  795. case tp.event
  796. when :call, :return
  797. assert_raise(RuntimeError) { tp.raised_exception }
  798. when :raise
  799. assert_kind_of(XYZZYException, tp.raised_exception)
  800. end
  801. }
  802. trace.enable{
  803. begin
  804. method_test_tracepoint_raised_exception XYZZYException
  805. rescue XYZZYException
  806. # ok
  807. else
  808. raise
  809. end
  810. }
  811. end
  812. def method_for_test_tracepoint_block
  813. yield
  814. end
  815. def test_tracepoint_block
  816. events = []
  817. TracePoint.new(:call, :return, :c_call, :b_call, :c_return, :b_return){|tp|
  818. next if !target_thread?
  819. events << [
  820. tp.event, tp.method_id, tp.defined_class, tp.self.class,
  821. /return/ =~ tp.event ? tp.return_value : nil
  822. ]
  823. }.enable{
  824. 1.times{
  825. 3
  826. }
  827. method_for_test_tracepoint_block{
  828. 4
  829. }
  830. }
  831. # pp events
  832. # expected_events =
  833. [[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  834. [:c_call, :times, Integer, Integer, nil],
  835. [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  836. [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3],
  837. [:c_return, :times, Integer, Integer, 1],
  838. [:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  839. [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  840. [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
  841. [:return, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
  842. [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4]
  843. ].zip(events){|expected, actual|
  844. assert_equal(expected, actual)
  845. }
  846. end
  847. def test_tracepoint_thread
  848. events = []
  849. thread_self = nil
  850. created_thread = nil
  851. TracePoint.new(:thread_begin, :thread_end){|tp|
  852. events << [Thread.current,
  853. tp.event,
  854. tp.lineno, #=> 0
  855. tp.path, #=> nil
  856. tp.binding, #=> nil
  857. tp.defined_class, #=> nil,
  858. tp.self.class # tp.self return creating/ending thread
  859. ]
  860. }.enable{
  861. created_thread = Thread.new{thread_self = self}
  862. created_thread.join
  863. }
  864. events.reject!{|i| i[0] != created_thread}
  865. assert_equal(self, thread_self)
  866. assert_equal([created_thread, :thread_begin, 0, nil, nil, nil, Thread], events[0])
  867. assert_equal([created_thread, :thread_end, 0, nil, nil, nil, Thread], events[1])
  868. assert_equal(2, events.size)
  869. end
  870. def test_tracepoint_inspect
  871. events = []
  872. th = nil
  873. trace = TracePoint.new{|tp|
  874. next if !target_thread? && th != Thread.current
  875. events << [tp.event, tp.inspect]
  876. }
  877. assert_equal("#<TracePoint:disabled>", trace.inspect)
  878. trace.enable{
  879. assert_equal("#<TracePoint:enabled>", trace.inspect)
  880. th = Thread.new{}
  881. th.join
  882. }
  883. assert_equal("#<TracePoint:disabled>", trace.inspect)
  884. events.each{|(ev, str)|
  885. case ev
  886. when :line
  887. assert_match(/ in /, str)
  888. when :call, :c_call
  889. assert_match(/call \`/, str) # #<TracePoint:c_call `inherited'@../trunk/test.rb:11>
  890. when :return, :c_return
  891. assert_match(/return \`/, str) # #<TracePoint:return `m'@../trunk/test.rb:3>
  892. when /thread/
  893. assert_match(/\#<Thread:/, str) # #<TracePoint:thread_end of #<Thread:0x87076c0>>
  894. else
  895. assert_match(/\#<TracePoint:/, str)
  896. end
  897. }
  898. end
  899. def test_tracepoint_exception_at_line
  900. assert_raise(RuntimeError) do
  901. TracePoint.new(:line) {
  902. next if !target_thread?
  903. raise
  904. }.enable {
  905. 1
  906. }
  907. end
  908. end
  909. def test_tracepoint_exception_at_return
  910. assert_nothing_raised(Timeout::Error, 'infinite trace') do
  911. assert_normal_exit('def m; end; TracePoint.new(:return) {raise}.enable {m}', '', timeout: 3)
  912. end
  913. end
  914. def test_tracepoint_exception_at_c_return
  915. assert_nothing_raised(Timeout::Error, 'infinite trace') do
  916. assert_normal_exit %q{
  917. begin
  918. TracePoint.new(:c_return){|tp|
  919. raise
  920. }.enable{
  921. tap{ itself }
  922. }
  923. rescue
  924. end
  925. }, '', timeout: 3
  926. end
  927. end
  928. def test_tracepoint_with_multithreads
  929. assert_nothing_raised do
  930. TracePoint.new{
  931. 10.times{
  932. Thread.pass
  933. }
  934. }.enable do
  935. (1..10).map{
  936. Thread.new{
  937. 1000.times{
  938. }
  939. }
  940. }.each{|th|
  941. th.join
  942. }
  943. end
  944. end
  945. end
  946. class FOO_ERROR < RuntimeError; end
  947. class BAR_ERROR < RuntimeError; end
  948. def m1_test_trace_point_at_return_when_exception
  949. m2_test_trace_point_at_return_when_exception
  950. end
  951. def m2_test_trace_point_at_return_when_exception
  952. raise BAR_ERROR
  953. end
  954. def test_trace_point_at_return_when_exception
  955. bug_7624 = '[ruby-core:51128] [ruby-trunk - Bug #7624]'
  956. TracePoint.new{|tp|
  957. next if !target_thread?
  958. if tp.event == :return &&
  959. tp.method_id == :m2_test_trace_point_at_return_when_exception
  960. raise FOO_ERROR
  961. end
  962. }.enable do
  963. assert_raise(FOO_ERROR, bug_7624) do
  964. m1_test_trace_point_at_return_when_exception
  965. end
  966. end
  967. bug_7668 = '[Bug #7668]'
  968. ary = []
  969. trace = TracePoint.new{|tp|
  970. next if !target_thread?
  971. ary << tp.event
  972. raise
  973. }
  974. begin
  975. trace.enable{
  976. 1.times{
  977. raise
  978. }
  979. }
  980. rescue
  981. assert_equal([:b_call, :b_return], ary, bug_7668)
  982. end
  983. end
  984. def m1_for_test_trace_point_binding_in_ifunc(arg)
  985. arg + nil
  986. rescue
  987. end
  988. def m2_for_test_trace_point_binding_in_ifunc(arg)
  989. arg.inject(:+)
  990. rescue
  991. end
  992. def test_trace_point_binding_in_ifunc
  993. bug7774 = '[ruby-dev:46908]'
  994. src = %q{
  995. tp = TracePoint.new(:raise) do |tp|
  996. tp.binding
  997. end
  998. tp.enable do
  999. obj = Object.new
  1000. class << obj
  1001. include Enumerable
  1002. def each
  1003. yield 1
  1004. end
  1005. end
  1006. %s
  1007. end
  1008. }
  1009. assert_normal_exit src % %q{obj.zip({}) {}}, bug7774
  1010. assert_normal_exit src % %q{
  1011. require 'continuation'
  1012. begin
  1013. c = nil
  1014. obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
  1015. c.call
  1016. rescue RuntimeError
  1017. end
  1018. }, bug7774
  1019. # TracePoint
  1020. tp_b = nil
  1021. TracePoint.new(:raise) do |tp|
  1022. next if !target_thread?
  1023. tp_b = tp.binding
  1024. end.enable do
  1025. m1_for_test_trace_point_binding_in_ifunc(0)
  1026. assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
  1027. m2_for_test_trace_point_binding_in_ifunc([0, nil])
  1028. assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
  1029. end
  1030. # set_trace_func
  1031. stf_b = nil
  1032. set_trace_func ->(event, file, line, id, binding, klass) do
  1033. stf_b = binding if event == 'raise'
  1034. end
  1035. begin
  1036. m1_for_test_trace_point_binding_in_ifunc(0)
  1037. assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
  1038. m2_for_test_trace_point_binding_in_ifunc([0, nil])
  1039. assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
  1040. ensure
  1041. set_trace_func(nil)
  1042. end
  1043. end
  1044. def test_trace_point_binding_after_break
  1045. bug10689 = '[ruby-dev:48797]'
  1046. assert_in_out_err([], <<-INPUT, [], [], bug10689)
  1047. class Bug
  1048. include Enumerable
  1049. def each
  1050. [0].each do
  1051. yield
  1052. end
  1053. end
  1054. end
  1055. TracePoint.trace(:c_return) do |tp|
  1056. tp.binding
  1057. end
  1058. Bug.new.all? { false }
  1059. INPUT
  1060. end
  1061. def test_tracepoint_b_return_with_next
  1062. n = 0
  1063. TracePoint.new(:b_return){
  1064. next if !target_thread?
  1065. n += 1
  1066. }.enable{
  1067. 3.times{
  1068. next
  1069. } # 3 times b_return
  1070. } # 1 time b_return
  1071. assert_equal 4, n
  1072. end
  1073. def test_tracepoint_b_return_with_lambda
  1074. n = 0
  1075. TracePoint.new(:b_return){
  1076. next if !target_thread?
  1077. n+=1
  1078. }.enable{
  1079. lambda{
  1080. return
  1081. }.call # n += 1 #=> 1
  1082. 3.times{
  1083. lambda{
  1084. return # n += 3 #=> 4
  1085. }.call
  1086. } # n += 3 #=> 7
  1087. begin
  1088. lambda{
  1089. raise
  1090. }.call # n += 1 #=> 8
  1091. rescue
  1092. # ignore
  1093. end # n += 1 #=> 9
  1094. }
  1095. assert_equal 9, n
  1096. end
  1097. def test_isolated_raise_in_trace
  1098. bug9088 = '[ruby-dev:47793] [Bug #9088]'
  1099. assert_in_out_err([], <<-END, [], [], bug9088)
  1100. set_trace_func proc {raise rescue nil}
  1101. 1.times {break}
  1102. END
  1103. end
  1104. def test_a_call
  1105. events = []
  1106. TracePoint.new(:a_call){|tp|
  1107. next if !target_thread?
  1108. events << tp.event
  1109. }.enable{
  1110. 1.times{
  1111. 3
  1112. }
  1113. method_for_test_tracepoint_block{
  1114. 4
  1115. }
  1116. }
  1117. assert_equal([
  1118. :b_call,
  1119. :c_call,
  1120. :b_call,
  1121. :call,
  1122. :b_call,
  1123. ], events)
  1124. end
  1125. def test_a_return
  1126. events = []
  1127. TracePoint.new(:a_return){|tp|
  1128. next if !target_thread?
  1129. events << tp.event
  1130. }.enable{
  1131. 1.times{
  1132. 3
  1133. }
  1134. method_for_test_tracepoint_block{
  1135. 4
  1136. }
  1137. }
  1138. assert_equal([
  1139. :b_return,
  1140. :c_return,
  1141. :b_return,
  1142. :return,
  1143. :b_return
  1144. ], events)
  1145. end
  1146. def test_const_missing
  1147. bug59398 = '[ruby-core:59398]'
  1148. events = []
  1149. assert !defined?(MISSING_CONSTANT_59398)
  1150. TracePoint.new(:c_call, :c_return, :call, :return){|tp|
  1151. next if !target_thread?
  1152. next unless tp.defined_class == Module
  1153. # rake/ext/module.rb aliases :const_missing and Ruby uses the aliased name
  1154. # but this only happens when running the full test suite
  1155. events << [tp.event,tp.method_id] if tp.method_id == :const_missing || tp.method_id == :rake_original_const_missing
  1156. }.enable{
  1157. MISSING_CONSTANT_59398 rescue nil
  1158. }
  1159. if events.map{|e|e[1]}.include?(:rake_original_const_missing)
  1160. assert_equal([
  1161. [:call, :const_missing],
  1162. [:c_call, :rake_original_const_missing],
  1163. [:c_return, :rake_original_const_missing],
  1164. [:return, :const_missing],
  1165. ], events, bug59398)
  1166. else
  1167. assert_equal([
  1168. [:c_call, :const_missing],
  1169. [:c_return, :const_missing]
  1170. ], events, bug59398)
  1171. end
  1172. end
  1173. class AliasedRubyMethod
  1174. def foo; 1; end;
  1175. alias bar foo
  1176. end
  1177. def test_aliased_ruby_method
  1178. events = []
  1179. aliased = AliasedRubyMethod.new
  1180. TracePoint.new(:call, :return){|tp|
  1181. next if !target_thread?
  1182. events << [tp.event, tp.method_id]
  1183. }.enable{
  1184. aliased.bar
  1185. }
  1186. assert_equal([
  1187. [:call, :foo],
  1188. [:return, :foo]
  1189. ], events, "should use original method name for tracing ruby methods")
  1190. end
  1191. class AliasedCMethod < Hash
  1192. alias original_size size
  1193. def size; original_size; end
  1194. end
  1195. def test_aliased_c_method
  1196. events = []
  1197. aliased = AliasedCMethod.new
  1198. TracePoint.new(:call, :return, :c_call, :c_return){|tp|
  1199. next if !target_thread?
  1200. events << [tp.event, tp.method_id]
  1201. }.enable{
  1202. aliased.size
  1203. }
  1204. assert_equal([
  1205. [:call, :size],
  1206. [:c_call, :size],
  1207. [:c_return, :size],
  1208. [:return, :size]
  1209. ], events, "should use alias method name for tracing c methods")
  1210. end
  1211. def test_method_missing
  1212. bug59398 = '[ruby-core:59398]'
  1213. events = []
  1214. assert !respond_to?(:missing_method_59398)
  1215. TracePoint.new(:c_call, :c_return, :call, :return){|tp|
  1216. next if !target_thread?
  1217. next unless tp.defined_class == BasicObject
  1218. # rake/ext/module.rb aliases :const_missing and Ruby uses the aliased name
  1219. # but this only happens when running the full test suite
  1220. events << [tp.event,tp.method_id] if tp.method_id == :method_missing
  1221. }.enable{
  1222. missing_method_59398 rescue nil
  1223. }
  1224. assert_equal([
  1225. [:c_call, :method_missing],
  1226. [:c_return, :method_missing]
  1227. ], events, bug59398)
  1228. end
  1229. class C9759
  1230. define_method(:foo){
  1231. raise
  1232. }
  1233. end
  1234. def test_define_method_on_exception
  1235. events = []
  1236. obj = C9759.new
  1237. TracePoint.new(:call, :return){|tp|
  1238. next unless target_thread?
  1239. events << [tp.event, tp.method_id]
  1240. }.enable{
  1241. obj.foo rescue nil
  1242. }
  1243. assert_equal([[:call, :foo], [:return, :foo]], events, 'Bug #9759')
  1244. events = []
  1245. begin
  1246. set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
  1247. next unless target_thread?
  1248. case event
  1249. when 'call', 'return'
  1250. events << [event, mid]
  1251. end
  1252. })
  1253. obj.foo rescue nil
  1254. set_trace_func(nil)
  1255. assert_equal([['call', :foo], ['return', :foo]], events, 'Bug #9759')
  1256. ensure
  1257. end
  1258. end
  1259. class C11492
  1260. define_method(:foo_return){
  1261. return true
  1262. }
  1263. define_method(:foo_break){
  1264. break true
  1265. }
  1266. end
  1267. def test_define_method_on_return
  1268. # return
  1269. events = []
  1270. obj = C11492.new
  1271. TracePoint.new(:call, :return){|tp|
  1272. next unless target_thread?
  1273. events << [tp.event, tp.method_id]
  1274. }.enable{
  1275. obj.foo_return
  1276. }
  1277. assert_equal([[:call, :foo_return], [:return, :foo_return]], events, 'Bug #11492')
  1278. # break
  1279. events = []
  1280. obj = C11492.new
  1281. TracePoint.new(:call, :return){|tp|
  1282. next unless target_thread?
  1283. events << [tp.event, tp.method_id]
  1284. }.enable{
  1285. obj.foo_break
  1286. }
  1287. assert_equal([[:call, :foo_break], [:return, :foo_break]], events, 'Bug #11492')
  1288. # set_trace_func
  1289. # return
  1290. events = []
  1291. begin
  1292. set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
  1293. next unless target_thread?
  1294. case event
  1295. when 'call', 'return'
  1296. events << [event, mid]
  1297. end
  1298. })
  1299. obj.foo_return
  1300. set_trace_func(nil)
  1301. assert_equal([['call', :foo_return], ['return', :foo_return]], events, 'Bug #11492')
  1302. ensure
  1303. end
  1304. # break
  1305. events = []
  1306. begin
  1307. set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
  1308. next unless target_thread?
  1309. case event
  1310. when 'call', 'return'
  1311. events << [event, mid]
  1312. end
  1313. })
  1314. obj.foo_break
  1315. set_trace_func(nil)
  1316. assert_equal([['call', :foo_break], ['return', :foo_break]], events, 'Bug #11492')
  1317. ensure
  1318. end
  1319. end
  1320. def test_recursive
  1321. assert_in_out_err([], %q{\
  1322. TracePoint.new(:c_call){|tp|
  1323. p tp.method_id
  1324. }.enable{
  1325. p 1
  1326. }
  1327. }, %w[:p :to_s 1], [], '[Bug #9940]')
  1328. end
  1329. def method_prefix event
  1330. case event
  1331. when :call, :return
  1332. :n
  1333. when :c_call, :c_return
  1334. :c
  1335. when :b_call, :b_return
  1336. :b
  1337. end
  1338. end
  1339. def method_label tp
  1340. "#{method_prefix(tp.event)}##{tp.method_id}"
  1341. end
  1342. def assert_consistent_call_return message='', check_events: nil
  1343. check_events ||= %i(a_call a_return)
  1344. call_stack = []
  1345. TracePoint.new(*check_events){|tp|
  1346. next unless target_thread?
  1347. case tp.event.to_s
  1348. when /call/
  1349. call_stack << method_label(tp)
  1350. when /return/
  1351. frame = call_stack.pop
  1352. assert_equal(frame, method_label(tp))
  1353. end
  1354. }.enable do
  1355. yield
  1356. end
  1357. assert_equal true, call_stack.empty?
  1358. end
  1359. def method_test_rescue_should_not_cause_b_return
  1360. begin
  1361. raise
  1362. rescue
  1363. return
  1364. end
  1365. end
  1366. def method_test_ensure_should_not_cause_b_return
  1367. begin
  1368. raise
  1369. ensure
  1370. return
  1371. end
  1372. end
  1373. def test_rescue_and_ensure_should_not_cause_b_return
  1374. assert_consistent_call_return '[Bug #9957]' do
  1375. method_test_rescue_should_not_cause_b_return
  1376. begin
  1377. method_test_ensure_should_not_cause_b_return
  1378. rescue
  1379. # ignore
  1380. end
  1381. end
  1382. end
  1383. define_method(:method_test_argument_error_on_bmethod){|correct_key: 1|}
  1384. def test_argument_error_on_bmethod
  1385. assert_consistent_call_return '[Bug #9959]' do
  1386. begin
  1387. method_test_argument_error_on_bmethod(wrong_key: 2)
  1388. rescue
  1389. # ignore
  1390. end
  1391. end
  1392. end
  1393. def test_rb_rescue
  1394. assert_consistent_call_return '[Bug #9961]' do
  1395. begin
  1396. -Numeric.new
  1397. rescue
  1398. # ignore
  1399. end
  1400. end
  1401. end
  1402. def test_b_call_with_redo
  1403. assert_consistent_call_return '[Bug #9964]' do
  1404. i = 0
  1405. 1.times{
  1406. break if (i+=1) > 10
  1407. redo
  1408. }
  1409. end
  1410. end
  1411. def test_no_duplicate_line_events
  1412. lines = []
  1413. dummy = []
  1414. TracePoint.new(:line){|tp|
  1415. next unless target_thread?
  1416. lines << tp.lineno
  1417. }.enable{
  1418. dummy << (1) + (2)
  1419. dummy << (1) + (2)
  1420. }
  1421. assert_equal [__LINE__ - 3, __LINE__ - 2], lines, 'Bug #10449'
  1422. end
  1423. def test_elsif_line_event
  1424. bug10763 = '[ruby-core:67720] [Bug #10763]'
  1425. lines = []
  1426. line = nil
  1427. TracePoint.new(:line){|tp|
  1428. next unless target_thread?
  1429. lines << tp.lineno if line
  1430. }.enable{
  1431. line = __LINE__
  1432. if !line
  1433. 1
  1434. elsif line
  1435. 2
  1436. end
  1437. }
  1438. assert_equal [line+1, line+3, line+4], lines, bug10763
  1439. end
  1440. class Bug10724
  1441. def initialize
  1442. loop{return}
  1443. end
  1444. end
  1445. def test_throwing_return_with_finish_frame
  1446. evs = []
  1447. TracePoint.new(:call, :return){|tp|
  1448. next unless target_thread?
  1449. evs << tp.event
  1450. }.enable{
  1451. Bug10724.new
  1452. }
  1453. assert_equal([:call, :return], evs)
  1454. end
  1455. require 'fiber'
  1456. def test_fiber_switch
  1457. # test for resume/yield
  1458. evs = []
  1459. TracePoint.new(:fiber_switch){|tp|
  1460. next unless target_thread?
  1461. evs << tp.event
  1462. }.enable{
  1463. f = Fiber.new{
  1464. Fiber.yield
  1465. Fiber.yield
  1466. Fiber.yield
  1467. }
  1468. f.resume
  1469. f.resume
  1470. f.resume
  1471. f.resume
  1472. begin
  1473. f.resume
  1474. rescue FiberError
  1475. end
  1476. }
  1477. assert_equal 8, evs.size
  1478. evs.each{|ev|
  1479. assert_equal ev, :fiber_switch
  1480. }
  1481. # test for transfer
  1482. evs = []
  1483. TracePoint.new(:fiber_switch){|tp|
  1484. next unless target_thread?
  1485. evs << tp.event
  1486. }.enable{
  1487. f1 = f2 = nil
  1488. f1 = Fiber.new{
  1489. f2.transfer
  1490. f2.transfer
  1491. Fiber.yield :ok
  1492. }
  1493. f2 = Fiber.new{
  1494. f1.transfer
  1495. f1.transfer
  1496. }
  1497. assert_equal :ok, f1.resume
  1498. }
  1499. assert_equal 6, evs.size
  1500. evs.each{|ev|
  1501. assert_equal ev, :fiber_switch
  1502. }
  1503. end
  1504. def test_tracepoint_callee_id
  1505. events = []
  1506. capture_events = Proc.new{|tp|
  1507. next unless target_thread?
  1508. events << [tp.event, tp.method_id, tp.callee_id]
  1509. }
  1510. o = Class.new{
  1511. def m
  1512. raise
  1513. end
  1514. alias alias_m m
  1515. }.new
  1516. TracePoint.new(:raise, :call, :return, &capture_events).enable{
  1517. o.alias_m rescue nil
  1518. }…

Large files files are truncated, but you can click here to view the full file