Class: Peep::Analysis

Inherits:
Object
  • Object
show all
Defined in:
lib/peep/peep.rb

Constant Summary collapse

IT_FLAGS =
{
  1 => "LINKED",
  2 => "CAS",
  3 => "LINKED CAS",
  4 => "SLABBED",
  5 => "LINKED SLABBED",
  6 => "SLABBED CAS",
  7 => "LINKED SLABBED CAS"
}
HEADER =
['time', 'exptime', 'nbytes', 'nsuffix', 'it_f', 'clsid', 'nkey', 'key', 'exprd', 'flushd']

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pid, options = {}) ⇒ Analysis

Returns a new instance of Analysis.

Raises:

  • (TypeError)


49
50
51
52
53
54
# File 'lib/peep/peep.rb', line 49

def initialize(pid, options = {})
  raise TypeError unless pid.is_a? Integer
  @pack_format = SIZES['address'] == 4 ? "l" : "q"
  @pid = pid
  init_addresses
end

Instance Attribute Details

#addressesObject (readonly)

Returns the value of attribute addresses.



6
7
8
# File 'lib/peep/peep.rb', line 6

def addresses
  @addresses
end

#pack_formatObject (readonly)

Returns the value of attribute pack_format.



7
8
9
# File 'lib/peep/peep.rb', line 7

def pack_format
  @pack_format
end

#pidObject (readonly)

Returns the value of attribute pid.



5
6
7
# File 'lib/peep/peep.rb', line 5

def pid
  @pid
end

Instance Method Details

#attachedObject



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/peep/peep.rb', line 56

def attached
  GC.disable
  Peep.attach pid
  sleep 0.5
  value = yield
  Peep.detach pid
  GC.enable
  value
ensure
  Process.kill("CONT", pid)
end

#basicsObject



68
69
70
71
72
73
74
75
76
77
# File 'lib/peep/peep.rb', line 68

def basics
  @basics ||= attached do
    {
      'hashtable' => read_long(addresses['primary_hashtable']),
      'hash_items' => read_long(addresses['hash_items']),
      'hashpower' => read_int(addresses['hashpower']),
   'process_started' => read_long(addresses['process_started'])
    }
  end
end

#init_addressesObject



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/peep/peep.rb', line 36

def init_addresses
  @addresses = {}
  IO.popen("gdb -q memcached #{pid}", "w+") do |gdb|
    %w(primary_hashtable hash_items hashpower stats settings process_started).each do | key|
      gdb.puts "p &#{key}"
      1 while (line = gdb.gets) !~ /\(gdb\)/
      @addresses[key] = line.split.last.hex
    end
    gdb.puts "detach"
    gdb.puts "quit"
  end
end

#itemsObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/peep/peep.rb', line 120

def items
  basics

  now = Time.now.to_i - basics['process_started']
  flushed = settings['oldest_live']
  items = []

  hashtable = basics['hashtable'] 

  attached do        
    (2**basics['hashpower']).times do
      bucket = read_long(hashtable)

      while !bucket.zero?
        item = [
          time = read_int(bucket + ITEM_OFFSETS['time']),
          exptime = read_int(bucket + ITEM_OFFSETS['exptime']),
          read_int(bucket + ITEM_OFFSETS['nbytes']),
          read_uint8(bucket + ITEM_OFFSETS['nsuffix']),
          IT_FLAGS[it_flags = read_uint8(bucket + ITEM_OFFSETS['it_flags'])],
          read_uint8(bucket + ITEM_OFFSETS['slabs_clsid']),
          nkey = read_uint8(bucket + ITEM_OFFSETS['nkey']),
          read_bytes(bucket + ITEM_OFFSETS['end'] + (it_flags & 2 ? 8 : 0), nkey),

          !(exptime.zero? or now < exptime), # expired?
          !(flushed.zero? or flushed < time) # flushed?
        ]
        
        block_given? ? yield(*item) : items << item

        bucket = read_long(bucket + ITEM_OFFSETS['h_next'])
      end
      
      hashtable += SIZES['address']
    end
  end

  items unless block_given?
end

#read_bytes(addr, nbytes) ⇒ Object



24
25
26
27
28
29
30
# File 'lib/peep/peep.rb', line 24

def read_bytes(addr, nbytes)
  buffer = ""
  (nbytes / SIZES['address'].to_f).ceil.times do |offset|
    buffer << [read_long(addr + offset * SIZES['address'])].pack(pack_format)
  end
  buffer[0, nbytes]
end

#read_double(addr) ⇒ Object



32
33
34
# File 'lib/peep/peep.rb', line 32

def read_double(addr)
  read_bytes(addr, SIZES['double']).unpack('d')
end

#read_int(addr) ⇒ Object



13
14
15
16
# File 'lib/peep/peep.rb', line 13

def read_int(addr)
  n = Peep.peek(pid, addr)
  SIZES['little_endian'] ? n & 0xffffffff : (n >> 32) & 0xffffffff
end

#read_long(addr) ⇒ Object



9
10
11
# File 'lib/peep/peep.rb', line 9

def read_long(addr)
  Peep.peek(pid, addr)
end

#read_uint8(addr) ⇒ Object



18
19
20
21
22
# File 'lib/peep/peep.rb', line 18

def read_uint8(addr)
  offset = addr % SIZES['address']
  word = read_long(addr - offset)
  [word].pack(pack_format)[offset]
end

#settingsObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/peep/peep.rb', line 90

def settings
  @settings ||= attached do
    # Static
    Hash[*
      SETTINGS_OFFSETS.map do |key, offset|
        [key, case key
            when 'prefix_delimiter'
              read_int(addresses['settings'] + offset).chr
            when 'factor'
              read_double(addresses['settings'] + offset)
            else
              read_long(addresses['settings'] + offset)
         end]
      end.flatten
    ]
  end
end

#statsObject



79
80
81
82
83
84
85
86
87
88
# File 'lib/peep/peep.rb', line 79

def stats
  @stats ||= attached do
    # Static
    Hash[*
      STATS_OFFSETS.map do |key, offset|
        [key, read_long(addresses['stats'] + offset)]
      end.flatten
    ]
  end
end