byebug 3.5.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +18 -1
  4. data/.travis.yml +21 -1
  5. data/CHANGELOG.md +356 -308
  6. data/CONTRIBUTING.md +31 -15
  7. data/GUIDE.md +859 -475
  8. data/Gemfile +8 -10
  9. data/LICENSE +1 -1
  10. data/README.md +41 -45
  11. data/Rakefile +30 -28
  12. data/byebug.gemspec +18 -18
  13. data/ext/byebug/breakpoint.c +88 -75
  14. data/ext/byebug/byebug.c +253 -252
  15. data/ext/byebug/byebug.h +53 -53
  16. data/ext/byebug/context.c +188 -159
  17. data/ext/byebug/extconf.rb +9 -6
  18. data/ext/byebug/locker.c +53 -11
  19. data/ext/byebug/threads.c +137 -39
  20. data/lib/byebug/attacher.rb +7 -2
  21. data/lib/byebug/breakpoint.rb +30 -0
  22. data/lib/byebug/command.rb +36 -32
  23. data/lib/byebug/commands/break.rb +49 -48
  24. data/lib/byebug/commands/catch.rb +64 -0
  25. data/lib/byebug/commands/condition.rb +13 -9
  26. data/lib/byebug/commands/continue.rb +8 -4
  27. data/lib/byebug/commands/delete.rb +10 -4
  28. data/lib/byebug/commands/display.rb +33 -25
  29. data/lib/byebug/commands/edit.rb +18 -13
  30. data/lib/byebug/commands/enable_disable.rb +26 -24
  31. data/lib/byebug/commands/eval.rb +77 -35
  32. data/lib/byebug/commands/finish.rb +9 -5
  33. data/lib/byebug/commands/frame.rb +66 -125
  34. data/lib/byebug/commands/help.rb +14 -21
  35. data/lib/byebug/commands/history.rb +5 -1
  36. data/lib/byebug/commands/info.rb +41 -106
  37. data/lib/byebug/commands/interrupt.rb +6 -2
  38. data/lib/byebug/commands/irb.rb +5 -2
  39. data/lib/byebug/commands/kill.rb +6 -2
  40. data/lib/byebug/commands/list.rb +21 -14
  41. data/lib/byebug/commands/method.rb +17 -9
  42. data/lib/byebug/commands/pry.rb +13 -3
  43. data/lib/byebug/commands/quit.rb +10 -5
  44. data/lib/byebug/commands/restart.rb +12 -19
  45. data/lib/byebug/commands/save.rb +10 -6
  46. data/lib/byebug/commands/set.rb +15 -14
  47. data/lib/byebug/commands/show.rb +8 -8
  48. data/lib/byebug/commands/source.rb +14 -8
  49. data/lib/byebug/commands/stepping.rb +15 -29
  50. data/lib/byebug/commands/threads.rb +73 -49
  51. data/lib/byebug/commands/tracevar.rb +56 -0
  52. data/lib/byebug/commands/undisplay.rb +8 -4
  53. data/lib/byebug/commands/untracevar.rb +38 -0
  54. data/lib/byebug/commands/var.rb +107 -0
  55. data/lib/byebug/context.rb +78 -42
  56. data/lib/byebug/core.rb +78 -40
  57. data/lib/byebug/helper.rb +58 -42
  58. data/lib/byebug/history.rb +12 -1
  59. data/lib/byebug/interface.rb +91 -11
  60. data/lib/byebug/interfaces/local_interface.rb +12 -19
  61. data/lib/byebug/interfaces/remote_interface.rb +12 -15
  62. data/lib/byebug/interfaces/script_interface.rb +14 -18
  63. data/lib/byebug/interfaces/test_interface.rb +54 -0
  64. data/lib/byebug/printers/base.rb +64 -0
  65. data/lib/byebug/printers/plain.rb +53 -0
  66. data/lib/byebug/processor.rb +20 -1
  67. data/lib/byebug/processors/command_processor.rb +57 -172
  68. data/lib/byebug/processors/control_command_processor.rb +16 -43
  69. data/lib/byebug/remote.rb +13 -7
  70. data/lib/byebug/runner.rb +102 -54
  71. data/lib/byebug/setting.rb +45 -68
  72. data/lib/byebug/settings/autoeval.rb +2 -0
  73. data/lib/byebug/settings/autoirb.rb +3 -0
  74. data/lib/byebug/settings/autolist.rb +3 -0
  75. data/lib/byebug/settings/autosave.rb +2 -0
  76. data/lib/byebug/settings/basename.rb +2 -0
  77. data/lib/byebug/settings/callstyle.rb +2 -0
  78. data/lib/byebug/settings/fullpath.rb +2 -0
  79. data/lib/byebug/settings/histfile.rb +2 -0
  80. data/lib/byebug/settings/histsize.rb +2 -0
  81. data/lib/byebug/settings/linetrace.rb +2 -0
  82. data/lib/byebug/settings/listsize.rb +2 -0
  83. data/lib/byebug/settings/post_mortem.rb +7 -2
  84. data/lib/byebug/settings/stack_on_error.rb +2 -0
  85. data/lib/byebug/settings/verbose.rb +2 -0
  86. data/lib/byebug/settings/width.rb +2 -0
  87. data/lib/byebug/state.rb +12 -0
  88. data/lib/byebug/states/control_state.rb +26 -0
  89. data/lib/byebug/states/regular_state.rb +178 -0
  90. data/lib/byebug/version.rb +1 -1
  91. metadata +24 -109
  92. data/lib/byebug/commands/catchpoint.rb +0 -53
  93. data/lib/byebug/commands/reload.rb +0 -29
  94. data/lib/byebug/commands/trace.rb +0 -50
  95. data/lib/byebug/commands/variables.rb +0 -206
  96. data/lib/byebug/options.rb +0 -46
  97. data/lib/byebug/settings/autoreload.rb +0 -12
  98. data/lib/byebug/settings/forcestep.rb +0 -14
  99. data/lib/byebug/settings/testing.rb +0 -12
  100. data/lib/byebug/settings/tracing_plus.rb +0 -11
  101. data/test/commands/break_test.rb +0 -364
  102. data/test/commands/condition_test.rb +0 -85
  103. data/test/commands/continue_test.rb +0 -47
  104. data/test/commands/delete_test.rb +0 -26
  105. data/test/commands/display_test.rb +0 -37
  106. data/test/commands/edit_test.rb +0 -52
  107. data/test/commands/eval_test.rb +0 -89
  108. data/test/commands/finish_test.rb +0 -74
  109. data/test/commands/frame_test.rb +0 -223
  110. data/test/commands/help_test.rb +0 -66
  111. data/test/commands/history_test.rb +0 -61
  112. data/test/commands/info_test.rb +0 -238
  113. data/test/commands/interrupt_test.rb +0 -45
  114. data/test/commands/irb_test.rb +0 -28
  115. data/test/commands/kill_test.rb +0 -50
  116. data/test/commands/list_test.rb +0 -174
  117. data/test/commands/method_test.rb +0 -52
  118. data/test/commands/post_mortem_test.rb +0 -71
  119. data/test/commands/pry_test.rb +0 -26
  120. data/test/commands/quit_test.rb +0 -53
  121. data/test/commands/reload_test.rb +0 -39
  122. data/test/commands/restart_test.rb +0 -46
  123. data/test/commands/save_test.rb +0 -67
  124. data/test/commands/set_test.rb +0 -140
  125. data/test/commands/show_test.rb +0 -76
  126. data/test/commands/source_test.rb +0 -46
  127. data/test/commands/stepping_test.rb +0 -192
  128. data/test/commands/thread_test.rb +0 -164
  129. data/test/commands/trace_test.rb +0 -71
  130. data/test/commands/undisplay_test.rb +0 -75
  131. data/test/commands/variables_test.rb +0 -105
  132. data/test/debugger_alias_test.rb +0 -7
  133. data/test/runner_test.rb +0 -150
  134. data/test/support/matchers.rb +0 -65
  135. data/test/support/test_interface.rb +0 -59
  136. data/test/support/utils.rb +0 -122
  137. data/test/test_helper.rb +0 -58
