/test/ruby/test_settracefunc.rb

https://github.com/kosaki/ruby · Ruby · 994 lines · 915 code · 66 blank · 13 comment · 34 complexity · 0f726b322956e4f965b8a72e07e0ff1d MD5 · raw file

  1. require 'test/unit'
  2. require_relative 'envutil'
  3. class TestSetTraceFunc < Test::Unit::TestCase
  4. def setup
  5. @original_compile_option = RubyVM::InstructionSequence.compile_option
  6. RubyVM::InstructionSequence.compile_option = {
  7. :trace_instruction => true,
  8. :specialized_instruction => false
  9. }
  10. end
  11. def teardown
  12. set_trace_func(nil)
  13. RubyVM::InstructionSequence.compile_option = @original_compile_option
  14. end
  15. def test_c_call
  16. events = []
  17. name = "#{self.class}\##{__method__}"
  18. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  19. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  20. 2: events << [event, lineno, mid, klass] if file == name
  21. 3: })
  22. 4: x = 1 + 1
  23. 5: set_trace_func(nil)
  24. EOF
  25. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  26. events.shift)
  27. assert_equal(["line", 4, __method__, self.class],
  28. events.shift)
  29. assert_equal(["c-call", 4, :+, Fixnum],
  30. events.shift)
  31. assert_equal(["c-return", 4, :+, Fixnum],
  32. events.shift)
  33. assert_equal(["line", 5, __method__, self.class],
  34. events.shift)
  35. assert_equal(["c-call", 5, :set_trace_func, Kernel],
  36. events.shift)
  37. assert_equal([], events)
  38. end
  39. def test_call
  40. events = []
  41. name = "#{self.class}\##{__method__}"
  42. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  43. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  44. 2: events << [event, lineno, mid, klass] if file == name
  45. 3: })
  46. 4: def add(x, y)
  47. 5: x + y
  48. 6: end
  49. 7: x = add(1, 1)
  50. 8: set_trace_func(nil)
  51. EOF
  52. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  53. events.shift)
  54. assert_equal(["line", 4, __method__, self.class],
  55. events.shift)
  56. assert_equal(["c-call", 4, :method_added, self.class],
  57. events.shift)
  58. assert_equal(["c-return", 4, :method_added, self.class],
  59. events.shift)
  60. assert_equal(["line", 7, __method__, self.class],
  61. events.shift)
  62. assert_equal(["call", 4, :add, self.class],
  63. events.shift)
  64. assert_equal(["line", 5, :add, self.class],
  65. events.shift)
  66. assert_equal(["c-call", 5, :+, Fixnum],
  67. events.shift)
  68. assert_equal(["c-return", 5, :+, Fixnum],
  69. events.shift)
  70. assert_equal(["return", 6, :add, self.class],
  71. events.shift)
  72. assert_equal(["line", 8, __method__, self.class],
  73. events.shift)
  74. assert_equal(["c-call", 8, :set_trace_func, Kernel],
  75. events.shift)
  76. assert_equal([], events)
  77. end
  78. def test_class
  79. events = []
  80. name = "#{self.class}\##{__method__}"
  81. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  82. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  83. 2: events << [event, lineno, mid, klass] if file == name
  84. 3: })
  85. 4: class Foo
  86. 5: def bar
  87. 6: end
  88. 7: end
  89. 8: x = Foo.new.bar
  90. 9: set_trace_func(nil)
  91. EOF
  92. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  93. events.shift)
  94. assert_equal(["line", 4, __method__, self.class],
  95. events.shift)
  96. assert_equal(["c-call", 4, :inherited, Class],
  97. events.shift)
  98. assert_equal(["c-return", 4, :inherited, Class],
  99. events.shift)
  100. assert_equal(["class", 4, nil, nil],
  101. events.shift)
  102. assert_equal(["line", 5, nil, nil],
  103. events.shift)
  104. assert_equal(["c-call", 5, :method_added, Module],
  105. events.shift)
  106. assert_equal(["c-return", 5, :method_added, Module],
  107. events.shift)
  108. assert_equal(["end", 7, nil, nil],
  109. events.shift)
  110. assert_equal(["line", 8, __method__, self.class],
  111. events.shift)
  112. assert_equal(["c-call", 8, :new, Class],
  113. events.shift)
  114. assert_equal(["c-call", 8, :initialize, BasicObject],
  115. events.shift)
  116. assert_equal(["c-return", 8, :initialize, BasicObject],
  117. events.shift)
  118. assert_equal(["c-return", 8, :new, Class],
  119. events.shift)
  120. assert_equal(["call", 5, :bar, Foo],
  121. events.shift)
  122. assert_equal(["return", 6, :bar, Foo],
  123. events.shift)
  124. assert_equal(["line", 9, __method__, self.class],
  125. events.shift)
  126. assert_equal(["c-call", 9, :set_trace_func, Kernel],
  127. events.shift)
  128. assert_equal([], events)
  129. end
  130. def test_return # [ruby-dev:38701]
  131. events = []
  132. name = "#{self.class}\##{__method__}"
  133. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  134. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  135. 2: events << [event, lineno, mid, klass] if file == name
  136. 3: })
  137. 4: def meth_return(a)
  138. 5: return if a
  139. 6: return
  140. 7: end
  141. 8: meth_return(true)
  142. 9: meth_return(false)
  143. 10: set_trace_func(nil)
  144. EOF
  145. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  146. events.shift)
  147. assert_equal(["line", 4, __method__, self.class],
  148. events.shift)
  149. assert_equal(["c-call", 4, :method_added, self.class],
  150. events.shift)
  151. assert_equal(["c-return", 4, :method_added, self.class],
  152. events.shift)
  153. assert_equal(["line", 8, __method__, self.class],
  154. events.shift)
  155. assert_equal(["call", 4, :meth_return, self.class],
  156. events.shift)
  157. assert_equal(["line", 5, :meth_return, self.class],
  158. events.shift)
  159. assert_equal(["return", 5, :meth_return, self.class],
  160. events.shift)
  161. assert_equal(["line", 9, :test_return, self.class],
  162. events.shift)
  163. assert_equal(["call", 4, :meth_return, self.class],
  164. events.shift)
  165. assert_equal(["line", 5, :meth_return, self.class],
  166. events.shift)
  167. assert_equal(["return", 7, :meth_return, self.class],
  168. events.shift)
  169. assert_equal(["line", 10, :test_return, self.class],
  170. events.shift)
  171. assert_equal(["c-call", 10, :set_trace_func, Kernel],
  172. events.shift)
  173. assert_equal([], events)
  174. end
  175. def test_return2 # [ruby-core:24463]
  176. events = []
  177. name = "#{self.class}\##{__method__}"
  178. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  179. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  180. 2: events << [event, lineno, mid, klass] if file == name
  181. 3: })
  182. 4: def meth_return2
  183. 5: a = 5
  184. 6: return a
  185. 7: end
  186. 8: meth_return2
  187. 9: set_trace_func(nil)
  188. EOF
  189. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  190. events.shift)
  191. assert_equal(["line", 4, __method__, self.class],
  192. events.shift)
  193. assert_equal(["c-call", 4, :method_added, self.class],
  194. events.shift)
  195. assert_equal(["c-return", 4, :method_added, self.class],
  196. events.shift)
  197. assert_equal(["line", 8, __method__, self.class],
  198. events.shift)
  199. assert_equal(["call", 4, :meth_return2, self.class],
  200. events.shift)
  201. assert_equal(["line", 5, :meth_return2, self.class],
  202. events.shift)
  203. assert_equal(["line", 6, :meth_return2, self.class],
  204. events.shift)
  205. assert_equal(["return", 7, :meth_return2, self.class],
  206. events.shift)
  207. assert_equal(["line", 9, :test_return2, self.class],
  208. events.shift)
  209. assert_equal(["c-call", 9, :set_trace_func, Kernel],
  210. events.shift)
  211. assert_equal([], events)
  212. end
  213. def test_raise
  214. events = []
  215. name = "#{self.class}\##{__method__}"
  216. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  217. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  218. 2: events << [event, lineno, mid, klass] if file == name
  219. 3: })
  220. 4: begin
  221. 5: raise TypeError, "error"
  222. 6: rescue TypeError
  223. 7: end
  224. 8: set_trace_func(nil)
  225. EOF
  226. assert_equal(["c-return", 1, :set_trace_func, Kernel],
  227. events.shift)
  228. assert_equal(["line", 4, __method__, self.class],
  229. events.shift)
  230. assert_equal(["line", 5, __method__, self.class],
  231. events.shift)
  232. assert_equal(["c-call", 5, :raise, Kernel],
  233. events.shift)
  234. assert_equal(["c-call", 5, :exception, Exception],
  235. events.shift)
  236. assert_equal(["c-call", 5, :initialize, Exception],
  237. events.shift)
  238. assert_equal(["c-return", 5, :initialize, Exception],
  239. events.shift)
  240. assert_equal(["c-return", 5, :exception, Exception],
  241. events.shift)
  242. assert_equal(["c-call", 5, :backtrace, Exception],
  243. events.shift)
  244. assert_equal(["c-return", 5, :backtrace, Exception],
  245. events.shift)
  246. assert_equal(["raise", 5, :test_raise, TestSetTraceFunc],
  247. events.shift)
  248. assert_equal(["c-return", 5, :raise, Kernel],
  249. events.shift)
  250. assert_equal(["c-call", 6, :===, Module],
  251. events.shift)
  252. assert_equal(["c-return", 6, :===, Module],
  253. events.shift)
  254. assert_equal(["line", 8, __method__, self.class],
  255. events.shift)
  256. assert_equal(["c-call", 8, :set_trace_func, Kernel],
  257. events.shift)
  258. assert_equal([], events)
  259. end
  260. def test_break # [ruby-core:27606] [Bug #2610]
  261. events = []
  262. name = "#{self.class}\##{__method__}"
  263. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  264. 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  265. 2: events << [event, lineno, mid, klass] if file == name
  266. 3: })
  267. 4: [1,2,3].any? {|n| n}
  268. 8: set_trace_func(nil)
  269. EOF
  270. [["c-return", 1, :set_trace_func, Kernel],
  271. ["line", 4, __method__, self.class],
  272. ["c-call", 4, :any?, Enumerable],
  273. ["c-call", 4, :each, Array],
  274. ["line", 4, __method__, self.class],
  275. ["c-return", 4, :each, Array],
  276. ["c-return", 4, :any?, Enumerable],
  277. ["line", 5, __method__, self.class],
  278. ["c-call", 5, :set_trace_func, Kernel]].each{|e|
  279. assert_equal(e, events.shift)
  280. }
  281. end
  282. def test_invalid_proc
  283. assert_raise(TypeError) { set_trace_func(1) }
  284. end
  285. def test_raise_in_trace
  286. set_trace_func proc {raise rescue nil}
  287. assert_equal(42, (raise rescue 42), '[ruby-core:24118]')
  288. end
  289. def test_thread_trace
  290. events = {:set => [], :add => []}
  291. prc = Proc.new { |event, file, lineno, mid, binding, klass|
  292. events[:set] << [event, lineno, mid, klass, :set]
  293. }
  294. prc = prc # suppress warning
  295. prc2 = Proc.new { |event, file, lineno, mid, binding, klass|
  296. events[:add] << [event, lineno, mid, klass, :add]
  297. }
  298. prc2 = prc2 # suppress warning
  299. th = Thread.new do
  300. th = Thread.current
  301. name = "#{self.class}\##{__method__}"
  302. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  303. 1: th.set_trace_func(prc)
  304. 2: th.add_trace_func(prc2)
  305. 3: class ThreadTraceInnerClass
  306. 4: def foo
  307. 5: _x = 1 + 1
  308. 6: end
  309. 7: end
  310. 8: ThreadTraceInnerClass.new.foo
  311. 9: th.set_trace_func(nil)
  312. EOF
  313. end
  314. th.join
  315. [["c-return", 1, :set_trace_func, Thread, :set],
  316. ["line", 2, __method__, self.class, :set],
  317. ["c-call", 2, :add_trace_func, Thread, :set]].each do |e|
  318. assert_equal(e, events[:set].shift)
  319. end
  320. [["c-return", 2, :add_trace_func, Thread],
  321. ["line", 3, __method__, self.class],
  322. ["c-call", 3, :inherited, Class],
  323. ["c-return", 3, :inherited, Class],
  324. ["class", 3, nil, nil],
  325. ["line", 4, nil, nil],
  326. ["c-call", 4, :method_added, Module],
  327. ["c-return", 4, :method_added, Module],
  328. ["end", 7, nil, nil],
  329. ["line", 8, __method__, self.class],
  330. ["c-call", 8, :new, Class],
  331. ["c-call", 8, :initialize, BasicObject],
  332. ["c-return", 8, :initialize, BasicObject],
  333. ["c-return", 8, :new, Class],
  334. ["call", 4, :foo, ThreadTraceInnerClass],
  335. ["line", 5, :foo, ThreadTraceInnerClass],
  336. ["c-call", 5, :+, Fixnum],
  337. ["c-return", 5, :+, Fixnum],
  338. ["return", 6, :foo, ThreadTraceInnerClass],
  339. ["line", 9, __method__, self.class],
  340. ["c-call", 9, :set_trace_func, Thread]].each do |e|
  341. [:set, :add].each do |type|
  342. assert_equal(e + [type], events[type].shift)
  343. end
  344. end
  345. assert_equal([], events[:set])
  346. assert_equal([], events[:add])
  347. end
  348. def test_trace_defined_method
  349. events = []
  350. name = "#{self.class}\##{__method__}"
  351. eval <<-EOF.gsub(/^.*?: /, ""), nil, name
  352. 1: class FooBar; define_method(:foobar){}; end
  353. 2: fb = FooBar.new
  354. 3: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
  355. 4: events << [event, lineno, mid, klass] if file == name
  356. 5: })
  357. 6: fb.foobar
  358. 7: set_trace_func(nil)
  359. EOF
  360. [["c-return", 3, :set_trace_func, Kernel],
  361. ["line", 6, __method__, self.class],
  362. ["call", 6, :foobar, FooBar],
  363. ["return", 6, :foobar, FooBar],
  364. ["line", 7, __method__, self.class],
  365. ["c-call", 7, :set_trace_func, Kernel]].each{|e|
  366. assert_equal(e, events.shift)
  367. }
  368. end
  369. def test_remove_in_trace
  370. bug3921 = '[ruby-dev:42350]'
  371. ok = false
  372. func = lambda{|e, f, l, i, b, k|
  373. set_trace_func(nil)
  374. ok = eval("self", b)
  375. }
  376. set_trace_func(func)
  377. assert_equal(self, ok, bug3921)
  378. end
  379. class << self
  380. define_method(:method_added, Module.method(:method_added))
  381. end
  382. def trace_by_tracepoint *trace_events
  383. events = []
  384. trace = nil
  385. xyzzy = nil
  386. _local_var = :outer
  387. raised_exc = nil
  388. method = :trace_by_tracepoint
  389. _get_data = lambda{|tp|
  390. case tp.event
  391. when :return, :c_return
  392. tp.return_value
  393. when :raise
  394. tp.raised_exception
  395. else
  396. :nothing
  397. end
  398. }
  399. _defined_class = lambda{|tp|
  400. klass = tp.defined_class
  401. begin
  402. # If it is singleton method, then return original class
  403. # to make compatible with set_trace_func().
  404. # This is very ad-hoc hack. I hope I can make more clean test on it.
  405. case klass.inspect
  406. when /Class:TracePoint/; return TracePoint
  407. when /Class:Exception/; return Exception
  408. else klass
  409. end
  410. rescue Exception => e
  411. e
  412. end if klass
  413. }
  414. trace = nil
  415. begin
  416. eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
  417. 1: trace = TracePoint.trace(*trace_events){|tp|
  418. 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'
  419. 3: }
  420. 4: 1.times{|;_local_var| _local_var = :inner
  421. 5: tap{}
  422. 6: }
  423. 7: class XYZZY
  424. 8: _local_var = :XYZZY_outer
  425. 9: def foo
  426. 10: _local_var = :XYZZY_foo
  427. 11: bar
  428. 12: end
  429. 13: def bar
  430. 14: _local_var = :XYZZY_bar
  431. 15: tap{}
  432. 16: end
  433. 17: end
  434. 18: xyzzy = XYZZY.new
  435. 19: xyzzy.foo
  436. 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
  437. 21: trace.disable
  438. EOF
  439. self.class.class_eval{remove_const(:XYZZY)}
  440. ensure
  441. trace.disable if trace && trace.enabled?
  442. end
  443. answer_events = [
  444. #
  445. [:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace],
  446. [:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
  447. [:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
  448. [:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
  449. [:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
  450. [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
  451. [:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
  452. [:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
  453. [:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
  454. [:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
  455. [:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
  456. [:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
  457. [:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
  458. [:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  459. [:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
  460. [:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
  461. [:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  462. [:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
  463. [:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
  464. [:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
  465. [:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  466. [:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
  467. [:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
  468. [:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
  469. [:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
  470. [:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  471. [:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
  472. [:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
  473. [:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
  474. [:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
  475. [:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
  476. [:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
  477. [:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
  478. [:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
  479. [:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
  480. [:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
  481. [:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  482. [:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  483. [:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
  484. [:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
  485. [:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
  486. [:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
  487. [:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
  488. [:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
  489. [:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
  490. [:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
  491. [:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
  492. [:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
  493. [:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
  494. [:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
  495. [:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing],
  496. ]
  497. return events, answer_events
  498. end
  499. def trace_by_set_trace_func
  500. events = []
  501. trace = nil
  502. trace = trace
  503. xyzzy = nil
  504. xyzzy = xyzzy
  505. _local_var = :outer
  506. eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
  507. 1: set_trace_func(lambda{|event, file, line, id, binding, klass|
  508. 2: events << [event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")] if file == 'xyzzy'
  509. 3: })
  510. 4: 1.times{|;_local_var| _local_var = :inner
  511. 5: tap{}
  512. 6: }
  513. 7: class XYZZY
  514. 8: _local_var = :XYZZY_outer
  515. 9: def foo
  516. 10: _local_var = :XYZZY_foo
  517. 11: bar
  518. 12: end
  519. 13: def bar
  520. 14: _local_var = :XYZZY_bar
  521. 15: tap{}
  522. 16: end
  523. 17: end
  524. 18: xyzzy = XYZZY.new
  525. 19: xyzzy.foo
  526. 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
  527. 21: set_trace_func(nil)
  528. EOF
  529. self.class.class_eval{remove_const(:XYZZY)}
  530. return events
  531. end
  532. def test_tracepoint
  533. events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
  534. mesg = events1.map{|e|
  535. if false
  536. p [:event, e[0]]
  537. p [:line_file, e[1], e[2]]
  538. p [:id, e[4]]
  539. end
  540. "#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
  541. }.join("\n")
  542. answer_events.zip(events1){|answer, event|
  543. assert_equal answer, event, mesg
  544. }
  545. events2 = trace_by_set_trace_func
  546. events1.zip(events2){|ev1, ev2|
  547. ev2[0] = ev2[0].sub('-', '_').to_sym
  548. assert_equal ev1[0..2], ev2[0..2], ev1.inspect
  549. # event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")
  550. assert_equal ev1[3].nil?, ev2[3].nil? # klass
  551. assert_equal ev1[4].nil?, ev2[4].nil? # id
  552. assert_equal ev1[6], ev2[6] # _local_var
  553. }
  554. [:line, :class, :end, :call, :return, :c_call, :c_return, :raise].each{|event|
  555. events1, answer_events = *trace_by_tracepoint(event)
  556. answer_events.find_all{|e| e[0] == event}.zip(events1){|answer_line, event_line|
  557. assert_equal answer_line, event_line
  558. }
  559. }
  560. end
  561. def test_tracepoint_object_id
  562. tps = []
  563. trace = TracePoint.trace(){|tp|
  564. tps << tp
  565. }
  566. tap{}
  567. tap{}
  568. tap{}
  569. trace.disable
  570. # passed tp is unique, `trace' object which is genereted by TracePoint.trace
  571. tps.each{|tp|
  572. assert_equal trace, tp
  573. }
  574. end
  575. def test_tracepoint_access_from_outside
  576. tp_store = nil
  577. trace = TracePoint.trace(){|tp|
  578. tp_store = tp
  579. }
  580. tap{}
  581. trace.disable
  582. assert_raise(RuntimeError){tp_store.lineno}
  583. assert_raise(RuntimeError){tp_store.event}
  584. assert_raise(RuntimeError){tp_store.path}
  585. assert_raise(RuntimeError){tp_store.method_id}
  586. assert_raise(RuntimeError){tp_store.defined_class}
  587. assert_raise(RuntimeError){tp_store.binding}
  588. assert_raise(RuntimeError){tp_store.self}
  589. assert_raise(RuntimeError){tp_store.return_value}
  590. assert_raise(RuntimeError){tp_store.raised_exception}
  591. end
  592. def foo
  593. end
  594. def test_tracepoint_enable
  595. ary = []
  596. trace = TracePoint.new(:call){|tp|
  597. ary << tp.method_id
  598. }
  599. foo
  600. trace.enable{
  601. foo
  602. }
  603. foo
  604. assert_equal([:foo], ary)
  605. trace = TracePoint.new{}
  606. begin
  607. assert_equal(false, trace.enable)
  608. assert_equal(true, trace.enable)
  609. trace.enable{}
  610. assert_equal(true, trace.enable)
  611. ensure
  612. trace.disable
  613. end
  614. end
  615. def test_tracepoint_disable
  616. ary = []
  617. trace = TracePoint.trace(:call){|tp|
  618. ary << tp.method_id
  619. }
  620. foo
  621. trace.disable{
  622. foo
  623. }
  624. foo
  625. trace.disable
  626. assert_equal([:foo, :foo], ary)
  627. trace = TracePoint.new{}
  628. trace.enable{
  629. assert_equal(true, trace.disable)
  630. assert_equal(false, trace.disable)
  631. trace.disable{}
  632. assert_equal(false, trace.disable)
  633. }
  634. end
  635. def test_tracepoint_enabled
  636. trace = TracePoint.trace(:call){|tp|
  637. #
  638. }
  639. assert_equal(true, trace.enabled?)
  640. trace.disable{
  641. assert_equal(false, trace.enabled?)
  642. trace.enable{
  643. assert_equal(true, trace.enabled?)
  644. }
  645. }
  646. trace.disable
  647. assert_equal(false, trace.enabled?)
  648. end
  649. def method_test_tracepoint_return_value obj
  650. obj
  651. end
  652. def test_tracepoint_return_value
  653. trace = TracePoint.new(:call, :return){|tp|
  654. next if tp.path != __FILE__
  655. case tp.event
  656. when :call
  657. assert_raise(RuntimeError) {tp.return_value}
  658. when :return
  659. assert_equal("xyzzy", tp.return_value)
  660. end
  661. }
  662. trace.enable{
  663. method_test_tracepoint_return_value "xyzzy"
  664. }
  665. end
  666. class XYZZYException < Exception; end
  667. def method_test_tracepoint_raised_exception err
  668. raise err
  669. end
  670. def test_tracepoint_raised_exception
  671. trace = TracePoint.new(:call, :return){|tp|
  672. case tp.event
  673. when :call, :return
  674. assert_raise(RuntimeError) { tp.raised_exception }
  675. when :raise
  676. assert_equal(XYZZYError, tp.raised_exception)
  677. end
  678. }
  679. trace.enable{
  680. begin
  681. method_test_tracepoint_raised_exception XYZZYException
  682. rescue XYZZYException
  683. # ok
  684. else
  685. raise
  686. end
  687. }
  688. end
  689. def method_for_test_tracepoint_block
  690. yield
  691. end
  692. def test_tracepoint_block
  693. events = []
  694. TracePoint.new(:call, :return, :c_call, :b_call, :c_return, :b_return){|tp|
  695. events << [
  696. tp.event, tp.method_id, tp.defined_class, tp.self.class,
  697. /return/ =~ tp.event ? tp.return_value : nil
  698. ]
  699. }.enable{
  700. 1.times{
  701. 3
  702. }
  703. method_for_test_tracepoint_block{
  704. 4
  705. }
  706. }
  707. # pp events
  708. # expected_events =
  709. [[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  710. [:c_call, :times, Integer, Fixnum, nil],
  711. [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  712. [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3],
  713. [:c_return, :times, Integer, Fixnum, 1],
  714. [:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  715. [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
  716. [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
  717. [:return, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
  718. [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4]
  719. ].zip(events){|expected, actual|
  720. assert_equal(expected, actual)
  721. }
  722. end
  723. def test_tracepoint_thread
  724. events = []
  725. thread_self = nil
  726. created_thread = nil
  727. TracePoint.new(:thread_begin, :thread_end){|tp|
  728. events << [Thread.current,
  729. tp.event,
  730. tp.lineno, #=> 0
  731. tp.path, #=> nil
  732. tp.binding, #=> nil
  733. tp.defined_class, #=> nil,
  734. tp.self.class # tp.self return creating/ending thread
  735. ]
  736. }.enable{
  737. created_thread = Thread.new{thread_self = self}
  738. created_thread.join
  739. }
  740. assert_equal(self, thread_self)
  741. assert_equal([created_thread, :thread_begin, 0, nil, nil, nil, Thread], events[0])
  742. assert_equal([created_thread, :thread_end, 0, nil, nil, nil, Thread], events[1])
  743. assert_equal(2, events.size)
  744. end
  745. def test_tracepoint_inspect
  746. events = []
  747. trace = TracePoint.new{|tp| events << [tp.event, tp.inspect]}
  748. assert_equal("#<TracePoint:disabled>", trace.inspect)
  749. trace.enable{
  750. assert_equal("#<TracePoint:enabled>", trace.inspect)
  751. Thread.new{}.join
  752. }
  753. assert_equal("#<TracePoint:disabled>", trace.inspect)
  754. events.each{|(ev, str)|
  755. case ev
  756. when :line
  757. assert_match(/ in /, str)
  758. when :call, :c_call
  759. assert_match(/call \`/, str) # #<TracePoint:c_call `inherited'@../trunk/test.rb:11>
  760. when :return, :c_return
  761. assert_match(/return \`/, str) # #<TracePoint:return `m'@../trunk/test.rb:3>
  762. when /thread/
  763. assert_match(/\#<Thread:/, str) # #<TracePoint:thread_end of #<Thread:0x87076c0>>
  764. else
  765. assert_match(/\#<TracePoint:/, str)
  766. end
  767. }
  768. end
  769. def test_tracepoint_exception_at_line
  770. assert_raise(RuntimeError) do
  771. TracePoint.new(:line) {raise}.enable {
  772. 1
  773. }
  774. end
  775. end
  776. def test_tracepoint_exception_at_return
  777. assert_nothing_raised(Timeout::Error, 'infinite trace') do
  778. assert_normal_exit('def m; end; TracePoint.new(:return) {raise}.enable {m}', '', timeout: 3)
  779. end
  780. end
  781. def test_tracepoint_with_multithreads
  782. assert_nothing_raised do
  783. TracePoint.new{
  784. 10.times{
  785. Thread.pass
  786. }
  787. }.enable do
  788. (1..10).map{
  789. Thread.new{
  790. 1000.times{
  791. }
  792. }
  793. }.each{|th|
  794. th.join
  795. }
  796. end
  797. end
  798. end
  799. class FOO_ERROR < RuntimeError; end
  800. class BAR_ERROR < RuntimeError; end
  801. def m1_test_trace_point_at_return_when_exception
  802. m2_test_trace_point_at_return_when_exception
  803. end
  804. def m2_test_trace_point_at_return_when_exception
  805. raise BAR_ERROR
  806. end
  807. def test_trace_point_at_return_when_exception
  808. bug_7624 = '[ruby-core:51128] [ruby-trunk - Bug #7624]'
  809. TracePoint.new{|tp|
  810. if tp.event == :return &&
  811. tp.method_id == :m2_test_trace_point_at_return_when_exception
  812. raise FOO_ERROR
  813. end
  814. }.enable do
  815. assert_raise(FOO_ERROR, bug_7624) do
  816. m1_test_trace_point_at_return_when_exception
  817. end
  818. end
  819. bug_7668 = '[Bug #7668]'
  820. ary = []
  821. trace = TracePoint.new{|tp|
  822. ary << tp.event
  823. raise
  824. }
  825. begin
  826. trace.enable{
  827. 1.times{
  828. raise
  829. }
  830. }
  831. rescue
  832. assert_equal([:b_call, :b_return], ary, bug_7668)
  833. end
  834. end
  835. def m1_for_test_trace_point_binding_in_ifunc(arg)
  836. arg + nil
  837. rescue
  838. end
  839. def m2_for_test_trace_point_binding_in_ifunc(arg)
  840. arg.inject(:+)
  841. rescue
  842. end
  843. def test_trace_point_binding_in_ifunc
  844. bug7774 = '[ruby-dev:46908]'
  845. src = %q{
  846. tp = TracePoint.new(:raise) do |tp|
  847. tp.binding
  848. end
  849. tp.enable do
  850. obj = Object.new
  851. class << obj
  852. include Enumerable
  853. def each
  854. yield 1
  855. end
  856. end
  857. %s
  858. end
  859. }
  860. assert_normal_exit src % %q{obj.zip({}) {}}, bug7774
  861. assert_normal_exit src % %q{
  862. require 'continuation'
  863. begin
  864. c = nil
  865. obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
  866. c.call
  867. rescue RuntimeError
  868. end
  869. }, bug7774
  870. # TracePoint
  871. tp_b = nil
  872. TracePoint.new(:raise) do |tp|
  873. tp_b = tp.binding
  874. end.enable do
  875. m1_for_test_trace_point_binding_in_ifunc(0)
  876. assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
  877. m2_for_test_trace_point_binding_in_ifunc([0, nil])
  878. assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
  879. end
  880. # set_trace_func
  881. stf_b = nil
  882. set_trace_func ->(event, file, line, id, binding, klass) do
  883. stf_b = binding if event == 'raise'
  884. end
  885. begin
  886. m1_for_test_trace_point_binding_in_ifunc(0)
  887. assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
  888. m2_for_test_trace_point_binding_in_ifunc([0, nil])
  889. assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
  890. ensure
  891. set_trace_func(nil)
  892. end
  893. end
  894. def test_tracepoint_b_return_with_next
  895. n = 0
  896. TracePoint.new(:b_return){
  897. n += 1
  898. }.enable{
  899. 3.times{
  900. next
  901. } # 3 times b_retun
  902. } # 1 time b_return
  903. assert_equal 4, n
  904. end
  905. def test_tracepoint_b_return_with_lambda
  906. n = 0
  907. TracePoint.new(:b_return){
  908. n+=1
  909. }.enable{
  910. lambda{
  911. return
  912. }.call # n += 1 #=> 1
  913. 3.times{
  914. lambda{
  915. return # n += 3 #=> 4
  916. }.call
  917. } # n += 3 #=> 7
  918. begin
  919. lambda{
  920. raise
  921. }.call # n += 1 #=> 8
  922. rescue
  923. # ignore
  924. end # n += 1 #=> 9
  925. }
  926. assert_equal 9, n
  927. end
  928. end