def read_smap_file(): total = 0 fp = open(proc_path("self/smaps"), "rb") with fp: for line in fp: # Include both Private_Clean and Private_Dirty sections. line = line.rstrip() if line.startswith(b"Private_") and line.endswith(b'kB'): parts = line.split() total += int(parts[1]) * 1024 return total
def read_rcu_nocbs(self): cmdline = self.read_first_line(proc_path('cmdline')) if not cmdline: return match = re.search(r'\brcu_nocbs=([^ ]+)', cmdline) if not match: return cpus = match.group(1) return parse_cpu_list(cpus)
def read_proc(path): path = proc_path(path) try: fp = open_text(path) try: for line in fp: yield line.rstrip() finally: # don't use context manager to support StringIO on Python 2 # for unit tests fp.close() except (OSError, IOError): return
def check_tracking_memory(): mem_thread = PeakMemoryUsageThread() try: mem_thread.get() except IOError as exc: path = proc_path("self/smaps") return "unable to read %s: %s" % (path, exc) if not mem_thread.peak_usage: return "memory usage is zero" # it seems to work return None
class ASLR(Operation): # randomize_va_space procfs existed prior to 2.6.12-rc2 (2005) # which is first commit of the Linux git repository STATE = { '0': 'No randomization', '1': 'Conservative randomization', '2': 'Full randomization' } path = proc_path("sys/kernel/randomize_va_space") @classmethod def available(cls): return os.path.exists(cls.path) def __init__(self, system): Operation.__init__(self, 'ASLR', system) def show(self): line = self.read_first_line(self.path) try: state = self.STATE[line] except KeyError: self.error("Failed to read %s" % self.path) return self.log_state(state) self.tuned_for_benchmarks = (line == '2') if not self.tuned_for_benchmarks: self.advice("Enable full randomization: write 2 into %s" % self.path) def write(self, tune): value = self.read_first_line(self.path) if not value: return new_value = '2' if new_value == value: return try: write_text(self.path, new_value) except IOError as exc: self.check_permission_error(exc) self.error("Failed to write into %s: %s" % (self.path, exc)) else: self.log_action("Full randomization enabled: %r written into %s" % (new_value, self.path))
class PerfEvent(Operation): # Minimise time spent by the Linux perf kernel profiler. BENCHMARK_RATE = 1 path = proc_path("sys/kernel/perf_event_max_sample_rate") @classmethod def available(cls): return os.path.exists(cls.path) def __init__(self, system): Operation.__init__(self, 'Perf event', system) def read_max_sample_rate(self): line = self.read_first_line(self.path) if not line: return None return int(line) def show(self): max_sample_rate = self.read_max_sample_rate() if not max_sample_rate: return self.log_state("Maximum sample rate: %s per second" % max_sample_rate) self.tuned_for_benchmarks = (max_sample_rate == self.BENCHMARK_RATE) if not self.tuned_for_benchmarks: self.advice("Set max sample rate to %s" % self.BENCHMARK_RATE) def write(self, tune): if tune: new_rate = self.BENCHMARK_RATE else: new_rate = 100000 max_sample_rate = self.read_max_sample_rate() if max_sample_rate == new_rate: return try: write_text(self.path, str(new_rate)) except IOError as exc: self.check_permission_error(exc) self.error("Failed to write into %s: %s" % (self.path, exc)) else: self.log_action("Max sample rate set to %s per second" % new_rate)
def get_isolated_cpus(): """Get the list of isolated CPUs. Return a sorted list of CPU identifiers, or return None if no CPU is isolated. """ # The cpu/isolated sysfs was added in Linux 4.2 # (commit 59f30abe94bff50636c8cad45207a01fdcb2ee49) path = sysfs_path('devices/system/cpu/isolated') isolated = read_first_line(path) if isolated: return parse_cpu_list(isolated) cmdline = read_first_line(proc_path('cmdline')) if cmdline: match = re.search(r'\bisolcpus=([^ ]+)', cmdline) if match: isolated = match.group(1) return parse_cpu_list(isolated) return None
class IRQAffinity(Operation): # /proc/irq/N/smp_affinity existed prior to 2.6.12-rc2 (2005) # which is first commit of the Linux git repository irq_path = proc_path('irq') @classmethod def available(cls): return os.path.exists(cls.irq_path) def __init__(self, system): Operation.__init__(self, 'IRQ affinity', system) self.irq_affinity_path = os.path.join(self.irq_path, "%s/smp_affinity") self.default_affinity_path = os.path.join(self.irq_path, 'default_smp_affinity') self.systemctl = True self.irqs = None def read_irqbalance_systemctl(self): cmd = ('systemctl', 'status', 'irqbalance') exitcode, stdout = get_output(cmd) if not stdout: # systemctl is not installed? ignore errors self.systemctl = False return match = re.search(r"^ *Loaded: (.*)$", stdout, flags=re.MULTILINE) if not match: self.error("Failed to parse systemctl loaded state: %r" % stdout) return self.systemctl = True loaded = match.group(1) if loaded.startswith('not-found'): # irqbalance service is not installed: do nothing return match = re.search(r"^ *Active: ([^ ]+)", stdout, flags=re.MULTILINE) if not match: self.error("Failed to parse systemctl active state: %r" % stdout) return active = match.group(1) if active in ('active', 'activating'): return True elif active in ('inactive', 'deactivating', 'dead'): return False else: self.error("Unknown service state: %r" % active) def read_irqbalance_service(self): cmd = ('service', 'irqbalance', 'status') exitcode, stdout = get_output(cmd) if not stdout: # failed to the the status: ignore return stdout = stdout.rstrip() state = stdout.split(' ', 1)[-1] if state.startswith('stop'): return False elif state.startswith('start'): return True else: self.error("Unknown service state: %r" % stdout) def read_irqbalance_state(self): active = self.read_irqbalance_systemctl() if self.systemctl is False: active = self.read_irqbalance_service() return active def parse_affinity(self, mask): mask = parse_cpu_mask(mask) cpus = [] for cpu in range(self.system.logical_cpu_count): cpu_mask = 1 << cpu if cpu_mask & mask: cpus.append(cpu) return cpus def read_default_affinity(self): mask = self.read_first_line(self.default_affinity_path) if not mask: return return self.parse_affinity(mask) def get_irqs(self): if self.irqs is None: filenames = os.listdir(self.irq_path) self.irqs = [int(name) for name in filenames if name.isdigit()] self.irqs.sort() return self.irqs def read_irq_affinity(self, irq): path = self.irq_affinity_path % irq mask = self.read_first_line(path) if not mask: self.error("Failed to read %s" % path) return return self.parse_affinity(mask) def read_irqs_affinity(self): affinity = {} for irq in self.get_irqs(): if self.permission_error: break cpus = self.read_irq_affinity(irq) if cpus is not None: affinity[irq] = cpus return affinity def show(self): irqbalance_active = self.read_irqbalance_state() if irqbalance_active is not None: state = 'active' if irqbalance_active else 'inactive' self.log_state("irqbalance service: %s" % state) default_smp_affinity = self.read_default_affinity() if default_smp_affinity: self.log_state("Default IRQ affinity: CPU %s" % format_cpu_list(default_smp_affinity)) irq_affinity = self.read_irqs_affinity() if irq_affinity: infos = {irq: 'CPU %s' % format_cpu_list(cpus) for irq, cpus in irq_affinity.items()} infos = format_cpu_infos(infos) infos = ['IRQ %s' % info for info in infos] self.log_state('IRQ affinity: %s' % '; '.join(infos)) def write_irqbalance_service(self, enable): irqbalance_active = self.read_irqbalance_state() if irqbalance_active is None: # systemd service missing or failed to get its state: # don't try to start/stop the irqbalance service return if irqbalance_active == enable: # service is already in the expected state: nothing to do return action = 'start' if enable else 'stop' if self.systemctl is False: cmd = ('service', 'irqbalance', action) else: cmd = ('systemctl', action, 'irqbalance') exitcode = run_cmd(cmd) if exitcode: self.error('Failed to %s irqbalance service: ' '%s failed with exit code %s' % (action, ' '.join(cmd), exitcode)) return action = 'Start' if enable else 'Stop' self.log_action("%s irqbalance service" % action) def write_default(self, new_affinity): default_smp_affinity = self.read_default_affinity() if new_affinity == default_smp_affinity: return mask = format_cpus_as_mask(new_affinity) try: write_text(self.default_affinity_path, mask) except IOError as exc: self.check_permission_error(exc) self.error("Failed to write %r into %s: %s" % (mask, self.default_affinity_path, exc)) else: self.log_action("Set default affinity to CPU %s" % format_cpu_list(new_affinity)) def write_irq(self, irq, cpus): path = self.irq_affinity_path % irq mask = format_cpus_as_mask(cpus) try: write_text(path, mask) return True except IOError as exc: self.check_permission_error(exc) # EIO means that the IRQ doesn't support SMP affinity: # ignore the error if exc.errno != errno.EIO: self.error("Failed to write %r into %s: %s" % (mask, path, exc)) return False def write_irqs(self, new_cpus): affinity = self.read_irqs_affinity() modified = [] for irq in self.get_irqs(): if self.permission_error: break cpus = affinity.get(irq) if new_cpus == cpus: continue if self.write_irq(irq, new_cpus): modified.append(irq) if modified: self.log_action("Set affinity of IRQ %s to CPU %s" % (format_cpu_list(modified), format_cpu_list(new_cpus))) def write(self, tune): cpus = range(self.system.logical_cpu_count) if tune: excluded = set(self.system.cpus) # Only compute the subset if excluded is not the full list of cpus if excluded != set(cpus): cpus = (cpu for cpu in cpus if cpu not in excluded) cpus = list(cpus) self.write_irqbalance_service(not tune) self.write_default(cpus) self.write_irqs(cpus)