@@ -1,14 +1,19 @@
1
+ require 'byebug/command'
2
+
3
+ #
4
+ # TODO: Implement thread commands as a single command with subcommands, just
5
+ # like `info`, `var`, `enable` and `disable`. This allows consistent help
6
+ # format and we can go back to showing help for a single command in the `help`
7
+ # command.
8
+ #
1
9
  module Byebug
2
10
  #
3
11
  # Utilities to assist commands related to threads.
4
12
  #
5
13
  module ThreadFunctions
6
14
  def display_context(context, should_show_top_frame = true)
7
- args = thread_arguments(context, should_show_top_frame)
8
- interp = format("%s%s%d %s\t%s",
9
- args[:status_flag], args[:debug_flag], args[:id],
10
- args[:thread], args[:file_line])
11
- puts interp
15
+ puts pr('thread.context',
16
+ thread_arguments(context, should_show_top_frame))
12
17
  end
13
18
 
14
19
  def thread_arguments(context, should_show_top_frame = true)
@@ -18,50 +23,47 @@ module Byebug
18
23
  context.thread == Thread.current ? '+' : ' '
19
24
  end
20
25
  debug_flag = context.ignored? ? '!' : ' '
26
+
21
27
  if should_show_top_frame
22
- if context.thread == Thread.current && !context.dead?
23
- file = context.frame_file(0)
24
- line = context.frame_line(0)
28
+ if context == Byebug.current_context
29
+ file_line = "#{@state.file}:#{@state.line}"
25
30
  else
