VirtualBox

Changeset 94243 in vbox


Ignore:
Timestamp:
Mar 15, 2022 11:45:03 AM (3 years ago)
Author:
vboxsync
Message:

Audio/Validation Kit: More code to handle host process execution in a separate thread to not block required event processing. bugref:10008

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/tests/audio/tdAudioTest.py

    r94122 r94243  
    3737import os
    3838import sys
    39 import signal
    4039import subprocess
    4140import time
     41import threading
    4242
    4343# Only the main script needs to modify the path.
     
    9191        # Optional arguments passing to VKAT when verifying audio test sets.
    9292        self.asVkatVerifyArgs = [];
     93
     94        # Exit code of last host process execution, shared between exeuction thread and main thread.
     95        # This ASSUMES that we only have one thread running at a time. Rather hacky, but does the job for now.
     96        self.iThreadHstProcRc = 0;
    9397
    9498        # Enable audio debug mode.
     
    285289        """
    286290        Inner loop which handles the execution of a host binary.
     291
     292        Might be called synchronously in main thread or via the thread exeuction helper (asynchronous).
    287293        """
    288294        fRc = False;
    289 
    290         ## @todo r=bird: From what I can tell this is running on the main thread.
    291         ##               So, you're blocking event handling till the process
    292         ##               finishes.  Not a great idea.
    293         ##
    294         ##               Also, use the base API base.testdriver.pidFileAdd API
    295         ##               and you can kick out the fAsAdmin and killHstProcessByName
    296         ##               fun.  (If you need to kill stuff, check what pidFileRead,
    297         ##               or better add some name based killer around it to the
    298         ##               base class.)
    299295
    300296        asEnvTmp = os.environ.copy();
     
    307303
    308304        try:
     305            # Spawn process.
    309306            if  fAsAdmin \
    310307            and utils.getHostOs() != 'win':
    311                 sStdOut = utils.sudoProcessOutputChecked(asArgs, env = asEnvTmp);
     308                oProcess = utils.sudoProcessStart(asArgs, env = asEnvTmp, stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
     309            else:
     310                oProcess = utils.processStart(asArgs, env = asEnvTmp, stdout=subprocess.PIPE, stderr=subprocess.STDOUT);
     311
     312            if not oProcess:
     313                reporter.error('Starting process for "%s" failed!' % (sWhat));
     314                return False;
     315
     316            iPid = oProcess.pid;
     317            self.pidFileAdd(iPid);
     318
     319            iRc  = 0;
     320
     321            # For Python 3.x we provide "real-time" output.
     322            if sys.version_info[0] >= 3:
     323                while oProcess.stdout.readable():
     324                    sStdOut = oProcess.stdout.readline();
     325                    if sStdOut:
     326                        sStdOut = sStdOut.strip();
     327                        reporter.log('%s: %s' % (sWhat, sStdOut));
     328                    iRc = oProcess.poll();
     329                    if iRc is not None:
     330                        break;
     331            else:
     332                # For Python 2.x it's too much hassle to set the file descriptor options (O_NONBLOCK) and stuff,
     333                # so just use communicate() here and dump everythiong all at once when finished.
     334                sStdOut = oProcess.communicate();
    312335                if sStdOut:
    313                     sStdOut = sStdOut.strip();
    314                     reporter.log("stdout:\n" + sStdOut);
     336                    reporter.log('%s: %s' % (sWhat, sStdOut));
     337                iRc = oProcess.poll();
     338
     339            if iRc == 0:
     340                reporter.log('*** %s: exit code %d' % (sWhat, iRc));
     341                fRc = True;
    315342            else:
    316                 (iExit, sStdOut, sStdErr) = utils.processOutputUnchecked(asArgs, env = asEnvTmp);
    317 
    318                 if sStdOut:
    319                     sStdOut = sStdOut.strip();
    320                     reporter.log("stdout:\n" + sStdOut);
    321 
    322                 if sStdErr:
    323                     sStdErr = sStdErr.strip();
    324                     reporter.log("stderr:\n" + sStdErr);
    325 
    326                 if iExit == 0:
    327                     reporter.log('*** %s: exit code %d' % (sWhat, iExit));
    328                     fRc = True;
    329                 else:
    330                     reporter.log('!*! %s: exit code %d' % (sWhat, iExit));
     343                reporter.log('!*! %s: exit code %d' % (sWhat, iRc));
     344
     345            self.pidFileRemove(iPid);
     346
     347            # Save thread result code.
     348            self.iThreadHstProcRc = iRc;
    331349
    332350        except:
     
    335353        return fRc;
    336354
    337     def executeHst(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
    338         """
    339         Runs a binary (image) with optional admin (root) rights on the host and
    340         waits until it terminates.
    341 
    342         Windows currently is not supported yet running stuff as Administrator.
    343 
    344         Returns success status (exit code is 0).
    345         """
    346         reporter.log('Executing \"%s\" on host (as admin = %s)' % (sWhat, fAsAdmin));
    347 
    348         try:    sys.stdout.flush();
    349         except: pass;
    350         try:    sys.stderr.flush();
    351         except: pass;
    352 
     355    def executeHstThread(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
     356        """
     357        Thread execution helper to run a process on the host.
     358        """
    353359        fRc = self.executeHstLoop(sWhat, asArgs, asEnv, fAsAdmin);
    354360        if fRc:
     
    357363            reporter.log('Executing \"%s\" on host failed' % (sWhat,));
    358364
    359         return fRc;
    360 
    361     def killHstProcessByName(self, sProcName):
    362         """
    363         Kills processes by their name.
    364         """
    365 
    366         ##
    367         ## @todo r=bird: This doesn't belong here and probably won't work right everywhere anyway.
    368         ##               See alternative approach outlined in executeHstLoop.
    369         ##
    370 
    371         reporter.log('Trying to kill processes named "%s"' % (sProcName,));
    372         if sys.platform == 'win32':
    373             sArgProcName = '\"%s.exe\"' % sProcName;
    374             asArgs       = [ 'taskkill', '/IM', sArgProcName, '/F' ];
    375             self.executeHst('Killing process', asArgs);
    376         else: # Note: killall is not available on older Debians (requires psmisc).
    377             # Using the BSD syntax here; MacOS also should understand this.
    378             procPs = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE); # pylint: disable=consider-using-with
    379             out, err = procPs.communicate();
    380             if err:
    381                 reporter.log('PS stderr:');
    382                 for sLine in err.decode('utf-8').splitlines():
    383                     reporter.log(sLine);
    384             if out:
    385                 reporter.log4('PS stdout:');
    386                 for sLine in out.decode('utf-8').splitlines():
    387                     reporter.log4(sLine);
    388                     if sProcName in sLine: ## @todo r=bird: This just isn't good enough for short stuff like 'vkat'!
    389                         pid = int(sLine.split(None, 1)[0]);
    390                         reporter.log('Killing PID %d' % (pid,));
    391                         os.kill(pid, signal.SIGKILL); # pylint: disable=no-member
    392 
    393     def killHstVkat(self):
    394         """
    395         Kills VKAT (VBoxAudioTest) on the host side.
    396         """
    397         reporter.log('Killing stale/old VKAT processes ...');
    398         self.killHstProcessByName("vkat");
    399         self.killHstProcessByName("VBoxAudioTest");
     365    def executeHst(self, sWhat, asArgs, asEnv = None, fAsAdmin = False):
     366        """
     367        Runs a binary (image) with optional admin (root) rights on the host and
     368        waits until it terminates.
     369
     370        Windows currently is not supported yet running stuff as Administrator.
     371
     372        Returns success status (exit code is 0).
     373        """
     374        reporter.log('Executing \"%s\" on host (as admin = %s)' % (sWhat, fAsAdmin));
     375
     376        try:    sys.stdout.flush();
     377        except: pass;
     378        try:    sys.stderr.flush();
     379        except: pass;
     380
     381        # Initialize thread rc.
     382        self.iThreadHstProcRc = -42;
     383
     384        try:
     385            oThread = threading.Thread(target = self.executeHstThread, args = [ sWhat, asArgs, asEnv, fAsAdmin ]);
     386            oThread.start();
     387            while oThread.join(0.1):
     388                if not oThread.is_alive():
     389                    break;
     390                self.wait(1);
     391            reporter.log2('Thread returned exit code for "%s": %d' % (sWhat, self.iThreadHstProcRc));
     392        except:
     393            self.logXcpt('Starting thread for "%s" failed' % (sWhat,));
     394
     395        return self.iThreadHstProcRc == 0;
    400396
    401397    def getWinFirewallArgsDisable(self, sOsType):
     
    661657            return False;
    662658
    663         # First try to kill any old VKAT / VBoxAudioTest processes lurking around on the host.
    664         # Might happen because of former (aborted) runs.
    665         self.killHstVkat();
    666 
    667659        reporter.log("Active tests: %s" % (self.asTests,));
    668660
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette