passenger 1.0.1 → 1.0.2

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 (123) hide show
  1. data/LICENSE +3 -1
  2. data/Rakefile +17 -12
  3. data/bin/passenger-config +22 -0
  4. data/bin/passenger-install-apache2-module +2 -31
  5. data/bin/passenger-make-enterprisey +66 -0
  6. data/bin/passenger-memory-stats +192 -0
  7. data/doc/Users guide.html +150 -66
  8. data/doc/Users guide.txt +141 -51
  9. data/doc/cxxapi/ApplicationPoolClientServer_8h-source.html +1 -1
  10. data/doc/cxxapi/ApplicationPoolServer_8h-source.html +545 -0
  11. data/doc/cxxapi/ApplicationPool_8h-source.html +1 -1
  12. data/doc/cxxapi/Application_8h-source.html +1 -1
  13. data/doc/cxxapi/Configuration_8h-source.html +2 -2
  14. data/doc/cxxapi/DummySpawnManager_8h-source.html +1 -1
  15. data/doc/cxxapi/Exceptions_8h-source.html +1 -1
  16. data/doc/cxxapi/Hooks_8h-source.html +1 -1
  17. data/doc/cxxapi/Logging_8h-source.html +1 -1
  18. data/doc/cxxapi/MessageChannel_8h-source.html +210 -205
  19. data/doc/cxxapi/SpawnManager_8h-source.html +1 -1
  20. data/doc/cxxapi/StandardApplicationPool_8h-source.html +1 -1
  21. data/doc/cxxapi/Utils_8h-source.html +1 -1
  22. data/doc/cxxapi/annotated.html +1 -1
  23. data/doc/cxxapi/classClient-members.html +30 -0
  24. data/doc/cxxapi/classClient.html +112 -0
  25. data/doc/cxxapi/classPassenger_1_1Application-members.html +1 -1
  26. data/doc/cxxapi/classPassenger_1_1Application.html +1 -1
  27. data/doc/cxxapi/classPassenger_1_1ApplicationPool-members.html +1 -1
  28. data/doc/cxxapi/classPassenger_1_1ApplicationPool.html +1 -1
  29. data/doc/cxxapi/classPassenger_1_1ApplicationPoolServer-members.html +1 -1
  30. data/doc/cxxapi/classPassenger_1_1ApplicationPoolServer.html +1 -1
  31. data/doc/cxxapi/classPassenger_1_1Application_1_1Session-members.html +1 -1
  32. data/doc/cxxapi/classPassenger_1_1Application_1_1Session.html +1 -1
  33. data/doc/cxxapi/classPassenger_1_1ConfigurationException-members.html +28 -0
  34. data/doc/cxxapi/classPassenger_1_1ConfigurationException.html +41 -0
  35. data/doc/cxxapi/classPassenger_1_1DummySpawnManager-members.html +1 -1
  36. data/doc/cxxapi/classPassenger_1_1DummySpawnManager.html +1 -1
  37. data/doc/cxxapi/classPassenger_1_1FileNotFoundException-members.html +1 -1
  38. data/doc/cxxapi/classPassenger_1_1FileNotFoundException.html +1 -1
  39. data/doc/cxxapi/classPassenger_1_1IOException-members.html +1 -1
  40. data/doc/cxxapi/classPassenger_1_1IOException.html +1 -1
  41. data/doc/cxxapi/classPassenger_1_1MessageChannel-members.html +1 -1
  42. data/doc/cxxapi/classPassenger_1_1MessageChannel.html +1 -1
  43. data/doc/cxxapi/classPassenger_1_1SpawnException-members.html +1 -1
  44. data/doc/cxxapi/classPassenger_1_1SpawnException.html +1 -1
  45. data/doc/cxxapi/classPassenger_1_1SpawnManager-members.html +1 -1
  46. data/doc/cxxapi/classPassenger_1_1SpawnManager.html +1 -1
  47. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool-members.html +1 -1
  48. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool.html +1 -1
  49. data/doc/cxxapi/classPassenger_1_1SystemException-members.html +1 -1
  50. data/doc/cxxapi/classPassenger_1_1SystemException.html +1 -1
  51. data/doc/cxxapi/definitions_8h-source.html +1 -1
  52. data/doc/cxxapi/files.html +1 -1
  53. data/doc/cxxapi/functions.html +1 -1
  54. data/doc/cxxapi/functions_func.html +1 -1
  55. data/doc/cxxapi/functions_type.html +1 -1
  56. data/doc/cxxapi/graph_legend.html +1 -1
  57. data/doc/cxxapi/graph_legend.png +0 -0
  58. data/doc/cxxapi/group__Configuration.html +3 -3
  59. data/doc/cxxapi/group__Configuration.png +0 -0
  60. data/doc/cxxapi/group__Core.html +1 -1
  61. data/doc/cxxapi/group__Core.png +0 -0
  62. data/doc/cxxapi/group__Exceptions.html +1 -1
  63. data/doc/cxxapi/group__Hooks.html +1 -1
  64. data/doc/cxxapi/group__Hooks.png +0 -0
  65. data/doc/cxxapi/group__Support.html +1 -1
  66. data/doc/cxxapi/hierarchy.html +1 -1
  67. data/doc/cxxapi/inherit__graph__0.png +0 -0
  68. data/doc/cxxapi/inherit__graph__1.png +0 -0
  69. data/doc/cxxapi/inherit__graph__10.map +1 -0
  70. data/doc/cxxapi/inherit__graph__10.md5 +1 -0
  71. data/doc/cxxapi/inherit__graph__10.png +0 -0
  72. data/doc/cxxapi/inherit__graph__2.png +0 -0
  73. data/doc/cxxapi/inherit__graph__3.png +0 -0
  74. data/doc/cxxapi/inherit__graph__4.png +0 -0
  75. data/doc/cxxapi/inherit__graph__5.png +0 -0
  76. data/doc/cxxapi/inherit__graph__6.png +0 -0
  77. data/doc/cxxapi/inherit__graph__7.png +0 -0
  78. data/doc/cxxapi/inherit__graph__8.png +0 -0
  79. data/doc/cxxapi/inherit__graph__9.png +0 -0
  80. data/doc/cxxapi/inherits.html +1 -1
  81. data/doc/cxxapi/main.html +1 -1
  82. data/doc/cxxapi/modules.html +1 -1
  83. data/doc/cxxapi/structPassenger_1_1AnythingToString-members.html +1 -1
  84. data/doc/cxxapi/structPassenger_1_1AnythingToString.html +1 -1
  85. data/doc/cxxapi/structPassenger_1_1AnythingToString_3_01vector_3_01string_01_4_01_4-members.html +1 -1
  86. data/doc/cxxapi/structPassenger_1_1AnythingToString_3_01vector_3_01string_01_4_01_4.html +1 -1
  87. data/doc/rdoc/classes/Passenger/RequestHandler.html +84 -74
  88. data/doc/rdoc/classes/Passenger/SpawnManager.html +54 -49
  89. data/doc/rdoc/classes/PlatformInfo.html +25 -12
  90. data/doc/rdoc/created.rid +1 -1
  91. data/doc/rdoc/files/DEVELOPERS_TXT.html +1 -1
  92. data/doc/rdoc/files/ext/passenger/native_support_c.html +1 -1
  93. data/doc/rdoc/files/lib/passenger/abstract_server_rb.html +1 -1
  94. data/doc/rdoc/files/lib/passenger/application_spawner_rb.html +1 -1
  95. data/doc/rdoc/files/lib/passenger/dependencies_rb.html +1 -1
  96. data/doc/rdoc/files/lib/passenger/framework_spawner_rb.html +1 -1
  97. data/doc/rdoc/files/lib/passenger/platform_info_rb.html +1 -1
  98. data/doc/rdoc/files/lib/passenger/request_handler_rb.html +1 -1
  99. data/doc/rdoc/files/lib/passenger/spawn_manager_rb.html +1 -1
  100. data/ext/apache2/Configuration.h +1 -1
  101. data/ext/apache2/Hooks.cpp +68 -16
  102. data/ext/apache2/Logging.cpp +1 -1
  103. data/ext/apache2/MessageChannel.h +48 -43
  104. data/ext/passenger/native_support.c +46 -43
  105. data/lib/passenger/application_spawner.rb +37 -4
  106. data/lib/passenger/dependencies.rb +5 -1
  107. data/lib/passenger/framework_spawner.rb +2 -0
  108. data/lib/passenger/platform_info.rb +32 -17
  109. data/lib/passenger/request_handler.rb +10 -1
  110. data/lib/passenger/spawn_manager.rb +14 -8
  111. data/lib/passenger/templates/error_layout.html.erb +2 -2
  112. data/test/CxxTestMain.cpp +1 -0
  113. data/test/MessageChannelTest.cpp +3 -1
  114. data/test/application_spawner_spec.rb +2 -0
  115. data/test/framework_spawner_spec.rb +2 -0
  116. data/test/integration_tests.rb +1 -0
  117. data/test/spawn_manager_spec.rb +2 -0
  118. data/test/spawner_privilege_lowering_spec.rb +1 -0
  119. data/test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_controller.rb +4 -0
  120. data/test/stub/minimal-railsapp/vendor/rails/activerecord/lib/active_record.rb +7 -0
  121. data/test/stub/rails_apps/foobar/config/environments/test.rb +22 -0
  122. metadata +28 -22
  123. data/lib/passenger/templates/osx_broken_apache_warning.txt.erb +0 -23
data/LICENSE CHANGED
@@ -7,7 +7,9 @@ of the application's own licensing terms. The application will not be bound to
7
7
  the terms of the GPL in any way. That is, the GPL only applies to Passenger
8
8
  itself, and not to applications that are run through Passenger.
9
9
 
10
- We also explicitly allow Apache to load the Passenger Apache module.
10
+ We also explicitly allow Apache to load the Passenger Apache module, without
11
+ Apache or any other loaded modules needing to be released under the terms of
12
+ the GPL.
11
13
 
12
14
  ----------------------------------------------------------------------
13
15
 
data/Rakefile CHANGED
@@ -27,13 +27,13 @@ require 'passenger/platform_info'
27
27
  ##### Configuration
28
28
 
29
29
  # Don't forget to edit Configuration.h too
30
- PACKAGE_VERSION = "1.0.1"
30
+ PACKAGE_VERSION = "1.0.2"
31
31
 
32
32
  include PlatformInfo
33
33
  APXS2.nil? and raise "Could not find 'apxs' or 'apxs2'."
34
34
  APACHE2CTL.nil? and raise "Could not find 'apachectl' or 'apache2ctl'."
35
35
  HTTPD.nil? and raise "Could not find the Apache web server binary."
36
- APR1_FLAGS.nil? and raise "Could not find Apache Portable Runtime (APR)."
36
+ APR_FLAGS.nil? and raise "Could not find Apache Portable Runtime (APR)."
37
37
 
38
38
  CXX = "g++"
39
39
  THREADING_FLAGS = "-D_REENTRANT"
@@ -101,7 +101,7 @@ end
101
101
  ##### Apache module
102
102
 
103
103
  class APACHE2
104
- CXXFLAGS = CXXFLAGS + " -fPIC -g -DPASSENGER_DEBUG #{APR1_FLAGS} #{APXS2_FLAGS} -I.."
104
+ CXXFLAGS = "-I.. -fPIC -g -DPASSENGER_DEBUG #{APR_FLAGS} #{APXS2_FLAGS} #{CXXFLAGS}"
105
105
  OBJECTS = {
106
106
  'Configuration.o' => %w(Configuration.cpp Configuration.h),
107
107
  'Hooks.o' => %w(Hooks.cpp Hooks.h
@@ -128,7 +128,7 @@ subdir 'ext/apache2' do
128
128
  #
129
129
  # Oh, and libtool sucks too. Do we even need it anymore in 2008?
130
130
  linkflags = "#{LDFLAGS} #{MULTI_ARCH_FLAGS}"
131
- linkflags << " -lstdc++ -lpthread ../boost/src/libboost_thread.a #{APR1_LIBS}"
131
+ linkflags << " -lstdc++ -lpthread ../boost/src/libboost_thread.a #{APR_LIBS}"
132
132
  create_shared_library 'mod_passenger.so',
133
133
  APACHE2::OBJECTS.keys.join(' ') << ' mod_passenger.o',
134
134
  linkflags
@@ -179,7 +179,7 @@ end
179
179
 
180
180
  class TEST
181
181
  CXXFLAGS = ::CXXFLAGS + " -Isupport -DTESTING_SPAWN_MANAGER -DTESTING_APPLICATION_POOL "
182
- AP2_FLAGS = "-I../ext/apache2 -I../ext #{APR1_FLAGS}"
182
+ AP2_FLAGS = "-I../ext/apache2 -I../ext #{APR_FLAGS}"
183
183
 
184
184
  AP2_OBJECTS = {
185
185
  'CxxTestMain.o' => %w(CxxTestMain.cpp),
@@ -239,12 +239,13 @@ subdir 'test' do
239
239
  " ../ext/apache2/Utils.o" <<
240
240
  " ../ext/apache2/Logging.o"
241
241
  create_executable "Apache2ModuleTests", objects,
242
- "#{LDFLAGS} #{APR1_LIBS} ../ext/boost/src/libboost_thread.a -lpthread"
242
+ "#{LDFLAGS} #{APR_LIBS} #{MULTI_ARCH_FLAGS} " <<
243
+ "../ext/boost/src/libboost_thread.a -lpthread"
243
244
  end
244
245
 
245
246
  TEST::AP2_OBJECTS.each_pair do |target, sources|
246
247
  file target => sources do
247
- compile_cxx sources[0], TEST::CXXFLAGS + " " + TEST::AP2_FLAGS
248
+ compile_cxx sources[0], "#{TEST::AP2_FLAGS} #{TEST::CXXFLAGS}"
248
249
  end
249
250
  end
250
251
 
@@ -272,7 +273,7 @@ subdir 'benchmark' do
272
273
  file 'DummyRequestHandler' => ['DummyRequestHandler.cpp',
273
274
  '../ext/apache2/MessageChannel.h'] do
274
275
  create_executable "DummyRequestHandler", "DummyRequestHandler.cpp",
275
- "#{CXXFLAGS} -I../ext -I../ext/apache2 #{LDFLAGS}"
276
+ "-I../ext -I../ext/apache2 #{CXXFLAGS} #{LDFLAGS}"
276
277
  end
277
278
 
278
279
  task :clean do
@@ -333,7 +334,7 @@ end
333
334
 
334
335
  spec = Gem::Specification.new do |s|
335
336
  s.platform = Gem::Platform::RUBY
336
- s.homepage = "http://passenger.phusion.nl/"
337
+ s.homepage = "http://www.modrails.com/"
337
338
  s.summary = "Apache module for Ruby on Rails support."
338
339
  s.name = "passenger"
339
340
  s.version = PACKAGE_VERSION
@@ -344,8 +345,6 @@ spec = Gem::Specification.new do |s|
344
345
  s.require_path = "lib"
345
346
  s.add_dependency 'rake', '>= 0.8.1'
346
347
  s.add_dependency 'fastthread', '>= 1.0.1'
347
- s.add_dependency 'rspec', '>= 1.1.2'
348
- s.add_dependency 'rails', '>= 1.2.0'
349
348
  s.extensions << 'ext/passenger/extconf.rb'
350
349
  s.files = FileList[
351
350
  'Rakefile',
@@ -380,7 +379,13 @@ spec = Gem::Specification.new do |s|
380
379
  ] - Dir['test/stub/*/log/*'] \
381
380
  - Dir['test/stub/*/tmp/*/*'] \
382
381
  - Dir['test/stub/apache2/*.{pid,lock,log}']
383
- s.executables = ['passenger-spawn-server', 'passenger-install-apache2-module']
382
+ s.executables = [
383
+ 'passenger-spawn-server',
384
+ 'passenger-install-apache2-module',
385
+ 'passenger-config',
386
+ 'passenger-memory-stats',
387
+ 'passenger-make-enterprisey'
388
+ ]
384
389
  s.has_rdoc = true
385
390
  s.extra_rdoc_files = ['README']
386
391
  s.rdoc_options <<
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ PASSENGER_ROOT = File.expand_path(File.dirname(__FILE__) << "/..")
3
+
4
+ def help
5
+ puts "Tool for showing Passenger configuration information."
6
+ puts "Usage: passenger-config <OPTIONS>"
7
+ puts
8
+ puts "Options:"
9
+ puts " --root Show Passenger's root directory."
10
+ puts " --version Show Passenger's version number."
11
+ end
12
+
13
+ case ARGV[0]
14
+ when "--root"
15
+ puts PASSENGER_ROOT
16
+ when "--version"
17
+ File.read("#{PASSENGER_ROOT}/Rakefile") =~ /^PACKAGE_VERSION = "(.*)"/
18
+ puts $1
19
+ else
20
+ help
21
+ exit 1
22
+ end
@@ -27,10 +27,11 @@ ENV["PATH"] += ":/usr/sbin:/sbin:/usr/local/sbin"
27
27
  require 'passenger/platform_info'
28
28
  require 'passenger/dependencies'
29
29
  require 'passenger/console_text_template'
30
- include Passenger
31
30
  include PlatformInfo
32
31
 
33
32
  class Installer
33
+ include Passenger
34
+
34
35
  PASSENGER_WEBSITE = "http://www.modrails.com/"
35
36
  PHUSION_WEBSITE = "www.phusion.nl"
36
37
  USERS_GUIDE = "#{PASSENGER_ROOT}/doc/Users guide.html"
@@ -51,10 +52,8 @@ class Installer
51
52
  Dir.chdir(PASSENGER_ROOT)
52
53
  show_welcome_screen
53
54
  check_dependencies || exit(1)
54
- warn_about_broken_apache_on_osx
55
55
  check_whether_apache_uses_prefork_mpm
56
56
  check_write_permission_to_passenger_root || exit(1)
57
- check_write_permission_to_apache_modules_dir || exit(1)
58
57
  if install_apache2_module
59
58
  show_apache2_config_snippets
60
59
  show_deployment_example
@@ -121,13 +120,6 @@ private
121
120
  end
122
121
  end
123
122
 
124
- def warn_about_broken_apache_on_osx
125
- return unless RUBY_PLATFORM =~ /darwin/ && APXS2 == "/usr/sbin/apxs"
126
- line
127
- render_template 'osx_broken_apache_warning'
128
- wait
129
- end
130
-
131
123
  def check_whether_apache_uses_prefork_mpm
132
124
  line
133
125
  # 'httpd -V' output is in the form of:
@@ -171,27 +163,6 @@ private
171
163
  File.unlink("__test__.txt") rescue nil
172
164
  end
173
165
 
174
- def check_write_permission_to_apache_modules_dir
175
- install_dir = `#{APXS2} -q LIBEXECDIR`.strip
176
- begin
177
- File.new("#{install_dir}/__test__.txt", "w").close
178
- return true
179
- rescue
180
- puts
181
- line
182
- if Process.uid == 0
183
- render_template 'no_write_permission_to_passenger_root',
184
- :path => install_dir
185
- else
186
- render_template 'run_installer_as_root',
187
- :path => install_dir
188
- end
189
- return false
190
- ensure
191
- File.unlink("#{install_dir}/__test__.txt") rescue nil
192
- end
193
- end
194
-
195
166
  def install_apache2_module
196
167
  puts
197
168
  line
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ # Phusion Passenger - http://www.modrails.com/
3
+ # Copyright (C) 2008 Phusion
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; version 2 of the License.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License along
15
+ # with this program; if not, write to the Free Software Foundation, Inc.,
16
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
+
18
+ PASSENGER_ROOT = File.expand_path(File.dirname(__FILE__) << "/..")
19
+ require 'digest/md5'
20
+
21
+ ##############################################################################
22
+ #
23
+ # Hidden license
24
+ #
25
+ # By reading the source code of this file, you're automatically agreeing
26
+ # with the following conditions:
27
+ #
28
+ # 1. You will sell your soul to us for $0.
29
+ # 2. You will watch the movie "Hot Fuzz".
30
+ #
31
+ # [ Allow ] or [ Deny ]
32
+ #
33
+ ##############################################################################
34
+
35
+
36
+
37
+
38
+ trap("INT") { exit 1 }
39
+ if File.exist?("#{PASSENGER_ROOT}/enterprisey.txt")
40
+ puts "Congratulations, your Passenger Enterprise License has already been activated!"
41
+ else
42
+ puts %{
43
+ Phusion Genuine Advantage
44
+ -------------------------
45
+ Welcome to the Phusion Genuine Advantage (PGA) program. This program will help
46
+ you with activating your Passenger Enterprise License.
47
+
48
+ Please enter your Enterprise License Key:
49
+ }.gsub(/^\t\t/, '').strip
50
+ done = false
51
+ while !done
52
+ key = STDIN.readline.strip
53
+ if key == Digest::MD5.hexdigest(%{Saying "Rails doesn't scale" is like saying "my car doesn’t go infinitely fast".})
54
+ done = true
55
+ else
56
+ STDERR.puts "Invalid key given. Please try again:"
57
+ end
58
+ end
59
+ if system("touch", "#{PASSENGER_ROOT}/enterprisey.txt")
60
+ puts "Congratulations! Your Passenger Enterprise License has been activated!"
61
+ puts "Please restart Apache to take full advantage of your Enterprise License."
62
+ else
63
+ STDERR.puts "Could not write to the Passenger folder. Please run this tool as root."
64
+ exit 1
65
+ end
66
+ end
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib")
3
+ require 'passenger/platform_info'
4
+
5
+ # Container for tabular data.
6
+ class Table
7
+ def initialize(column_names)
8
+ @column_names = column_names
9
+ @rows = []
10
+ end
11
+
12
+ def add_row(values)
13
+ @rows << values.to_a
14
+ end
15
+
16
+ def add_rows(list_of_rows)
17
+ list_of_rows.each do |row|
18
+ add_row(row)
19
+ end
20
+ end
21
+
22
+ def remove_column(name)
23
+ i = @column_names.index(name)
24
+ @column_names.delete_at(i)
25
+ @rows.each do |row|
26
+ row.delete_at(i)
27
+ end
28
+ end
29
+
30
+ def to_s(title = nil)
31
+ max_column_widths = [1] * @column_names.size
32
+ (@rows + [@column_names]).each do |row|
33
+ row.each_with_index do |value, i|
34
+ max_column_widths[i] = [value.to_s.size, max_column_widths[i]].max
35
+ end
36
+ end
37
+
38
+ format_string = max_column_widths.map{ |i| "%-#{i}s" }.join(" ")
39
+ header = sprintf(format_string, *@column_names).rstrip << "\n"
40
+ if title
41
+ free_space = header.size - title.size - 2
42
+ if free_space <= 0
43
+ left_bar_size = 3
44
+ right_bar_size = 3
45
+ else
46
+ left_bar_size = free_space / 2
47
+ right_bar_size = free_space - left_bar_size
48
+ end
49
+ result = "#{"-" * left_bar_size} #{title} #{"-" * right_bar_size}\n"
50
+ result << header
51
+ else
52
+ result = header.dup
53
+ end
54
+ result << ("-" * header.size) << "\n"
55
+ @rows.each do |row|
56
+ result << sprintf(format_string, *row).rstrip << "\n"
57
+ end
58
+ result
59
+ end
60
+ end
61
+
62
+ class MemoryStats
63
+ class Process
64
+ attr_accessor :pid
65
+ attr_accessor :ppid
66
+ attr_accessor :threads
67
+ attr_accessor :vm_size # in KB
68
+ attr_accessor :name
69
+ attr_accessor :private_dirty_rss # in KB
70
+
71
+ def vm_size_in_mb
72
+ return sprintf("%.1f MB", vm_size / 1024.0)
73
+ end
74
+
75
+ def private_dirty_rss_in_mb
76
+ if private_dirty_rss.is_a?(Numeric)
77
+ return sprintf("%.1f MB", private_dirty_rss / 1024.0)
78
+ else
79
+ return "?"
80
+ end
81
+ end
82
+
83
+ def to_a
84
+ return [pid, ppid, threads, vm_size_in_mb, private_dirty_rss_in_mb, name]
85
+ end
86
+ end
87
+
88
+ def start
89
+ apache_processes = list_processes(:exe => PlatformInfo::HTTPD)
90
+ print_process_list("Apache processes", apache_processes)
91
+
92
+ puts
93
+ passenger_processes = list_processes(:match => /(^Passenger |^Rails:|ApplicationPoolServerExecutable)/)
94
+ print_process_list("Passenger processes", passenger_processes, :show_ppid => false)
95
+
96
+ if RUBY_PLATFORM !~ /linux/
97
+ puts
98
+ puts "*** WARNING: The private dirty RSS can only be displayed " <<
99
+ "on Linux. You're currently using '#{RUBY_PLATFORM}'."
100
+ elsif ::Process.uid != 0 && (apache_processes + passenger_processes).any?{ |p| p.private_dirty_rss.nil? }
101
+ puts
102
+ puts "*** WARNING: Please run this tool as root. Otherwise the " <<
103
+ "private dirty RSS of processes cannot be determined."
104
+ end
105
+ end
106
+
107
+ # Returns a list of Process objects that match the given search criteria.
108
+ #
109
+ # # Search by executable path.
110
+ # list_processes(:exe => '/usr/sbin/apache2')
111
+ #
112
+ # # Search by executable name.
113
+ # list_processes(:name => 'ruby1.8')
114
+ #
115
+ # # Search by process name.
116
+ # list_processes(:match => 'Passenger FrameworkSpawner')
117
+ def list_processes(options)
118
+ if options[:exe]
119
+ name = options[:exe].sub(/.*\/(.*)/, '\1')
120
+ ps = "ps -C '#{name}'"
121
+ elsif options[:name]
122
+ ps = "ps -C '#{options[:name]}'"
123
+ elsif options[:match]
124
+ ps = "ps -A"
125
+ else
126
+ raise ArgumentError, "Invalid options."
127
+ end
128
+
129
+ processes = []
130
+ list = `#{ps} -o pid,ppid,nlwp,vsz,command`.split("\n")
131
+ list.shift
132
+ list.each do |line|
133
+ line.gsub!(/^ */, '')
134
+ line.gsub!(/ *$/, '')
135
+
136
+ p = Process.new
137
+ p.pid, p.ppid, p.threads, p.vm_size, p.name = line.split(/ +/, 5)
138
+ if p.name !~ /^ps/ && (!options[:match] || p.name.match(options[:match]))
139
+ [:pid, :ppid, :threads, :vm_size].each do |attr|
140
+ p.send("#{attr}=", p.send(attr).to_i)
141
+ end
142
+ p.private_dirty_rss = determine_private_dirty_rss(p.pid)
143
+ processes << p
144
+ end
145
+ end
146
+ return processes
147
+ end
148
+
149
+ private
150
+ # Returns the private dirty RSS for the given process, in KB.
151
+ def determine_private_dirty_rss(pid)
152
+ total = 0
153
+ File.read("/proc/#{pid}/smaps").split("\n").each do |line|
154
+ line =~ /^(Private)_Dirty: +(\d+)/
155
+ if $2
156
+ total += $2.to_i
157
+ end
158
+ end
159
+ return total
160
+ rescue Errno::EACCES, Errno::ENOENT
161
+ return nil
162
+ end
163
+
164
+ def print_process_list(title, processes, options = {})
165
+ table = Table.new(%w{PID PPID Threads VMSize Private Name})
166
+ table.add_rows(processes)
167
+ if options.has_key?(:show_ppid) && !options[:show_ppid]
168
+ table.remove_column('PPID')
169
+ end
170
+ puts table.to_s(title)
171
+
172
+ total_private_dirty_rss = 0
173
+ some_private_dirty_rss_cannot_be_determined = false
174
+ processes.each do |p|
175
+ if p.private_dirty_rss.is_a?(Numeric)
176
+ total_private_dirty_rss += p.private_dirty_rss
177
+ else
178
+ some_private_dirty_rss_cannot_be_determined = true
179
+ end
180
+ end
181
+ puts "### Processes: #{processes.size}"
182
+ printf "### Total private dirty RSS: %.2f MB", total_private_dirty_rss / 1024.0
183
+ if some_private_dirty_rss_cannot_be_determined
184
+ puts " (?)"
185
+ else
186
+ puts
187
+ end
188
+ end
189
+ end
190
+
191
+ MemoryStats.new.start
192
+