26
- if context.thread.backtrace_locations &&
27
- context.thread.backtrace_locations[0]
28
- file = context.thread.backtrace_locations[0].path
29
- line = context.thread.backtrace_locations[0].lineno
31
+ backtrace = context.thread.backtrace_locations
32
+ if backtrace && backtrace[0]
33
+ file_line = "#{backtrace[0].path}:#{backtrace[0].lineno}"
30
34
  end
31
35
  end
32
- file_line = "#{file}:#{line}"
33
36
  end
34
37
  {
35
38
  status_flag: status_flag,
36
39
  debug_flag: debug_flag,
37
40
  id: context.thnum,
38
41
  thread: context.thread.inspect,
39
- file_line: file_line ? file_line : ''
42
+ file_line: file_line || ''
40
43
  }
41
44
  end
42
45
 
43
46
  def parse_thread_num(subcmd, arg)
44
- return errmsg("\"#{subcmd}\" needs a thread number") if '' == arg
45
-
46
- thread_num, err = get_int(arg, subcmd, 1)
47
- return errmsg(err) unless thread_num
47
+ thnum, err = get_int(arg, subcmd, 1)
48
+ return [nil, err] unless thnum
48
49
 
49
50
  Byebug.contexts.find { |c| c.thnum == thnum }
50
51
  end
51
52
 
52
53
  def parse_thread_num_for_cmd(subcmd, arg)
53
- c = parse_thread_num(subcmd, arg)
54
- return unless c
54
+ c, err = parse_thread_num(subcmd, arg)
55
55
 
56
56
  case
57
- when nil == c
58
- errmsg 'No such thread'
57
+ when err
58
+ [c, err]
59
+ when c.nil?
60
+ [nil, pr('thread.errors.no_thread')]
59
61
  when @state.context == c
60
- errmsg "It's the current thread"
62
+ [c, pr('thread.errors.current_thread')]
61
63
  when c.ignored?
62
- errmsg "Can't #{subcmd} thread #{arg}"
64
+ [c, pr('thread.errors.wrong_action', subcmd: subcmd, arg: arg)]
63
65
  else
64
- c
66
+ [c, nil]
65
67
  end
66
68
  end
67
69
  end
@@ -70,6 +72,8 @@ module Byebug
70
72
  # List current threads.
71
73
  #
72
74
  class ThreadListCommand < Command
75
+ include ThreadFunctions
76
+
73
77
  self.allow_in_control = true
74
78
 
75
79
  def regexp
@@ -77,8 +81,13 @@ module Byebug
77
81
  end
78
82
 
79
83
  def execute
80
- Byebug.contexts.select { |c| Thread.list.include?(c.thread) }
81
- .sort_by(&:thnum).each { |c| display_context(c) }
84
+ contexts = Byebug.contexts.sort_by(&:thnum)
85
+
86
+ thread_list = prc('thread.context', contexts) do |context, _|
87
+ thread_arguments(context)
88
+ end
89
+
90
+ print(thread_list)
82
91
  end
