Project

General

Profile

Actions

Bug #10010

closed

Error in TestEnv#test_memory_leak_* on Solaris

Added by ngoto (Naohisa Goto) almost 10 years ago. Updated almost 10 years ago.

Status:
Closed
Target version:
ruby -v:
ruby 2.2.0dev (2014-07-04) [sparc64-solaris2.10]
[ruby-dev:48370]

Description

r46550 くらい以降、Solaris 10 にて make test-all 中、以下のエラーが発生します。
(r44686 にて確認)

test/envutil.rb の 438行目にて、:size や :rss 用の情報が、OS側の何らかの理由で示されず、その結果ハッシュにも格納されず、 a または b が nil になることがあるのに、それに対応していないのが原因と思います。

なお、Solaris では、/proc/self/status は struct pstatus_t の内容のバイナリを返しますが、それには全く対応していないので、nil になるか、偶然バイナリ値が何かに一致した場合にでたらめな値が返されるかのいずれかになります。

 53) Error:
TestEnv#test_memory_leak_shift:
NoMethodError: undefined method `>' for nil:NilClass
    /XXXXX/test/ruby/envutil.rb:438:in `block in assert_no_memory_leak'
    /XXXXX/test/ruby/envutil.rb:435:in `each'
    /XXXXX/test/ruby/envutil.rb:435:in `assert_no_memory_leak'
    /XXXXX/test/ruby/test_env.rb:543:in `test_memory_leak_shift'

 54) Error:
TestEnv#test_memory_leak_select:
NoMethodError: undefined method `>' for nil:NilClass
    /XXXXX/test/ruby/envutil.rb:438:in `block in assert_no_memory_leak'
    /XXXXX/test/ruby/envutil.rb:435:in `each'
    /XXXXX/test/ruby/envutil.rb:435:in `assert_no_memory_leak'
    /XXXXX/test/ruby/test_env.rb:525:in `test_memory_leak_select'

 55) Error:
