def parse_smaps_memory_region(pid, lines, has_header=True): """Parse a whole smaps region, which may look like: 7f5c8550e000-7f5c85554000 r--p 00000000 08:06 1309629 /fonts/Arial_Bold.ttf Size: 280 kB Rss: 152 kB Pss: 86 kB Shared_Clean: 132 kB Shared_Dirty: 12 kB Private_Clean: 20 kB Private_Dirty: 1 kB Referenced: 152 kB Anonymous: 2 kB AnonHugePages: 3 kB Shared_Hugetlb: 4 kB Private_Hugetlb: 5 kB Swap: 6 kB SwapPss: 7 kB KernelPageSize: 8 kB MMUPageSize: 9 kB Locked: 10 kB VmFlags: rd mr mw me sd""" has_header = is_memory_region_header(lines[0]) if has_header: region = parse_smaps_header(lines[0]) if region.name == '[vsyscall]': return None lines = lines[1:] else: region = MemoryRegion(free=False) region.pid = pid global _smaps_string_mappings for line in lines: LOGGER.debug('Parsing line: %s' % line) parts = re.split('[ :]+', line.strip()) if len(parts) < 2: LOGGER.debug('Skipping smaps line that is too short: %s' % line) elif 'Size' == parts[0]: # We calculate the size from the address ranges instead. pass elif 'VmFlags' == parts[0]: region.vm_flags = parts[1:] else: # All other lines should be an amount of some type of memory. try: region.__dict__[_smaps_string_mappings[parts[0]]] = int( parts[1]) * 1024 except KeyError: LOGGER.warn("Line not recognised: '%s'" % line) return region
def read_tailed_files(stream): section_name = '' data = '' processes = ProcessList() current_process = None current_thread = None out = {} out['stats'] = SystemStats() out['meminfo'] = MemoryStats() for line in stream: LOGGER.debug('Got line: %s' % line) if line == '': continue # tail gives us lines like: # # ==> /proc/99/smaps <== # # between files elif line.startswith('==>'): _parse_section(section_name, current_process, current_thread, data, out) data = '' section_name = '' current_process = None current_thread = None if '/proc/loadavg' in line: section_name = 'loadavg' continue elif '/proc/uptime' in line: section_name = 'uptime' continue elif '/proc/vmstat' in line: section_name = 'vmstat' continue elif '/proc/net/stat' in line: # We don't care about this entry. Skip it. continue # Now parse the new line. match = re.match(r'==> /proc/([0-9]+)/([\w]+) <==', line) if match is None: if any(x in line for x in ['/proc/stat', '/proc/self/', '/proc/thread-self/']): # We just ignore these entries, interetesting as they are, # for now. pass elif '/proc/meminfo' in line: section_name = 'meminfo' else: match = re.match( r'==> /proc/([0-9]+)/task/([0-9]+)/stat <==', line) if match is None: # The line might not be parsed here. There are a few LOGGER.warn('Unrecognised line, skipping: %s' % line) else: section_name = 'stat' pid = int(match.group(1)) # process id tid = int(match.group(2)) # thread id current_thread = processes.get(pid).get_thread(tid) else: section_name = match.group(2) current_process = processes.get(pid=int(match.group(1))) elif current_process and section_name == 'smaps' and is_memory_region_header( line): # We get here on reaching a new memory region in a smaps file. _save_smaps_region(current_process.maps, out['meminfo'], current_process.pid, data) data = line elif section_name != '': data += "\n" + line else: LOGGER.debug('Skipping line: %s' % line) # We've hit the end, parse the section we were in. _parse_section(section_name, current_process, current_thread, data, out) return out['stats'], processes, out['meminfo']