83
92
 
84
93
  class << self
@@ -87,7 +96,9 @@ module Byebug
87
96
  end
88
97
 
89
98
  def description
90
- %(th[read] l[ist] Lists all threads.)
99
+ prettify <<-EOD
100
+ th[read] l[ist] Lists all threads.
101
+ EOD
91
102
  end
92
103
  end
93
104
  end
@@ -96,6 +107,8 @@ module Byebug
96
107
  # Show current thread.
97
108
  #
98
109
  class ThreadCurrentCommand < Command
110
+ include ThreadFunctions
111
+
99
112
  def regexp
100
113
  /^\s* th(?:read)? \s+ (?:cur(?:rent)?)? \s*$/x
101
114
  end
@@ -110,7 +123,9 @@ module Byebug
110
123
  end
111
124
 
112
125
  def description
113
- %(th[read] [cur[rent]] Shows current thread.)
126
+ prettify <<-EOD
127
+ th[read] cur[rent] Shows current thread.
128
+ EOD
114
129
  end
115
130
  end
116
131
  end
@@ -119,7 +134,9 @@ module Byebug
119
134
  # Stop execution of a thread.
120
135
  #
121
136
  class ThreadStopCommand < Command
122
- self.allow_in_control = true
137
+ include ThreadFunctions
138
+
139
+ self.allow_in_control = true
123
140
  self.allow_in_post_mortem = false
124
141
 
125
142
  def regexp
@@ -127,8 +144,8 @@ module Byebug
127
144
  end
128
145
 
129
146
  def execute
130
- c = parse_thread_num_for_cmd('thread stop', @match[1])
131
- return unless c
147
+ c, err = parse_thread_num_for_cmd('thread stop', @match[1])
148
+ return errmsg(err) if err
132
149
 
133
150
  c.suspend
134
151
  display_context(c)
@@ -140,7 +157,9 @@ module Byebug
140
157
  end
141
158
 
142
159
  def description
143
- %(th[read] stop <n> Stops thread <n>.)
160
+ prettify <<-EOD
161
+ th[read] stop <n> Stops thread <n>.
162
+ EOD
144
163
  end
145
164
  end
146
165
  end
@@ -149,7 +168,9 @@ module Byebug
149
168
  # Resume execution of a thread.
150
169
  #
151
170
  class ThreadResumeCommand < Command
152
- self.allow_in_control = true
171
+ include ThreadFunctions
172
+
173
+ self.allow_in_control = true
153
174
  self.allow_in_post_mortem = false
154
175
 
155
176
  def regexp
@@ -157,9 +178,10 @@ module Byebug
157
178
  end
158
179
 
159
180
  def execute
160
- c = parse_thread_num_for_cmd('thread resume', @match[1])
161
- return unless c
162
- return errmsg('Already running') unless c.suspended?
181
+ c, err = parse_thread_num_for_cmd('thread resume', @match[1])
182
+ return errmsg(err) if err
183
+ return errmsg pr('thread.errors.already_running') unless c.suspended?
184
+
163
185
  c.resume
164
186
  display_context(c)
165
187
  end
@@ -170,7 +192,9 @@ module Byebug
170
192
  end
171
193
 
172
194
  def description
173
- %(th[read] resume <n> Resumes thread <n>.)
195
+ prettify <<-EOD
196
+ th[read] resume <n> Resumes thread <n>.
197
+ EOD
174
198
  end
175
199
  end
176
200
  end
@@ -179,24 +203,22 @@ module Byebug
179
203
  # Switch execution to a different thread.
180
204
  #
181
205
  class ThreadSwitchCommand < Command
182
- self.allow_in_control = true
206
+ include ThreadFunctions
207
+
208
+ self.allow_in_control = true
183
209
  self.allow_in_post_mortem = false
184
210
 
185
211
  def regexp
186
- /^\s* th(?:read)? \s+ (?:sw(?:itch)?\s+)? (\S+) \s*$/x
212
+ /^\s* th(?:read)? \s+ sw(?:itch)? (?:\s+(\S+))? \s*$/x
187
213
  end
