passenger 5.0.0.beta3 → 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (218) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.editorconfig +11 -5
  5. data/CHANGELOG +38 -0
  6. data/CONTRIBUTING.md +1 -4
  7. data/Gemfile +0 -1
  8. data/Gemfile.lock +0 -2
  9. data/Rakefile +33 -33
  10. data/bin/passenger +1 -1
  11. data/bin/passenger-config +1 -1
  12. data/bin/passenger-install-apache2-module +800 -800
  13. data/bin/passenger-install-nginx-module +592 -592
  14. data/bin/passenger-memory-stats +127 -127
  15. data/bin/passenger-status +216 -216
  16. data/build/agents.rb +127 -127
  17. data/build/apache2.rb +87 -87
  18. data/build/basics.rb +60 -60
  19. data/build/common_library.rb +165 -165
  20. data/build/cplusplus_support.rb +51 -51
  21. data/build/cxx_tests.rb +268 -268
  22. data/build/debian.rb +143 -143
  23. data/build/documentation.rb +58 -58
  24. data/build/integration_tests.rb +81 -81
  25. data/build/misc.rb +132 -132
  26. data/build/nginx.rb +20 -20
  27. data/build/node_tests.rb +7 -7
  28. data/build/oxt_tests.rb +14 -14
  29. data/build/packaging.rb +570 -570
  30. data/build/preprocessor.rb +260 -260
  31. data/build/rake_extensions.rb +71 -71
  32. data/build/ruby_extension.rb +29 -29
  33. data/build/ruby_tests.rb +6 -6
  34. data/build/test_basics.rb +37 -37
  35. data/debian.template/control.template +3 -5
  36. data/dev/copy_boost_headers +134 -134
  37. data/dev/install_scripts_bootstrap_code.rb +25 -25
  38. data/dev/list_tests +20 -20
  39. data/dev/ruby_server.rb +223 -223
  40. data/dev/runner +18 -18
  41. data/doc/ServerOptimizationGuide.txt.md +55 -2
  42. data/doc/Users guide Nginx.txt +0 -26
  43. data/doc/Users guide Standalone.txt +5 -1
  44. data/doc/users_guide_snippets/tips.txt +9 -0
  45. data/ext/common/ApplicationPool2/Group.h +23 -11
  46. data/ext/common/ApplicationPool2/Implementation.cpp +32 -7
  47. data/ext/common/ApplicationPool2/Pool.h +22 -17
  48. data/ext/common/ApplicationPool2/SmartSpawner.h +4 -1
  49. data/ext/common/ApplicationPool2/Spawner.h +1 -1
  50. data/ext/common/Constants.h +1 -1
  51. data/ext/common/agents/Base.cpp +35 -20
  52. data/ext/common/agents/HelperAgent/Main.cpp +8 -1
  53. data/ext/common/agents/HelperAgent/OptionParser.h +18 -4
  54. data/ext/common/agents/HelperAgent/RequestHandler.h +2 -83
  55. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +54 -1
  56. data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +7 -4
  57. data/ext/common/agents/Main.cpp +1 -1
  58. data/ext/common/agents/Watchdog/Main.cpp +54 -19
  59. data/ext/nginx/Configuration.c +7 -0
  60. data/ext/nginx/ContentHandler.c +9 -1
  61. data/helper-scripts/backtrace-sanitizer.rb +106 -87
  62. data/helper-scripts/crash-watch.rb +32 -0
  63. data/helper-scripts/download_binaries/extconf.rb +38 -38
  64. data/helper-scripts/meteor-loader.rb +107 -107
  65. data/helper-scripts/prespawn +101 -101
  66. data/helper-scripts/rack-loader.rb +96 -96
  67. data/helper-scripts/rack-preloader.rb +137 -137
  68. data/lib/phusion_passenger.rb +292 -292
  69. data/lib/phusion_passenger/abstract_installer.rb +438 -438
  70. data/lib/phusion_passenger/active_support3_extensions/init.rb +168 -170
  71. data/lib/phusion_passenger/admin_tools.rb +20 -20
  72. data/lib/phusion_passenger/admin_tools/instance.rb +178 -178
  73. data/lib/phusion_passenger/admin_tools/instance_registry.rb +61 -61
  74. data/lib/phusion_passenger/admin_tools/memory_stats.rb +267 -267
  75. data/lib/phusion_passenger/apache2/config_options.rb +182 -182
  76. data/lib/phusion_passenger/common_library.rb +479 -485
  77. data/lib/phusion_passenger/config/about_command.rb +161 -161
  78. data/lib/phusion_passenger/config/admin_command_command.rb +129 -129
  79. data/lib/phusion_passenger/config/agent_compiler.rb +121 -121
  80. data/lib/phusion_passenger/config/build_native_support_command.rb +43 -43
  81. data/lib/phusion_passenger/config/command.rb +25 -25
  82. data/lib/phusion_passenger/config/compile_agent_command.rb +62 -62
  83. data/lib/phusion_passenger/config/compile_nginx_engine_command.rb +88 -73
  84. data/lib/phusion_passenger/config/detach_process_command.rb +72 -72
  85. data/lib/phusion_passenger/config/download_agent_command.rb +246 -227
  86. data/lib/phusion_passenger/config/download_nginx_engine_command.rb +245 -224
  87. data/lib/phusion_passenger/config/install_agent_command.rb +144 -132
  88. data/lib/phusion_passenger/config/install_standalone_runtime_command.rb +205 -185
  89. data/lib/phusion_passenger/config/installation_utils.rb +204 -204
  90. data/lib/phusion_passenger/config/list_instances_command.rb +64 -64
  91. data/lib/phusion_passenger/config/main.rb +152 -152
  92. data/lib/phusion_passenger/config/nginx_engine_compiler.rb +319 -300
  93. data/lib/phusion_passenger/config/reopen_logs_command.rb +67 -67
  94. data/lib/phusion_passenger/config/restart_app_command.rb +155 -155
  95. data/lib/phusion_passenger/config/system_metrics_command.rb +13 -13
  96. data/lib/phusion_passenger/config/utils.rb +95 -95
  97. data/lib/phusion_passenger/config/validate_install_command.rb +198 -198
  98. data/lib/phusion_passenger/console_text_template.rb +25 -25
  99. data/lib/phusion_passenger/constants.rb +90 -90
  100. data/lib/phusion_passenger/debug_logging.rb +106 -106
  101. data/lib/phusion_passenger/loader_shared_helpers.rb +447 -432
  102. data/lib/phusion_passenger/message_channel.rb +312 -312
  103. data/lib/phusion_passenger/message_client.rb +176 -176
  104. data/lib/phusion_passenger/native_support.rb +369 -369
  105. data/lib/phusion_passenger/nginx/config_options.rb +297 -297
  106. data/lib/phusion_passenger/packaging.rb +131 -131
  107. data/lib/phusion_passenger/platform_info.rb +360 -360
  108. data/lib/phusion_passenger/platform_info/apache.rb +767 -767
  109. data/lib/phusion_passenger/platform_info/apache_detector.rb +199 -199
  110. data/lib/phusion_passenger/platform_info/binary_compatibility.rb +107 -107
  111. data/lib/phusion_passenger/platform_info/compiler.rb +570 -570
  112. data/lib/phusion_passenger/platform_info/curl.rb +32 -32
  113. data/lib/phusion_passenger/platform_info/cxx_portability.rb +188 -188
  114. data/lib/phusion_passenger/platform_info/depcheck.rb +372 -372
  115. data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +109 -109
  116. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +4 -4
  117. data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +10 -34
  118. data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +101 -101
  119. data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +5 -5
  120. data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +13 -13
  121. data/lib/phusion_passenger/platform_info/linux.rb +55 -55
  122. data/lib/phusion_passenger/platform_info/operating_system.rb +149 -149
  123. data/lib/phusion_passenger/platform_info/ruby.rb +468 -448
  124. data/lib/phusion_passenger/platform_info/zlib.rb +9 -9
  125. data/lib/phusion_passenger/plugin.rb +66 -66
  126. data/lib/phusion_passenger/preloader_shared_helpers.rb +126 -126
  127. data/lib/phusion_passenger/public_api.rb +191 -191
  128. data/lib/phusion_passenger/rack/out_of_band_gc.rb +93 -94
  129. data/lib/phusion_passenger/rack/thread_handler_extension.rb +231 -227
  130. data/lib/phusion_passenger/request_handler.rb +567 -577
  131. data/lib/phusion_passenger/request_handler/thread_handler.rb +379 -381
  132. data/lib/phusion_passenger/ruby_core_enhancements.rb +86 -86
  133. data/lib/phusion_passenger/ruby_core_io_enhancements.rb +74 -74
  134. data/lib/phusion_passenger/simple_benchmarking.rb +25 -25
  135. data/lib/phusion_passenger/standalone/app_finder.rb +153 -150
  136. data/lib/phusion_passenger/standalone/command.rb +44 -40
  137. data/lib/phusion_passenger/standalone/config_utils.rb +53 -53
  138. data/lib/phusion_passenger/standalone/control_utils.rb +38 -59
  139. data/lib/phusion_passenger/standalone/main.rb +73 -73
  140. data/lib/phusion_passenger/standalone/start_command.rb +697 -685
  141. data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +193 -155
  142. data/lib/phusion_passenger/standalone/start_command/nginx_engine.rb +162 -133
  143. data/lib/phusion_passenger/standalone/status_command.rb +64 -64
  144. data/lib/phusion_passenger/standalone/stop_command.rb +72 -72
  145. data/lib/phusion_passenger/standalone/version_command.rb +9 -9
  146. data/lib/phusion_passenger/union_station/connection.rb +32 -32
  147. data/lib/phusion_passenger/union_station/core.rb +251 -251
  148. data/lib/phusion_passenger/union_station/transaction.rb +126 -126
  149. data/lib/phusion_passenger/utils.rb +199 -167
  150. data/lib/phusion_passenger/utils/ansi_colors.rb +128 -128
  151. data/lib/phusion_passenger/utils/download.rb +196 -196
  152. data/lib/phusion_passenger/utils/file_system_watcher.rb +158 -158
  153. data/lib/phusion_passenger/utils/hosts_file_parser.rb +101 -101
  154. data/lib/phusion_passenger/utils/lock.rb +31 -31
  155. data/lib/phusion_passenger/utils/native_support_utils.rb +31 -31
  156. data/lib/phusion_passenger/utils/progress_bar.rb +26 -26
  157. data/lib/phusion_passenger/utils/shellwords.rb +20 -20
  158. data/lib/phusion_passenger/utils/terminal_choice_menu.rb +206 -206
  159. data/lib/phusion_passenger/utils/unseekable_socket.rb +272 -272
  160. data/lib/phusion_passenger/vendor/crash_watch/app.rb +129 -0
  161. data/lib/phusion_passenger/vendor/crash_watch/gdb_controller.rb +341 -0
  162. data/lib/phusion_passenger/vendor/crash_watch/version.rb +24 -0
  163. data/lib/phusion_passenger/vendor/daemon_controller.rb +877 -0
  164. data/lib/phusion_passenger/vendor/daemon_controller/lock_file.rb +127 -0
  165. data/lib/phusion_passenger/vendor/daemon_controller/spawn.rb +26 -0
  166. data/lib/phusion_passenger/vendor/daemon_controller/version.rb +29 -0
  167. data/packaging/rpm/passenger_spec/passenger.spec.template +0 -1
  168. data/passenger.gemspec +0 -1
  169. data/resources/templates/config/nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems.txt.erb +27 -0
  170. data/resources/templates/standalone/config.erb +19 -15
  171. data/test/integration_tests/apache2_tests.rb +566 -566
  172. data/test/integration_tests/downloaded_binaries_tests.rb +126 -125
  173. data/test/integration_tests/native_packaging_spec.rb +296 -296
  174. data/test/integration_tests/nginx_tests.rb +393 -393
  175. data/test/integration_tests/shared/example_webapp_tests.rb +282 -280
  176. data/test/integration_tests/source_packaging_test.rb +138 -138
  177. data/test/integration_tests/spec_helper.rb +5 -5
  178. data/test/integration_tests/standalone_tests.rb +367 -367
  179. data/test/ruby/debug_logging_spec.rb +133 -133
  180. data/test/ruby/message_channel_spec.rb +186 -186
  181. data/test/ruby/rack/loader_spec.rb +28 -28
  182. data/test/ruby/rack/preloader_spec.rb +34 -34
  183. data/test/ruby/rails3.0/loader_spec.rb +12 -12
  184. data/test/ruby/rails3.0/preloader_spec.rb +18 -18
  185. data/test/ruby/rails3.1/loader_spec.rb +12 -12
  186. data/test/ruby/rails3.1/preloader_spec.rb +18 -18
  187. data/test/ruby/rails3.2/loader_spec.rb +12 -12
  188. data/test/ruby/rails3.2/preloader_spec.rb +18 -18
  189. data/test/ruby/rails4.0/loader_spec.rb +12 -12
  190. data/test/ruby/rails4.0/preloader_spec.rb +18 -18
  191. data/test/ruby/rails4.1/loader_spec.rb +12 -12
  192. data/test/ruby/rails4.1/preloader_spec.rb +18 -18
  193. data/test/ruby/request_handler_spec.rb +730 -730
  194. data/test/ruby/shared/loader_sharedspec.rb +224 -224
  195. data/test/ruby/shared/rails/union_station_extensions_sharedspec.rb +327 -327
  196. data/test/ruby/shared/ruby_loader_sharedspec.rb +47 -47
  197. data/test/ruby/spec_helper.rb +65 -65
  198. data/test/ruby/standalone/runtime_installer_spec.rb +384 -384
  199. data/test/ruby/union_station_spec.rb +276 -276
  200. data/test/ruby/utils/file_system_watcher_spec.rb +220 -220
  201. data/test/ruby/utils/hosts_file_parser.rb +248 -248
  202. data/test/ruby/utils/tee_input_spec.rb +215 -215
  203. data/test/ruby/utils/unseekable_socket_spec.rb +57 -57
  204. data/test/ruby/utils_spec.rb +21 -21
  205. data/test/stub/rack/config.ru +87 -87
  206. data/test/stub/rack/library.rb +8 -8
  207. data/test/stub/rack/start.rb +30 -30
  208. data/test/support/apache2_controller.rb +191 -191
  209. data/test/support/nginx_controller.rb +90 -99
  210. data/test/support/placebo-preloader.rb +57 -57
  211. data/test/support/test_helper.rb +435 -435
  212. metadata +11 -21
  213. metadata.gz.asc +7 -7
  214. data/lib/phusion_passenger/standalone/command2.rb +0 -292
  215. data/lib/phusion_passenger/standalone/start2_command.rb +0 -799
  216. data/resources/templates/standalone/download_tool_missing.txt.erb +0 -18
  217. data/resources/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -17
  218. data/resources/templates/standalone/run_installer_as_root.txt.erb +0 -8