TestEnv#test_memory_leak_aset:
NoMethodError: undefined method `>' for nil:NilClass
    /XXXXX/test/ruby/envutil.rb:438:in `block in assert_no_memory_leak'
    /XXXXX/test/ruby/envutil.rb:435:in `each'
    /XXXXX/test/ruby/envutil.rb:435:in `assert_no_memory_leak'
    /XXXXX/test/ruby/test_env.rb:514:in `test_memory_leak_aset'


Related issues 1 (0 open1 closed)

Related to Ruby master - Bug #10020: TestEnv#test_memory_leak_*, Fiddle::TestPointer#test_no_memory_leak, and Test_StringModifyExpand#test_modify_expand_memory_leak on SolarisClosed07/09/2014Actions

Updated by ngoto (Naohisa Goto) almost 10 years ago

コードをよく読んだら原因の推測が間違っていたので訂正します。

なお、Solaris では、/proc/self/status は struct pstatus_t の内容のバイナリを返しますが、それには全く対応していないので、nil になるか、偶然バイナリ値が何かに一致した場合にでたらめな値が返されるかのいずれかになります。

は明確に間違いで、ps を利用して、それなりの値を返すようにはなっていました。

しかし、test_env.rb 内のテストコードで ENV.clear を呼んでいるため、
環境変数 PATH もクリアされてしまい、
memory_status.rb 内で IO.popen を使って ps を呼ぶ際に、
システム既定またはどこかにハードコードされた謎の PATH が使われ、
その結果、私の環境では /usr/ucb/ps が呼ばれ、
/usr/ucb/ps は POSIX の ps を想定したオプションを受け付けないため、
期待した結果を返さず、:size や :rss が nil になっていました。

memory_status.rb 内の

    PSCMD = ["ps", "-ovsz=","-orss=", "-p"]

の "ps" を絶対パスを使用するように変更すれば解決すると思いますが、マイナー環境対応を壊さないよう実現するコードはすぐには思いつきませんでした。

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago

  • Status changed from Open to Feedback

これでどうでしょう。
見つからなかったときにどうするか悩ましいですが。

diff --git a/test/ruby/memory_status.rb b/test/ruby/memory_status.rb
index f8e097a..ef973ee 100644
--- a/test/ruby/memory_status.rb
+++ b/test/ruby/memory_status.rb
@@ -82,7 +82,17 @@ module Memory
       end
     end
   else
-    PSCMD = ["ps", "-ovsz=","-orss=", "-p"]
+    ps = ENV["PATH"].split(File::PATH_SEPARATOR).find {|path|
+      next if path.empty?
+      ps = File.join(path, "ps")
+      begin
+        st = File.stat(ps)
+      rescue
+      else
+        break ps if st.file? and st.executable?
+      end
+    }
+    PSCMD = [ps, "-ovsz=","-orss=", "-p"]
     PAT = /^\s*(\d+)\s+(\d+)$/
 
     keys << :size << :rss

Updated by ngoto (Naohisa Goto) almost 10 years ago

TestEnv#test_memory_leak_* では、子プロセス内のコードで ENV.clear を呼んでいて、
上記コードもその子プロセス内で呼ばれることになるため、つまり、
上記コードを通る際には ENV[PATH]は nil になった状態です。
このため、ENV["PATH"].split の部分でエラーになると思います。

以下のようにPATHを保存して最後に復旧するようにしたら、Errorは出なくなりました。(Failureにはなりますが、OSのメモリ管理の方針によると思われます。)

Index: test/ruby/test_env.rb
===================================================================
--- test/ruby/test_env.rb	(revision 46727)
+++ test/ruby/test_env.rb	(working copy)
@@ -512,22 +512,26 @@
   def test_memory_leak_aset
     bug9977 = '[ruby-dev:48323] [Bug #9977]'
     assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9977, limit: 2.0)
+      path = ENV['PATH']
       ENV.clear
       k = 'FOO'
       v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
       doit = proc {ENV[k] = v}
       500.times(&doit)
+      ENV['PATH'] = path
     end;
   end
 
   def test_memory_leak_select
     bug9978 = '[ruby-dev:48325] [Bug #9978]'
     assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9978, limit: 2.0)
+      path = ENV['PATH']
       ENV.clear
       k = 'FOO'
       (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
       doit = proc {ENV.select {break}}
       500.times(&doit)
+      ENV['PATH'] = path
     end;
   end
 
@@ -541,11 +545,13 @@
   def test_memory_leak_shift
     bug9983 = '[ruby-dev:48332] [Bug #9983]'
     assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9983, limit: 2.0)
+      path = ENV['PATH']
       ENV.clear
       k = 'FOO'
       v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
       doit = proc {ENV[k] = v; ENV.shift}
       500.times(&doit)
+      ENV['PATH'] = path
     end;
   end
 end

これでも、Solarisで /usr/ucb を /usr/bin や /bin より先に PATH に入れていた場合は救えません。

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago

Naohisa Goto wrote:

TestEnv#test_memory_leak_* では、子プロセス内のコードで ENV.clear を呼んでいて、
上記コードもその子プロセス内で呼ばれることになるため、つまり、
上記コードを通る際には ENV[PATH]は nil になった状態です。
このため、ENV["PATH"].split の部分でエラーになると思います。

定数の初期化はロードされてモジュール定義のときですから、まだテストでPATHが変更される前です。

これでも、Solarisで /usr/ucb を /usr/bin や /bin より先に PATH に入れていた場合は救えません。

使えるpsを探すようにしてみました。

diff --git a/test/ruby/memory_status.rb b/test/ruby/memory_status.rb
index f8e097a..9bdaa73 100644
--- a/test/ruby/memory_status.rb
+++ b/test/ruby/memory_status.rb
@@ -82,8 +82,17 @@ module Memory
       end
     end
   else
-    PSCMD = ["ps", "-ovsz=","-orss=", "-p"]
     PAT = /^\s*(\d+)\s+(\d+)$/
+    PSCMD =  ENV["PATH"].split(File::PATH_SEPARATOR).find {|path|
+      next if path.empty?
+      pscmd = [File.join(path, "ps"), "-ovsz=", "-orss=", "-p"]
+      begin
+        break pscmd if PAT =~ IO.popen(pscmd + [$$.to_s], "r", err: [:child, :out], &:read)
+      rescue
+        next
+      end
+    }
+    raise MiniTest::Skip, "ps command not found" unless PSCMD
 
     keys << :size << :rss
     def self.read_status

Updated by ngoto (Naohisa Goto) almost 10 years ago

定数の初期化はロードされてモジュール定義のときですから、まだテストでPATHが変更される前です。

確かにそのとおりでした、すみません。

パッチを当てて、PATH=/usr/ucb:$PATH などとした状態でも Error が出なくなるのは確認しました。

Errorのかわりに、下記の2つのFailureに変わりましたが、本件とは別問題として調べてみます。

  1) Failure:
TestEnv#test_memory_leak_aset [/XXXXX/test/ruby/test_env.rb:514]:
[ruby-dev:48323] [Bug #9977].
size: 21692416 => 97189888..
Expected 4.480362537764351 to be < 2.0.

  2) Failure:
TestEnv#test_memory_leak_shift [/XXXXX/test/ruby/test_env.rb:543]:
[ruby-dev:48332] [Bug #9983].
size: 38469632 => 155910144..
Expected 4.0528109028960815 to be < 2.0.

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago

  • Status changed from Feedback to Closed
  • % Done changed from 0 to 100

Applied in changeset r46755.


memory_status.rb: find proper ps

  • test/ruby/memory_status.rb (Memory::PSCMD): use ps command which
    outputs expected result. [ruby-dev:48370] [Bug #10010]

Updated by ngoto (Naohisa Goto) almost 10 years ago

以下のように test/ruby/test_env.rb を実行すると、なぜか、
uninitialized constant EnvUtil::RbConfig (NameError) になります。
実行に使ったrubyはr46751で、svnレポジトリのローカルコピーr46756 の test/ruby/test_env.rb を相対パス指定にて直接実行した結果のものです。

 $ ruby test/ruby/test_env.rb 
Run options: 

# Running tests:

[22/49] TestEnv#test_memory_leak_aset = 0.15 s           
  1) Failure:
TestEnv#test_memory_leak_aset [test/ruby/test_env.rb:514]:
[ruby-dev:48323] [Bug #9977].
<[true, ""]> expected but was
<[false,
 "/XXXXX/test/ruby/find_executable.rb:3:in `find_executable': uninitialized constant EnvUtil::RbConfig (NameError)\n\tfrom /XXXXX/test/ruby/memory_status.rb:87:in `<module:Memory>'\n\tfrom /XXXXX/test/ruby/memory_status.rb:1:in `<top (required)>'\n\tfrom -:1:in `require'\n"]>.