188
214
 
189
215
  def execute
190
- if @match[1] =~ /switch/
191
- return errmsg('"thread switch" needs a thread number')
192
- end
193
-
194
- c = parse_thread_num_for_cmd('thread switch', @match[1])
195
- return unless c
216
+ c, err = parse_thread_num_for_cmd('thread switch', @match[1])
217
+ return errmsg(err) if err
196
218
 
197
219
  display_context(c)
198
- c.step_into 1
199
- c.thread.run
220
+
221
+ c.switch
200
222
  @state.proceed
201
223
  end
202
224
 
@@ -206,7 +228,9 @@ module Byebug
206
228
  end
207
229
 
208
230
  def description
209
- %(th[read] [sw[itch]] <nnn> Switches thread context to <n>.)
231
+ prettify <<-EOD
232
+ th[read] sw[itch] <n> Switches thread context to <n>.
233
+ EOD
210
234
  end
211
235
  end
212
236
  end
@@ -0,0 +1,56 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Show (and possibily stop) at every line that changes a global variable.
6
+ #
7
+ class TracevarCommand < Command
8
+ self.allow_in_post_mortem = false
9
+
10
+ def regexp
11
+ /^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)?
12
+ (?: \s+ (stop|nostop))?
13
+ \s*$/x
14
+ end
15
+
16
+ def execute
17
+ var = @match[1]
18
+ return errmsg(pr('trace.errors.needs_global_variable')) unless var
19
+
20
+ unless global_variables.include?(:"#{var}")
21
+ return errmsg(pr('trace.errors.var_is_not_global', name: var))
22
+ end
23
+
24
+ stop = @match[2] && @match[2] !~ /nostop/
25
+
26
+ instance_eval do
27
+ trace_var(:"#{var}") { |val| on_change(var, val, stop) }
28
+ end
29
+
30
+ puts pr('trace.messages.success', var: var)
31
+ end
32
+
33
+ def on_change(name, value, stop)
34
+ puts pr('trace.messages.on_change', name: name, value: value)
35
+ byebug(1, false) if stop
36
+ end
37
+
38
+ class << self
39
+ def names
40
+ %w(tracevar)
41
+ end
42
+
43
+ def description
44
+ prettify <<-EOD
45
+ tr[acevar] <variable> [[no]stop]
46
+
47
+ Start tracing variable <variable>.
48
+
49
+ If "stop" is specified, execution will stop every time the variable
50
+ changes its value. If nothing or "nostop" is specified, execution
51
+ won't stop, changes will just be logged in byebug's output.
52
+ EOD
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,3 +1,5 @@
1
+ require 'byebug/command'
2
+
1
3
  module Byebug
2
4
  #
3
5
  # Remove expressions from display list.
@@ -15,12 +17,12 @@ module Byebug
15
17
  return errmsg(err) unless err.nil?
16
18
 
17
19
  unless @state.display[pos - 1]
18
- return errmsg("Display expression #{pos} is not defined.")
20
+ return errmsg(pr('display.errors.undefined', expr: pos))
19
21
  end
20
22
 
21
23
  @state.display[pos - 1][0] = nil
22
24
  else
23
- return unless confirm('Clear all expressions? (y/n) ')
25
+ return unless confirm(pr('display.confirmations.clear_all'))
24
26
 
25
27
  @state.display.each { |d| d[0] = false }
26
28
  end
@@ -32,13 +34,15 @@ module Byebug
32
34
  end
33
35
 
34
36
  def description
35
- %(undisp[lay][ nnn]
37
+ prettify <<-EOD
38
+ undisp[lay][ nnn]
36
39
 
37
40
  Cancel some expressions to be displayed when program stops. Arguments
38
41
  are the code numbers of the expressions to stop displaying. No
39
42
  argument means cancel all automatic-display expressions. "delete
40
43
  display" has the same effect as this command. Do "info display" to see
41
- the current list of code numbers.)
44
+ the current list of code numbers.
45
+ EOD
42
46
  end
43
47
  end
44
48
  end
