PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Mogstored/ChildProcess/IOStat.pm

https://github.com/frett/MogileFS-Server
Perl | 157 lines | 121 code | 22 blank | 14 comment | 16 complexity | 380fd830d07ef0cea6fce1086de811aa MD5 | raw file
  1. package Mogstored::ChildProcess::IOStat;
  2. use strict;
  3. use base 'Mogstored::ChildProcess';
  4. my $docroot;
  5. my $iostat_cmd = $ENV{MOG_IOSTAT_CMD} || "iostat -dx 1 30";
  6. if ($^O =~ /darwin/) { $iostat_cmd =~ s/x// }
  7. sub pre_exec_init {
  8. my $class = shift;
  9. close STDIN;
  10. close STDOUT;
  11. close STDERR;
  12. my $iostat_pipe_w = Mogstored->get_iostat_writer_pipe;
  13. # We may not be able to see errors beyond this point
  14. open STDIN, '<', '/dev/null' or die "Couldn't open STDIN for reading from /dev/null";
  15. open STDOUT, '>&', $iostat_pipe_w or die "Couldn't dup pipe for use as STDOUT";
  16. open STDERR, '>', '/dev/null' or die "Couldn't open STDOUT for writing to /dev/null";
  17. $ENV{MOG_DOCROOT} = Perlbal->service('mogstored')->{docroot};
  18. }
  19. sub run {
  20. $docroot = $ENV{MOG_DOCROOT};
  21. die "\$ENV{MOG_DOCROOT} not set" unless $docroot;
  22. die "\$ENV{MOG_DOCROOT} not set to a directory" unless -d $docroot;
  23. # (runs in exec'd child process)
  24. $0 = "mogstored [iostat]";
  25. select((select(STDOUT), $|++)[0]);
  26. my $iostat_pid;
  27. $SIG{TERM} = $SIG{INT} = sub {
  28. kill 9, $iostat_pid if $iostat_pid;
  29. exit(0);
  30. };
  31. my $check_for_parent = sub {
  32. # shut ourselves down if our parent mogstored
  33. # has gone away.
  34. my $ppid = getppid();
  35. unless ($ppid && kill(0,$ppid)) {
  36. kill 9, $iostat_pid if $iostat_pid;
  37. exit(0);
  38. }
  39. };
  40. my $get_iostat_fh = sub {
  41. while (1) {
  42. if ($iostat_pid = open (my $fh, "$iostat_cmd|")) {
  43. return $fh;
  44. }
  45. # TODO: try and find other paths to iostat
  46. $check_for_parent->();
  47. warn "Failed to open iostat: $!\n"; # this will just go to /dev/null, but will be straceable
  48. sleep 10;
  49. }
  50. };
  51. while (1) {
  52. my $iofh = $get_iostat_fh->();
  53. my $mog_sysid = mog_sysid_map(); # 5 (mogdevid) -> 2340 (os devid)
  54. my $dev_sysid = {}; # hashref, populated lazily: { /dev/sdg => system dev_t }
  55. my %devt_util; # dev_t => 52.55
  56. my $stats = 0;
  57. while (<$iofh>) {
  58. if (m/^\s*(\S+)\s.*?([\d.]+)\s*$/) {
  59. my ($devnode, $util) = ("/dev/$1", $2);
  60. unless (exists $dev_sysid->{$devnode}) {
  61. $dev_sysid->{$devnode} = (stat($devnode))[6]; # rdev
  62. }
  63. my $devt = $dev_sysid->{$devnode};
  64. $devt_util{$devt} = $util;
  65. $stats++;
  66. } elsif ($stats) {
  67. # blank line is the end, or any other line we don't understand
  68. # if we have stats, we print them, otherwise do nothing
  69. $stats = 0;
  70. my $ret = "";
  71. foreach my $mogdevid (sort { $a <=> $b } keys %$mog_sysid) {
  72. my $devt = $mog_sysid->{$mogdevid};
  73. my $ut = defined $devt_util{$devt} ? $devt_util{$devt} : "-";
  74. $ret .= "$mogdevid\t$ut\n";
  75. }
  76. $ret .= ".\n";
  77. print $ret;
  78. $check_for_parent->();
  79. %devt_util = ();
  80. }
  81. }
  82. }
  83. }
  84. # returns hashref of { 5 => dev_t device } # mog_devid -> os_devid
  85. sub mog_sysid_map {
  86. my $path = $docroot;
  87. $path =~ s!/$!!;
  88. # find all devices below us
  89. my @devnum; # integer ids
  90. opendir(my $d, $path) or die "Failed to open docroot: $path: $!";
  91. @devnum = map { /^dev(\d+)$/ ? $1 : () } readdir($d);
  92. my $map = {};
  93. foreach my $mogdevid (@devnum) {
  94. my ($osdevid) = (stat("$path/dev$mogdevid"))[0];
  95. $map->{$mogdevid} = $osdevid;
  96. }
  97. if (lc($^O) eq 'linux') {
  98. # name_to_number and number_to_name are the data derived from /proc/partitions
  99. my %name_to_number; # ( hda1 => 769, ... )
  100. my %number_to_name; # ( 769 => hda1, ... )
  101. if (open my $partitions, '<', '/proc/partitions') {
  102. <$partitions>; <$partitions>; # First two lines are for humans
  103. while (my $line = <$partitions>) {
  104. next unless $line =~ m/^ \s* (\d+) \s+ (\d+) \s+ \d+ \s+ (\S+) \s* $/x;
  105. my ($major, $minor, $devname) = ($1, $2, $3);
  106. my $devno = ($major << 8) + $minor;
  107. $name_to_number{$devname} = $devno;
  108. $number_to_name{$devno} = $devname;
  109. }
  110. } else {
  111. warn "Unable to open /proc/partitions: $!";
  112. }
  113. # Iterate over the hash { 1 => 768 } meaning (mogile device dev1 points to os device 768)
  114. foreach my $mogdevid (keys %$map) {
  115. # Look up the original device number
  116. my $original = $map->{$mogdevid};
  117. # See if there is a mapping to turn it into a device name (eg. hda1)
  118. my $devname = $number_to_name{$original} or next;
  119. # Pull off the new device name with a regex
  120. if (my ($newname) = $devname =~ m/^([hs]d\w+)\d+$/) {
  121. # Skip if we can't map it back to a device number
  122. my $newnum = $name_to_number{$newname} or next;
  123. $map->{$mogdevid} = $newnum;
  124. } elsif (my ($newname, undef) = $devname =~ m/^(cciss\/c\d+d\d+)(\w+)?$/) {
  125. my $newnum = $name_to_number{$newname} or next;
  126. $map->{$mogdevid} = $newnum;
  127. }
  128. }
  129. }
  130. return $map;
  131. }
  132. 1;