@@ -0,0 +1,129 @@
1
+ # encoding: binary
2
+ #
3
+ # Copyright (c) 2010-2015 Phusion
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ require 'optparse'
25
+ PhusionPassenger.require_passenger_lib 'vendor/crash_watch/gdb_controller'
26
+ PhusionPassenger.require_passenger_lib 'vendor/crash_watch/version'
27
+
28
+ module CrashWatch
29
+ class App
30
+ def run(argv)
31
+ options = {}
32
+ parser = OptionParser.new do |opts|
33
+ opts.banner = "Usage: crash-watch [options] PID"
34
+ opts.separator ""
35
+
36
+ opts.separator "Options:"
37
+ opts.on("-d", "--debug", "Show GDB commands that crash-watch sends.") do
38
+ options[:debug] = true
39
+ end
40
+ opts.on("--dump", "Dump current process backtrace and exit immediately.") do
41
+ options[:dump] = true
42
+ end
43
+ opts.on("-v", "--version", "Show version number.") do
44
+ options[:version] = true
45
+ end
46
+ opts.on("-h", "--help", "Show this help message.") do
47
+ options[:help] = true
48
+ end
49
+ end
50
+ begin
51
+ parser.parse!(argv)
52
+ rescue OptionParser::ParseError => e
53
+ puts e
54
+ puts
55
+ puts "Please see '--help' for valid options."
56
+ exit 1
57
+ end
58
+
59
+ if options[:help]
60
+ puts parser
61
+ exit
62
+ elsif options[:version]
63
+ puts "crash-watch version #{CrashWatch::VERSION_STRING}"
64
+ exit
65
+ elsif argv.size != 1
66
+ puts parser
67
+ exit 1
68
+ end
69
+
70
+ begin
71
+ gdb = CrashWatch::GdbController.new
72
+ rescue CrashWatch::Error => e
73
+ abort e.message
74
+ end
75
+ begin
76
+ gdb.debug = options[:debug]
77
+
78
+ # Ruby sometimes uses SIGVTARLM for thread scheduling.
79
+ gdb.execute("handle SIGVTALRM noprint pass")
80
+
81
+ if gdb.attach(argv[0])
82
+ if options[:dump]
83
+ puts "*******************************************************"
84
+ puts "*"
85
+ puts "* Current thread (#{gdb.current_thread}) backtrace"
86
+ puts "*"
87
+ puts "*******************************************************"
88
+ puts
89
+ puts " " << gdb.current_thread_backtrace.gsub(/\n/, "\n ")
90
+ puts
91
+ puts
92
+ puts "*******************************************************"
93
+ puts "*"
94
+ puts "* All thread backtraces"
95
+ puts "*"
96
+ puts "*******************************************************"
97
+ puts
98
+ output = gdb.all_threads_backtraces
99
+ output.gsub!(/\n/, "\n ")
100
+ output.insert(0, " ")
101
+ output.gsub!(/^ (Thread .*):$/, "########### \\1 ###########\n")
102
+ puts output
103
+ else
104
+ puts "Monitoring PID #{argv[0]}..."
105
+ exit_info = gdb.wait_until_exit
106
+ puts "Process exited at #{Time.now}."
107
+ puts "Exit code: #{exit_info.exit_code}" if exit_info.exit_code
108
+ puts "Signal: #{exit_info.signal}" if exit_info.signaled?
109
+ if exit_info.backtrace
110
+ puts "Backtrace:"
111
+ puts " " << exit_info.backtrace.gsub(/\n/, "\n ")
112
+ end
113
+ end
114
+ else
115
+ puts "ERROR: Cannot attach to process."
116
+ if File.exist?("/proc/sys/kernel/yama/ptrace_scope")
117
+ puts
118
+ puts "This may be the result of kernel ptrace() hardening. Try disabling it with:"
119
+ puts " sudo sh -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'"
120
+ puts "See http://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process for more information."
121
+ end
122
+ exit 2
123
+ end
124
+ ensure
125
+ gdb.close
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,341 @@
1
+ # encoding: binary
2
+ #
3
+ # Copyright (c) 2010-2015 Phusion
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ require 'rbconfig'
25
+
26
+ module CrashWatch
27
+ class Error < StandardError
28
+ end
29
+
30
+ class GdbNotFound < Error
31
+ end
32
+
33
+ class GdbBroken < Error
34
+ end
35
+
36
+ class GdbController
37
+ class ExitInfo
38
+ attr_reader :exit_code, :signal, :backtrace, :snapshot
39
+
40
+ def initialize(exit_code, signal, backtrace, snapshot)
41
+ @exit_code = exit_code
42
+ @signal = signal
43
+ @backtrace = backtrace
44
+ @snapshot = snapshot
45
+ end
46
+
47
+ def signaled?
48
+ !!@signal
49
+ end
50
+ end
51
+
52
+ END_OF_RESPONSE_MARKER = '--------END_OF_RESPONSE--------'
53
+
54
+ attr_accessor :debug
55
+
56
+ def initialize
57
+ @pid, @in, @out = popen_command(find_gdb, "-n", "-q")
58
+ execute("set prompt ")
59
+ end
60
+
61
+ def execute(command_string, timeout = nil)
62
+ raise "GDB session is already closed" if !@pid
63
+ puts "gdb write #{command_string.inspect}" if @debug
64
+ @in.puts(command_string)
65
+ @in.puts("echo \\n#{END_OF_RESPONSE_MARKER}\\n")
66
+ done = false
67
+ result = ""
68
+ while !done
69
+ begin
70
+ if select([@out], nil, nil, timeout)
71
+ line = @out.readline
72
+ puts "gdb read #{line.inspect}" if @debug
73
+ if line == "#{END_OF_RESPONSE_MARKER}\n"
74
+ done = true
75
+ else
76
+ result << line
77
+ end
78
+ else
79
+ close!
80
+ done = true
81
+ result = nil
82
+ end
83
+ rescue EOFError
84
+ done = true
85
+ end
86
+ end
87
+ result
88
+ end
89
+
90
+ def closed?
91
+ !@pid
92
+ end
93
+
94
+ def close
95
+ if !closed?
96
+ begin
97
+ execute("detach", 5)
98
+ execute("quit", 5) if !closed?
99
+ rescue Errno::EPIPE
100
+ end
101
+ if !closed?
102
+ @in.close
103
+ @out.close
104
+ Process.waitpid(@pid)
105
+ @pid = nil
106
+ end
107
+ end
108
+ end
109
+
110
+ def close!
111
+ if !closed?
112
+ @in.close
113
+ @out.close
114
+ Process.kill('KILL', @pid)
115
+ Process.waitpid(@pid)
116
+ @pid = nil
117
+ end
118
+ end
119
+
120
+ def attach(pid)
121
+ pid = pid.to_s.strip
122
+ raise ArgumentError if pid.empty?
123
+ result = execute("attach #{pid}")
124
+ result !~ /(No such process|Unable to access task|Operation not permitted)/
125
+ end
126
+
127
+ def call(code)
128
+ result = execute("call #{code}")
129
+ result =~ /= (.*)$/
130
+ $1
131
+ end
132
+
133
+ def program_counter
134
+ execute("p/x $pc").gsub(/.* = /, '')
135
+ end
136
+
137
+ def current_thread
138
+ execute("thread") =~ /Current thread is (.+?) /
139
+ $1
140
+ end
141
+
142
+ def current_thread_backtrace
143
+ execute("bt full").strip
144
+ end
145
+
146
+ def all_threads_backtraces
147
+ execute("thread apply all bt full").strip
148
+ end
149
+
150
+ def ruby_backtrace
151
+ filename = "/tmp/gdb-capture.#{@pid}.txt"
152
+
153
+ orig_stdout_fd_copy = call("(int) dup(1)")
154
+ new_stdout = call(%Q{(void *) fopen("#{filename}", "w")})
155
+ new_stdout_fd = call("(int) fileno(#{new_stdout})")
156
+ call("(int) dup2(#{new_stdout_fd}, 1)")
157
+
158
+ # Let's hope stdout is set to line buffered or unbuffered mode...
159
+ call("(void) rb_backtrace()")
160
+
161
+ call("(int) dup2(#{orig_stdout_fd_copy}, 1)")
162
+ call("(int) fclose(#{new_stdout})")
163
+ call("(int) close(#{orig_stdout_fd_copy})")
164
+
165
+ if File.exist?(filename)
166
+ result = File.read(filename)
167
+ result.strip!
168
+ if result.empty?
169
+ nil
170
+ else
171
+ result
172
+ end
173
+ else
174
+ nil
175
+ end
176
+ ensure
177
+ if filename
178
+ File.unlink(filename) rescue nil
179
+ end
180
+ end
181
+
182
+ def wait_until_exit
183
+ execute("break _exit")
184
+
185
+ signal = nil
186
+ backtraces = nil
187
+ snapshot = nil
188
+
189
+ while true
190
+ result = execute("continue")
191
+ if result =~ /^Program received signal (.+?),/
192
+ signal = $1
193
+ backtraces = execute("thread apply all bt full").strip
194
+ if backtraces.empty?
195
+ backtraces = execute("bt full").strip
196
+ end
197
+ snapshot = yield(self) if block_given?
198
+
199
+ # This signal may or may not be immediately fatal; the
200
+ # signal might be ignored by the process, or the process
201
+ # has some clever signal handler that fixes the state,
202
+ # or maybe the signal handler must run some cleanup code
203
+ # before killing the process. Let's find out by running
204
+ # the next machine instruction.
205
+ old_program_counter = program_counter
206
+ result = execute("stepi")
207
+ if result =~ /^Program received signal .+?,/
208
+ # Yes, it was fatal. Here we don't care whether the
209
+ # instruction caused a different signal. The last
210
+ # one is probably what we're interested in.
211
+ return ExitInfo.new(nil, signal, backtraces, snapshot)
212
+ elsif result =~ /^Program (terminated|exited)/ || result =~ /^Breakpoint .*? _exit/
213
+ # Running the next instruction causes the program to terminate.
214
+ # Not sure what's going on but the previous signal and
215
+ # backtrace is probably what we're interested in.
216
+ return ExitInfo.new(nil, signal, backtraces, snapshot)
217
+ elsif old_program_counter == program_counter
218
+ # The process cannot continue but we're not sure what GDB
219
+ # is telling us.
220
+ raise "Unexpected GDB output: #{result}"
221
+ end
222
+ # else:
223
+ # The signal doesn't isn't immediately fatal, so save current
224
+ # status, continue, and check whether the process exits later.
225
+ elsif result =~ /^Program terminated with signal (.+?),/
226
+ if $1 == signal
227
+ # Looks like the signal we trapped earlier
228
+ # caused an exit.
229
+ return ExitInfo.new(nil, signal, backtraces, snapshot)
230
+ else
231
+ return ExitInfo.new(nil, signal, nil, snapshot)
232
+ end
233
+ elsif result =~ /^Breakpoint .*? _exit /
234
+ backtraces = execute("thread apply all bt full").strip
235
+ if backtraces.empty?
236
+ backtraces = execute("bt full").strip
237
+ end
238
+ snapshot = yield(self) if block_given?
239
+ # On OS X, gdb may fail to return from the 'continue' command
240
+ # even though the process exited. Kernel bug? In any case,
241
+ # we put a timeout here so that we don't wait indefinitely.
242
+ result = execute("continue", 10)
243
+ if result =~ /^Program exited with code (\d+)\.$/
244
+ return ExitInfo.new($1.to_i, nil, backtraces, snapshot)
245
+ elsif result =~ /^Program exited normally/
246
+ return ExitInfo.new(0, nil, backtraces, snapshot)
247
+ else
248
+ return ExitInfo.new(nil, nil, backtraces, snapshot)
249
+ end
250
+ elsif result =~ /^Program exited with code (\d+)\.$/
251
+ return ExitInfo.new($1.to_i, nil, nil, nil)
252
+ elsif result =~ /^Program exited normally/
253
+ return ExitInfo.new(0, nil, nil, nil)
254
+ else
255
+ return ExitInfo.new(nil, nil, nil, nil)
256
+ end
257
+ end
258
+ end
259
+
260
+ private
261
+ def popen_command(*command)
262
+ a, b = IO.pipe
263
+ c, d = IO.pipe
264
+ if Process.respond_to?(:spawn)
265
+ args = command.dup
266
+ args << {
267
+ STDIN => a,
268
+ STDOUT => d,
269
+ STDERR => d,
270
+ :close_others => true
271
+ }
272
+ pid = Process.spawn(*args)
273
+ else
274
+ pid = fork do
275
+ STDIN.reopen(a)
276
+ STDOUT.reopen(d)
277
+ STDERR.reopen(d)
278
+ b.close
279
+ c.close
280
+ exec(*command)
281
+ end
282
+ end
283
+ a.close
284
+ d.close
285
+ b.binmode
286
+ c.binmode
287
+ [pid, b, c]
288
+ end
289
+
290
+ def find_gdb
291
+ result = nil
292
+ if ENV['GDB'] && File.executable?(ENV['GDB'])
293
+ result = ENV['GDB']
294
+ else
295
+ ENV['PATH'].to_s.split(/:+/).each do |path|
296
+ filename = "#{path}/gdb"
297
+ if File.file?(filename) && File.executable?(filename)
298
+ result = filename
299
+ break
300
+ end
301
+ end
302
+ end
303
+
304
+ puts "Found gdb at: #{result}" if result
305
+
306
+ config = defined?(RbConfig) ? RbConfig::CONFIG : Config::CONFIG
307
+ if config['target_os'] =~ /freebsd/ && result == "/usr/bin/gdb"
308
+ # /usr/bin/gdb on FreeBSD is broken:
309
+ # https://github.com/FooBarWidget/crash-watch/issues/1
310
+ # Look for a newer one that's installed from ports.
311
+ puts "#{result} is broken on FreeBSD. Looking for an alternative..."
312
+ result = nil
313
+ ["/usr/local/bin/gdb76", "/usr/local/bin/gdb66"].each do |candidate|
314
+ if File.executable?(candidate)
315
+ result = candidate
316
+ break
317
+ end
318
+ end
319
+
320
+ if result.nil?
321
+ raise GdbBroken,
322
+ "*** ERROR ***: '/usr/bin/gdb' is broken on FreeBSD. " +
323
+ "Please install the one from the devel/gdb port instead. " +
324
+ "If you want to use another gdb"
325
+ else
326
+ puts "Found gdb at: #{result}" if result
327
+ result
328
+ end
329
+ elsif result.nil?
330
+ raise GdbNotFound,
331
+ "*** ERROR ***: 'gdb' isn't installed. Please install it first.\n" +
332
+ " Debian/Ubuntu: sudo apt-get install gdb\n" +
333
+ "RedHat/CentOS/Fedora: sudo yum install gdb\n" +
334
+ " Mac OS X: please install the Developer Tools or XCode\n" +
335
+ " FreeBSD: use the devel/gdb port\n"
336
+ else
337
+ result
338
+ end
339
+ end
340
+ end
341
+ end