@@ -0,0 +1,38 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Stop tracing a global variable.
6
+ #
7
+ class UntracevarCommand < Command
8
+ self.allow_in_post_mortem = false
9
+
10
+ def regexp
11
+ /^\s* untr(?:acevar)? (?:\s+ (\S+))? \s*$/x
12
+ end
13
+
14
+ def execute
15
+ var = @match[1]
16
+ if global_variables.include?(:"#{var}")
17
+ eval("untrace_var(:\"#{var}\")")
18
+ puts pr('trace.messages.undo', var: var)
19
+ else
20
+ errmsg pr('trace.errors.not_global', var: var)
21
+ end
22
+ end
23
+
24
+ class << self
25
+ def names
26
+ %w(untracevar)
27
+ end
28
+
29
+ def description
30
+ prettify <<-EOD
31
+ untr[acevar] <variable>
32
+
33
+ Stop tracing global variable <variable>.
34
+ EOD
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ require 'byebug/command'
2
+
3
+ module Byebug
4
+ #
5
+ # Utilities for the var command.
6
+ #
7
+ module VarFunctions
8
+ def var_list(ary, b = get_binding)
9
+ vars = ary.sort.map do |v|
10
+ s = begin
11
+ b.eval(v.to_s).inspect
12
+ rescue
13
+ begin
14
+ b.eval(v.to_s).to_s
15
+ rescue
16
+ '*Error in evaluation*'
17
+ end
18
+ end
19
+ [v, s]
20
+ end
21
+ puts prv(vars)
22
+ end
23
+
24
+ def var_global(_str = nil)
25
+ globals = global_variables.reject do |v|
26
+ [:$IGNORECASE, :$=, :$KCODE, :$-K, :$binding].include?(v)
27
+ end
28
+
29
+ var_list(globals)
30
+ end
31
+
32
+ def var_instance(str)
33
+ obj = bb_warning_eval(str || 'self')
34
+ var_list(obj.instance_variables, obj.instance_eval { binding })
35
+ end
36
+
37
+ def var_constant(str)
38
+ str ||= 'self.class'
39
+ obj = bb_warning_eval(str)
40
+ is_mod = obj.is_a?(Module)
41
+ return errmsg(pr('variable.errors.not_module', object: str)) unless is_mod
42
+
43
+ constants = bb_eval("#{str}.constants")
44
+ puts prv(constants.sort.map { |c| [c, obj.const_get(c)] })
45
+ end
46
+
47
+ def var_local(_str = nil)
48
+ _self = @state.context.frame_self(@state.frame)
49
+ locals = @state.context.frame_locals
50
+ puts prv(locals.keys.sort.map { |k| [k, locals[k]] })
51
+ end
52
+
53
+ def var_all(_str = nil)
54
+ var_global
55
+ var_instance('self')
56
+ var_local
57
+ end
58
+ end
59
+
60
+ #
61
+ # Show variables and its values.
62
+ #
63
+ class VarCommand < Command
64
+ include VarFunctions
65
+
66
+ Subcommands = [
67
+ ['constant', 2, 'Show constants of an object'],
68
+ ['global', 1, 'Show global variables'],
69
+ ['instance', 1, 'Show instance variables of self or a specific object'],
70
+ ['local', 1, 'Show local variables in current scope'],
71
+ ['all', 1, 'Shows local, global and instance variables of self']
72
+ ].map do |name, min, help|
73
+ Subcmd.new(name, min, help)
74
+ end
75
+
76
+ def regexp
77
+ /^\s* v(?:ar)? (?: \s+(\S+) (?:\s(\S+))? )? \s*$/x
78
+ end
79
+
80
+ def execute
81
+ return puts(self.class.help) unless @match[1]
82
+
83
+ subcmd = Command.find(Subcommands, @match[1])
84
+ return errmsg("Unknown var command #{@match[1]}\n") unless subcmd
85
+
86
+ if @state.context
87
+ send("var_#{subcmd.name}", @match[2])
88
+ else
89
+ errmsg "'var #{subcmd.name}' not available without a context.\n"
90
+ end
91
+ end
92
+
93
+ class << self
94
+ def names
95
+ %w(var)
96
+ end
97
+
98
+ def description
99
+ prettify <<-EOD
100
+ [v]ar
101
+
102
+ Show variables and its values.
103
+ EOD
104
+ end
105
+ end
106
+ end
107
+ end