[23/49] TestEnv#test_memory_leak_select = 0.06 s      
  2) Failure:
TestEnv#test_memory_leak_select [test/ruby/test_env.rb:525]:
[ruby-dev:48325] [Bug #9978].
<[true, ""]> expected but was
<[false,
 "/XXXXX/test/ruby/find_executable.rb:3:in `find_executable': uninitialized constant EnvUtil::RbConfig (NameError)\n\tfrom /XXXXX/test/ruby/memory_status.rb:87:in `<module:Memory>'\n\tfrom /XXXXX/test/ruby/memory_status.rb:1:in `<top (required)>'\n\tfrom -:1:in `require'\n"]>.

[24/49] TestEnv#test_memory_leak_shift = 0.06 s         
  3) Failure:
TestEnv#test_memory_leak_shift [test/ruby/test_env.rb:543]:
[ruby-dev:48332] [Bug #9983].
<[true, ""]> expected but was
<[false,
 "/XXXXX/test/ruby/find_executable.rb:3:in `find_executable': uninitialized constant EnvUtil::RbConfig (NameError)\n\tfrom /XXXXX/test/ruby/memory_status.rb:87:in `<module:Memory>'\n\tfrom /XXXXX/test/ruby/memory_status.rb:1:in `<top (required)>'\n\tfrom -:1:in `require'\n"]>.

Finished tests in 0.608052s, 80.5852 tests/s, 596.9886 assertions/s.
49 tests, 363 assertions, 3 failures, 0 errors, 0 skips

ruby -v: ruby 2.2.0dev (2014-07-07) [sparc64-solaris2.10]

Updated by ngoto (Naohisa Goto) almost 10 years ago

  • Status changed from Closed to Open

r46756 の make test-all にて、同じ Failure がすべての test/ruby/memory_status.rb 関連の場所で出たので再オープンします。

invoke_ruby によって --diable-gems を付けて呼んだ子プロセスの ruby 内で実行される envutil.rb にて、 require 'rbconfig' より前に find_executable が実行されるため、エラーになっているように見えます。(親プロセスでは通常 rubygems が有効のため、require 'rbconfig' が最初からされた状態なので、顕在化しなかった。)

現在(r26299 以降)、envutil.rb の最後のほうで require 'rbconfig' していますが、これをもっと前に持ってくれば解決しそうな気はします。

Updated by ngoto (Naohisa Goto) almost 10 years ago

  • Status changed from Open to Closed

r46762 fix this issue

Actions #10

Updated by ngoto (Naohisa Goto) almost 10 years ago

  • Related to Bug #10020: TestEnv#test_memory_leak_*, Fiddle::TestPointer#test_no_memory_leak, and Test_StringModifyExpand#test_modify_expand_memory_leak on Solaris added
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0