parent
							
								
									a2bb2dc9f4
								
							
						
					
					
						commit
						eaa6fe2510
					
				
				 3 changed files with 1 additions and 132 deletions
			
			
		@ -1,112 +0,0 @@ | 
				
			||||
#!/usr/bin/env python3 | 
				
			||||
import os | 
				
			||||
import threading | 
				
			||||
import time | 
				
			||||
import subprocess | 
				
			||||
import signal | 
				
			||||
 | 
				
			||||
if "CI" in os.environ: | 
				
			||||
  def tqdm(x): | 
				
			||||
    return x | 
				
			||||
else: | 
				
			||||
  from tqdm import tqdm   # type: ignore | 
				
			||||
 | 
				
			||||
import cereal.messaging as messaging | 
				
			||||
from collections import namedtuple | 
				
			||||
from openpilot.tools.lib.logreader import LogReader | 
				
			||||
from openpilot.tools.lib.openpilotci import get_url | 
				
			||||
from openpilot.common.basedir import BASEDIR | 
				
			||||
 | 
				
			||||
ProcessConfig = namedtuple('ProcessConfig', ['proc_name', 'pub_sub', 'ignore', 'command', 'path', 'segment', 'wait_for_response']) | 
				
			||||
 | 
				
			||||
CONFIGS = [ | 
				
			||||
  ProcessConfig( | 
				
			||||
    proc_name="ubloxd", | 
				
			||||
    pub_sub={ | 
				
			||||
      "ubloxRaw": ["ubloxGnss", "gpsLocationExternal"], | 
				
			||||
    }, | 
				
			||||
    ignore=[], | 
				
			||||
    command="./ubloxd", | 
				
			||||
    path="system/ubloxd", | 
				
			||||
    segment="0375fdf7b1ce594d|2019-06-13--08-32-25--3", | 
				
			||||
    wait_for_response=True | 
				
			||||
  ), | 
				
			||||
] | 
				
			||||
 | 
				
			||||
 | 
				
			||||
class TestValgrind: | 
				
			||||
  def extract_leak_sizes(self, log): | 
				
			||||
    if "All heap blocks were freed -- no leaks are possible" in log: | 
				
			||||
      return (0,0,0) | 
				
			||||
 | 
				
			||||
    log = log.replace(",","")  # fixes casting to int issue with large leaks | 
				
			||||
    err_lost1 = log.split("definitely lost: ")[1] | 
				
			||||
    err_lost2 = log.split("indirectly lost: ")[1] | 
				
			||||
    err_lost3 = log.split("possibly lost: ")[1] | 
				
			||||
    definitely_lost = int(err_lost1.split(" ")[0]) | 
				
			||||
    indirectly_lost = int(err_lost2.split(" ")[0]) | 
				
			||||
    possibly_lost = int(err_lost3.split(" ")[0]) | 
				
			||||
    return (definitely_lost, indirectly_lost, possibly_lost) | 
				
			||||
 | 
				
			||||
  def valgrindlauncher(self, arg, cwd): | 
				
			||||
    os.chdir(os.path.join(BASEDIR, cwd)) | 
				
			||||
    # Run valgrind on a process | 
				
			||||
    command = "valgrind --leak-check=full " + arg | 
				
			||||
    p = subprocess.Popen(command, stderr=subprocess.PIPE, shell=True, preexec_fn=os.setsid) | 
				
			||||
 | 
				
			||||
    while not self.replay_done: | 
				
			||||
      time.sleep(0.1) | 
				
			||||
 | 
				
			||||
    # Kill valgrind and extract leak output | 
				
			||||
    os.killpg(os.getpgid(p.pid), signal.SIGINT) | 
				
			||||
    _, err = p.communicate() | 
				
			||||
    error_msg = str(err, encoding='utf-8') | 
				
			||||
    with open(os.path.join(BASEDIR, "selfdrive/test/valgrind_logs.txt"), "a") as f: | 
				
			||||
      f.write(error_msg) | 
				
			||||
      f.write(5 * "\n") | 
				
			||||
    definitely_lost, indirectly_lost, possibly_lost = self.extract_leak_sizes(error_msg) | 
				
			||||
    if max(definitely_lost, indirectly_lost, possibly_lost) > 0: | 
				
			||||
      self.leak = True | 
				
			||||
      print("LEAKS from", arg, "\nDefinitely lost:", definitely_lost, "\nIndirectly lost", indirectly_lost, "\nPossibly lost", possibly_lost) | 
				
			||||
    else: | 
				
			||||
      self.leak = False | 
				
			||||
 | 
				
			||||
  def replay_process(self, config, logreader): | 
				
			||||
    pub_sockets = list(config.pub_sub.keys())  # We dump data from logs here | 
				
			||||
    sub_sockets = [s for _, sub in config.pub_sub.items() for s in sub]  # We get responses here | 
				
			||||
    pm = messaging.PubMaster(pub_sockets) | 
				
			||||
    sm = messaging.SubMaster(sub_sockets) | 
				
			||||
 | 
				
			||||
    print("Sorting logs") | 
				
			||||
    all_msgs = sorted(logreader, key=lambda msg: msg.logMonoTime) | 
				
			||||
    pub_msgs = [msg for msg in all_msgs if msg.which() in list(config.pub_sub.keys())] | 
				
			||||
 | 
				
			||||
    thread = threading.Thread(target=self.valgrindlauncher, args=(config.command, config.path)) | 
				
			||||
    thread.daemon = True | 
				
			||||
    thread.start() | 
				
			||||
 | 
				
			||||
    while not all(pm.all_readers_updated(s) for s in config.pub_sub.keys()): | 
				
			||||
      time.sleep(0) | 
				
			||||
 | 
				
			||||
    for msg in tqdm(pub_msgs): | 
				
			||||
      pm.send(msg.which(), msg.as_builder()) | 
				
			||||
      if config.wait_for_response: | 
				
			||||
        sm.update(100) | 
				
			||||
 | 
				
			||||
    self.replay_done = True | 
				
			||||
 | 
				
			||||
  def test_config(self): | 
				
			||||
    open(os.path.join(BASEDIR, "selfdrive/test/valgrind_logs.txt"), "w").close() | 
				
			||||
 | 
				
			||||
    for cfg in CONFIGS: | 
				
			||||
      self.leak = None | 
				
			||||
      self.replay_done = False | 
				
			||||
 | 
				
			||||
      r, n = cfg.segment.rsplit("--", 1) | 
				
			||||
      lr = LogReader(get_url(r, n)) | 
				
			||||
      self.replay_process(cfg, lr) | 
				
			||||
 | 
				
			||||
      while self.leak is None: | 
				
			||||
        time.sleep(0.1)  # Wait for the valgrind to finish | 
				
			||||
 | 
				
			||||
      assert not self.leak | 
				
			||||
					Loading…
					
					
				
		Reference in new issue