VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/common/utils.py@ 82012

Last change on this file since 82012 was 81112, checked in by vboxsync, 5 years ago

common/utils.py: Added getPresentCpuCount method. [fix] bugref:9501

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.8 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: utils.py 81112 2019-10-04 21:45:22Z vboxsync $
3# pylint: disable=too-many-lines
4
5"""
6Common Utility Functions.
7"""
8
9from __future__ import print_function;
10
11__copyright__ = \
12"""
13Copyright (C) 2012-2019 Oracle Corporation
14
15This file is part of VirtualBox Open Source Edition (OSE), as
16available from http://www.virtualbox.org. This file is free software;
17you can redistribute it and/or modify it under the terms of the GNU
18General Public License (GPL) as published by the Free Software
19Foundation, in version 2 as it comes in the "COPYING" file of the
20VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22
23The contents of this file may alternatively be used under the terms
24of the Common Development and Distribution License Version 1.0
25(CDDL) only, as it comes in the "COPYING.CDDL" file of the
26VirtualBox OSE distribution, in which case the provisions of the
27CDDL are applicable instead of those of the GPL.
28
29You may elect to license modified versions of this file under the
30terms and conditions of either the GPL or the CDDL or both.
31"""
32__version__ = "$Revision: 81112 $"
33
34
35# Standard Python imports.
36import datetime;
37import os;
38import platform;
39import re;
40import stat;
41import subprocess;
42import sys;
43import time;
44import traceback;
45import unittest;
46
47if sys.platform == 'win32':
48 import ctypes;
49 import msvcrt; # pylint: disable=import-error
50 import win32api; # pylint: disable=import-error
51 import win32con; # pylint: disable=import-error
52 import win32console; # pylint: disable=import-error
53 import win32file; # pylint: disable=import-error
54 import win32process; # pylint: disable=import-error
55 import winerror; # pylint: disable=import-error
56 import pywintypes; # pylint: disable=import-error
57else:
58 import signal;
59
60# Python 3 hacks:
61if sys.version_info[0] >= 3:
62 unicode = str; # pylint: disable=redefined-builtin,invalid-name
63 xrange = range; # pylint: disable=redefined-builtin,invalid-name
64 long = int; # pylint: disable=redefined-builtin,invalid-name
65
66
67#
68# Output.
69#
70
71def printOut(sString):
72 """
73 Outputs a string to standard output, dealing with python 2.x encoding stupidity.
74 """
75 sStreamEncoding = sys.stdout.encoding;
76 if sStreamEncoding is None: # Files, pipes and such on 2.x. (pylint is confused here)
77 sStreamEncoding = 'US-ASCII'; # pylint: disable=redefined-variable-type
78 if sStreamEncoding == 'UTF-8' or not isinstance(sString, unicode):
79 print(sString);
80 else:
81 print(sString.encode(sStreamEncoding, 'backslashreplace').decode(sStreamEncoding));
82
83def printErr(sString):
84 """
85 Outputs a string to standard error, dealing with python 2.x encoding stupidity.
86 """
87 sStreamEncoding = sys.stderr.encoding;
88 if sStreamEncoding is None: # Files, pipes and such on 2.x. (pylint is confused here)
89 sStreamEncoding = 'US-ASCII'; # pylint: disable=redefined-variable-type
90 if sStreamEncoding == 'UTF-8' or not isinstance(sString, unicode):
91 print(sString, file = sys.stderr);
92 else:
93 print(sString.encode(sStreamEncoding, 'backslashreplace').decode(sStreamEncoding), file = sys.stderr);
94
95
96#
97# Host OS and CPU.
98#
99
100def getHostOs():
101 """
102 Gets the host OS name (short).
103
104 See the KBUILD_OSES variable in kBuild/header.kmk for possible return values.
105 """
106 sPlatform = platform.system();
107 if sPlatform in ('Linux', 'Darwin', 'Solaris', 'FreeBSD', 'NetBSD', 'OpenBSD'):
108 sPlatform = sPlatform.lower();
109 elif sPlatform == 'Windows':
110 sPlatform = 'win';
111 elif sPlatform == 'SunOS':
112 sPlatform = 'solaris';
113 else:
114 raise Exception('Unsupported platform "%s"' % (sPlatform,));
115 return sPlatform;
116
117g_sHostArch = None;
118
119def getHostArch():
120 """
121 Gets the host CPU architecture.
122
123 See the KBUILD_ARCHES variable in kBuild/header.kmk for possible return values.
124 """
125 global g_sHostArch;
126 if g_sHostArch is None:
127 sArch = platform.machine();
128 if sArch in ('i386', 'i486', 'i586', 'i686', 'i786', 'i886', 'x86'):
129 sArch = 'x86';
130 elif sArch in ('AMD64', 'amd64', 'x86_64'):
131 sArch = 'amd64';
132 elif sArch == 'i86pc': # SunOS
133 if platform.architecture()[0] == '64bit':
134 sArch = 'amd64';
135 else:
136 try:
137 sArch = str(processOutputChecked(['/usr/bin/isainfo', '-n',]));
138 except:
139 pass;
140 sArch = sArch.strip();
141 if sArch != 'amd64':
142 sArch = 'x86';
143 else:
144 raise Exception('Unsupported architecture/machine "%s"' % (sArch,));
145 g_sHostArch = sArch;
146 return g_sHostArch;
147
148
149def getHostOsDotArch():
150 """
151 Gets the 'os.arch' for the host.
152 """
153 return '%s.%s' % (getHostOs(), getHostArch());
154
155
156def isValidOs(sOs):
157 """
158 Validates the OS name.
159 """
160 if sOs in ('darwin', 'dos', 'dragonfly', 'freebsd', 'haiku', 'l4', 'linux', 'netbsd', 'nt', 'openbsd', \
161 'os2', 'solaris', 'win', 'os-agnostic'):
162 return True;
163 return False;
164
165
166def isValidArch(sArch):
167 """
168 Validates the CPU architecture name.
169 """
170 if sArch in ('x86', 'amd64', 'sparc32', 'sparc64', 's390', 's390x', 'ppc32', 'ppc64', \
171 'mips32', 'mips64', 'ia64', 'hppa32', 'hppa64', 'arm', 'alpha'):
172 return True;
173 return False;
174
175def isValidOsDotArch(sOsDotArch):
176 """
177 Validates the 'os.arch' string.
178 """
179
180 asParts = sOsDotArch.split('.');
181 if asParts.length() != 2:
182 return False;
183 return isValidOs(asParts[0]) \
184 and isValidArch(asParts[1]);
185
186def getHostOsVersion():
187 """
188 Returns the host OS version. This is platform.release with additional
189 distro indicator on linux.
190 """
191 sVersion = platform.release();
192 sOs = getHostOs();
193 if sOs == 'linux':
194 sDist = '';
195 try:
196 # try /etc/lsb-release first to distinguish between Debian and Ubuntu
197 oFile = open('/etc/lsb-release');
198 for sLine in oFile:
199 oMatch = re.search(r'(?:DISTRIB_DESCRIPTION\s*=)\s*"*(.*)"', sLine);
200 if oMatch is not None:
201 sDist = oMatch.group(1).strip();
202 except:
203 pass;
204 if sDist:
205 sVersion += ' / ' + sDist;
206 else:
207 asFiles = \
208 [
209 [ '/etc/debian_version', 'Debian v'],
210 [ '/etc/gentoo-release', '' ],
211 [ '/etc/oracle-release', '' ],
212 [ '/etc/redhat-release', '' ],
213 [ '/etc/SuSE-release', '' ],
214 ];
215 for sFile, sPrefix in asFiles:
216 if os.path.isfile(sFile):
217 try:
218 oFile = open(sFile);
219 sLine = oFile.readline();
220 oFile.close();
221 except:
222 continue;
223 sLine = sLine.strip()
224 if sLine:
225 sVersion += ' / ' + sPrefix + sLine;
226 break;
227
228 elif sOs == 'solaris':
229 sVersion = platform.version();
230 if os.path.isfile('/etc/release'):
231 try:
232 oFile = open('/etc/release');
233 sLast = oFile.readlines()[-1];
234 oFile.close();
235 sLast = sLast.strip();
236 if sLast:
237 sVersion += ' (' + sLast + ')';
238 except:
239 pass;
240
241 elif sOs == 'darwin':
242 sOsxVersion = platform.mac_ver()[0];
243 codenames = {"4": "Tiger",
244 "5": "Leopard",
245 "6": "Snow Leopard",
246 "7": "Lion",
247 "8": "Mountain Lion",
248 "9": "Mavericks",
249 "10": "Yosemite",
250 "11": "El Capitan",
251 "12": "Sierra",
252 "13": "High Sierra",
253 "14": "Unknown 14", }
254 sVersion += ' / OS X ' + sOsxVersion + ' (' + codenames[sOsxVersion.split('.')[1]] + ')'
255
256 elif sOs == 'win':
257 class OSVersionInfoEx(ctypes.Structure):
258 """ OSVERSIONEX """
259 kaFields = [
260 ('dwOSVersionInfoSize', ctypes.c_ulong),
261 ('dwMajorVersion', ctypes.c_ulong),
262 ('dwMinorVersion', ctypes.c_ulong),
263 ('dwBuildNumber', ctypes.c_ulong),
264 ('dwPlatformId', ctypes.c_ulong),
265 ('szCSDVersion', ctypes.c_wchar*128),
266 ('wServicePackMajor', ctypes.c_ushort),
267 ('wServicePackMinor', ctypes.c_ushort),
268 ('wSuiteMask', ctypes.c_ushort),
269 ('wProductType', ctypes.c_byte),
270 ('wReserved', ctypes.c_byte)]
271 _fields_ = kaFields # pylint: disable=invalid-name
272
273 def __init__(self):
274 super(OSVersionInfoEx, self).__init__()
275 self.dwOSVersionInfoSize = ctypes.sizeof(self)
276
277 oOsVersion = OSVersionInfoEx()
278 rc = ctypes.windll.Ntdll.RtlGetVersion(ctypes.byref(oOsVersion))
279 if rc == 0:
280 # Python platform.release() is not reliable for newer server releases
281 if oOsVersion.wProductType != 1:
282 if oOsVersion.dwMajorVersion == 10 and oOsVersion.dwMinorVersion == 0:
283 sVersion = '2016Server';
284 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 3:
285 sVersion = '2012ServerR2';
286 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 2:
287 sVersion = '2012Server';
288 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 1:
289 sVersion = '2008ServerR2';
290 elif oOsVersion.dwMajorVersion == 6 and oOsVersion.dwMinorVersion == 0:
291 sVersion = '2008Server';
292 elif oOsVersion.dwMajorVersion == 5 and oOsVersion.dwMinorVersion == 2:
293 sVersion = '2003Server';
294 sVersion += ' build ' + str(oOsVersion.dwBuildNumber)
295 if oOsVersion.wServicePackMajor:
296 sVersion += ' SP' + str(oOsVersion.wServicePackMajor)
297 if oOsVersion.wServicePackMinor:
298 sVersion += '.' + str(oOsVersion.wServicePackMinor)
299
300 return sVersion;
301
302def getPresentCpuCount():
303 """
304 Gets the number of CPUs present in the system.
305
306 This differs from multiprocessor.cpu_count() and os.cpu_count() on windows in
307 that we return the active count rather than the maximum count. If we don't,
308 we will end up thinking testboxmem1 has 512 CPU threads, which it doesn't and
309 never will have.
310
311 @todo This is probably not exactly what we get on non-windows...
312 """
313
314 if getHostOs() == 'win':
315 fnGetActiveProcessorCount = getattr(ctypes.windll.kernel32, 'GetActiveProcessorCount', None);
316 if fnGetActiveProcessorCount:
317 cCpus = fnGetActiveProcessorCount(ctypes.c_ushort(0xffff));
318 if cCpus > 0:
319 return cCpus;
320
321 import multiprocessing
322 return multiprocessing.cpu_count();
323
324
325#
326# File system.
327#
328
329def openNoInherit(sFile, sMode = 'r'):
330 """
331 Wrapper around open() that tries it's best to make sure the file isn't
332 inherited by child processes.
333
334 This is a best effort thing at the moment as it doesn't synchronizes with
335 child process spawning in any way. Thus it can be subject to races in
336 multithreaded programs.
337 """
338
339 # Python 3.4 and later automatically creates non-inherit handles. See PEP-0446.
340 uPythonVer = (sys.version_info[0] << 16) | (sys.version_info[1] & 0xffff);
341 if uPythonVer >= ((3 << 16) | 4):
342 oFile = open(sFile, sMode);
343 else:
344 try:
345 from fcntl import FD_CLOEXEC, F_GETFD, F_SETFD, fcntl; # pylint: disable=import-error
346 except:
347 # On windows, we can use the 'N' flag introduced in Visual C++ 7.0 or 7.1 with python 2.x.
348 if getHostOs() == 'win':
349 if uPythonVer < (3 << 16):
350 offComma = sMode.find(',');
351 if offComma < 0:
352 return open(sFile, sMode + 'N');
353 return open(sFile, sMode[:offComma] + 'N' + sMode[offComma:]); # pylint: disable=bad-open-mode
354
355 # Just in case.
356 return open(sFile, sMode);
357
358 oFile = open(sFile, sMode);
359 #try:
360 fcntl(oFile, F_SETFD, fcntl(oFile, F_GETFD) | FD_CLOEXEC);
361 #except:
362 # pass;
363 return oFile;
364
365def openNoDenyDeleteNoInherit(sFile, sMode = 'r'):
366 """
367 Wrapper around open() that tries it's best to make sure the file isn't
368 inherited by child processes.
369
370 This is a best effort thing at the moment as it doesn't synchronizes with
371 child process spawning in any way. Thus it can be subject to races in
372 multithreaded programs.
373 """
374
375 if getHostOs() == 'win':
376 # Need to use CreateFile directly to open the file so we can feed it FILE_SHARE_DELETE.
377 # pylint: disable=no-member,c-extension-no-member
378 fAccess = 0;
379 fDisposition = win32file.OPEN_EXISTING;
380 if 'r' in sMode or '+' in sMode:
381 fAccess |= win32file.GENERIC_READ;
382 if 'a' in sMode:
383 fAccess |= win32file.GENERIC_WRITE;
384 fDisposition = win32file.OPEN_ALWAYS;
385 elif 'w' in sMode:
386 fAccess = win32file.GENERIC_WRITE;
387 if '+' in sMode:
388 fDisposition = win32file.OPEN_ALWAYS;
389 fAccess |= win32file.GENERIC_READ;
390 else:
391 fDisposition = win32file.CREATE_ALWAYS;
392 if not fAccess:
393 fAccess |= win32file.GENERIC_READ;
394 fSharing = (win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE
395 | win32file.FILE_SHARE_DELETE);
396 hFile = win32file.CreateFile(sFile, fAccess, fSharing, None, fDisposition, 0, None);
397 if 'a' in sMode:
398 win32file.SetFilePointer(hFile, 0, win32file.FILE_END);
399
400 # Turn the NT handle into a CRT file descriptor.
401 hDetachedFile = hFile.Detach();
402 if fAccess == win32file.GENERIC_READ:
403 fOpen = os.O_RDONLY;
404 elif fAccess == win32file.GENERIC_WRITE:
405 fOpen = os.O_WRONLY;
406 else:
407 fOpen = os.O_RDWR;
408 # pulint: enable=no-member,c-extension-no-member
409 if 'a' in sMode:
410 fOpen |= os.O_APPEND;
411 if 'b' in sMode or 't' in sMode:
412 fOpen |= os.O_TEXT; # pylint: disable=no-member
413 fdFile = msvcrt.open_osfhandle(hDetachedFile, fOpen);
414
415 # Tell python to use this handle.
416 oFile = os.fdopen(fdFile, sMode);
417 else:
418 oFile = open(sFile, sMode);
419
420 # Python 3.4 and later automatically creates non-inherit handles. See PEP-0446.
421 uPythonVer = (sys.version_info[0] << 16) | (sys.version_info[1] & 0xffff);
422 if uPythonVer < ((3 << 16) | 4):
423 try:
424 from fcntl import FD_CLOEXEC, F_GETFD, F_SETFD, fcntl; # pylint: disable=import-error
425 except:
426 pass;
427 else:
428 fcntl(oFile, F_SETFD, fcntl(oFile, F_GETFD) | FD_CLOEXEC);
429 return oFile;
430
431def noxcptReadLink(sPath, sXcptRet, sEncoding = 'utf-8'):
432 """
433 No exceptions os.readlink wrapper.
434 """
435 try:
436 sRet = os.readlink(sPath); # pylint: disable=no-member
437 except:
438 return sXcptRet;
439 if hasattr(sRet, 'decode'):
440 sRet = sRet.decode(sEncoding, 'ignore');
441 return sRet;
442
443def readFile(sFile, sMode = 'rb'):
444 """
445 Reads the entire file.
446 """
447 oFile = open(sFile, sMode);
448 sRet = oFile.read();
449 oFile.close();
450 return sRet;
451
452def noxcptReadFile(sFile, sXcptRet, sMode = 'rb', sEncoding = 'utf-8'):
453 """
454 No exceptions common.readFile wrapper.
455 """
456 try:
457 sRet = readFile(sFile, sMode);
458 except:
459 sRet = sXcptRet;
460 if sEncoding is not None and hasattr(sRet, 'decode'):
461 sRet = sRet.decode(sEncoding, 'ignore');
462 return sRet;
463
464def noxcptRmDir(sDir, oXcptRet = False):
465 """
466 No exceptions os.rmdir wrapper.
467 """
468 oRet = True;
469 try:
470 os.rmdir(sDir);
471 except:
472 oRet = oXcptRet;
473 return oRet;
474
475def noxcptDeleteFile(sFile, oXcptRet = False):
476 """
477 No exceptions os.remove wrapper.
478 """
479 oRet = True;
480 try:
481 os.remove(sFile);
482 except:
483 oRet = oXcptRet;
484 return oRet;
485
486
487def dirEnumerateTree(sDir, fnCallback, fIgnoreExceptions = True):
488 # type: (string, (string, stat) -> bool) -> bool
489 """
490 Recursively walks a directory tree, calling fnCallback for each.
491
492 fnCallback takes a full path and stat object (can be None). It
493 returns a boolean value, False stops walking and returns immediately.
494
495 Returns True or False depending on fnCallback.
496 Returns None fIgnoreExceptions is True and an exception was raised by listdir.
497 """
498 def __worker(sCurDir):
499 """ Worker for """
500 try:
501 asNames = os.listdir(sCurDir);
502 except:
503 if not fIgnoreExceptions:
504 raise;
505 return None;
506 rc = True;
507 for sName in asNames:
508 if sName not in [ '.', '..' ]:
509 sFullName = os.path.join(sCurDir, sName);
510 try: oStat = os.lstat(sFullName);
511 except: oStat = None;
512 if fnCallback(sFullName, oStat) is False:
513 return False;
514 if oStat is not None and stat.S_ISDIR(oStat.st_mode):
515 rc = __worker(sFullName);
516 if rc is False:
517 break;
518 return rc;
519
520 # Ensure unicode path here so listdir also returns unicode on windows.
521 ## @todo figure out unicode stuff on non-windows.
522 if sys.platform == 'win32':
523 sDir = unicode(sDir);
524 return __worker(sDir);
525
526
527
528def formatFileMode(uMode):
529 # type: (int) -> string
530 """
531 Format a st_mode value 'ls -la' fasion.
532 Returns string.
533 """
534 if stat.S_ISDIR(uMode): sMode = 'd';
535 elif stat.S_ISREG(uMode): sMode = '-';
536 elif stat.S_ISLNK(uMode): sMode = 'l';
537 elif stat.S_ISFIFO(uMode): sMode = 'p';
538 elif stat.S_ISCHR(uMode): sMode = 'c';
539 elif stat.S_ISBLK(uMode): sMode = 'b';
540 elif stat.S_ISSOCK(uMode): sMode = 's';
541 else: sMode = '?';
542 ## @todo sticky bits.
543 sMode += 'r' if uMode & stat.S_IRUSR else '-';
544 sMode += 'w' if uMode & stat.S_IWUSR else '-';
545 sMode += 'x' if uMode & stat.S_IXUSR else '-';
546 sMode += 'r' if uMode & stat.S_IRGRP else '-';
547 sMode += 'w' if uMode & stat.S_IWGRP else '-';
548 sMode += 'x' if uMode & stat.S_IXGRP else '-';
549 sMode += 'r' if uMode & stat.S_IROTH else '-';
550 sMode += 'w' if uMode & stat.S_IWOTH else '-';
551 sMode += 'x' if uMode & stat.S_IXOTH else '-';
552 sMode += ' ';
553 return sMode;
554
555
556def formatFileStat(oStat):
557 # type: (stat) -> string
558 """
559 Format a stat result 'ls -la' fasion (numeric IDs).
560 Returns string.
561 """
562 return '%s %3s %4s %4s %10s %s' \
563 % (formatFileMode(oStat.st_mode), oStat.st_nlink, oStat.st_uid, oStat.st_gid, oStat.st_size,
564 time.strftime('%Y-%m-%d %H:%M', time.localtime(oStat.st_mtime)), );
565
566## Good buffer for file operations.
567g_cbGoodBufferSize = 256*1024;
568
569## The original shutil.copyfileobj.
570g_fnOriginalShCopyFileObj = None;
571
572def __myshutilcopyfileobj(fsrc, fdst, length = g_cbGoodBufferSize):
573 """ shutil.copyfileobj with different length default value (16384 is slow with python 2.7 on windows). """
574 return g_fnOriginalShCopyFileObj(fsrc, fdst, length);
575
576def __installShUtilHacks(shutil):
577 """ Installs the shutil buffer size hacks. """
578 global g_fnOriginalShCopyFileObj;
579 if g_fnOriginalShCopyFileObj is None:
580 g_fnOriginalShCopyFileObj = shutil.copyfileobj;
581 shutil.copyfileobj = __myshutilcopyfileobj;
582 return True;
583
584
585def copyFileSimple(sFileSrc, sFileDst):
586 """
587 Wrapper around shutil.copyfile that simply copies the data of a regular file.
588 Raises exception on failure.
589 Return True for show.
590 """
591 import shutil;
592 __installShUtilHacks(shutil);
593 return shutil.copyfile(sFileSrc, sFileDst);
594
595
596def getDiskUsage(sPath):
597 """
598 Get free space of a partition that corresponds to specified sPath in MB.
599
600 Returns partition free space value in MB.
601 """
602 if platform.system() == 'Windows':
603 oCTypeFreeSpace = ctypes.c_ulonglong(0);
604 ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(sPath), None, None,
605 ctypes.pointer(oCTypeFreeSpace));
606 cbFreeSpace = oCTypeFreeSpace.value;
607 else:
608 oStats = os.statvfs(sPath); # pylint: disable=no-member
609 cbFreeSpace = long(oStats.f_frsize) * oStats.f_bfree;
610
611 # Convert to MB
612 cMbFreeSpace = long(cbFreeSpace) / (1024 * 1024);
613
614 return cMbFreeSpace;
615
616
617
618#
619# SubProcess.
620#
621
622def _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs):
623 """
624 If the "executable" is a python script, insert the python interpreter at
625 the head of the argument list so that it will work on systems which doesn't
626 support hash-bang scripts.
627 """
628
629 asArgs = dKeywordArgs.get('args');
630 if asArgs is None:
631 asArgs = aPositionalArgs[0];
632
633 if asArgs[0].endswith('.py'):
634 if sys.executable:
635 asArgs.insert(0, sys.executable);
636 else:
637 asArgs.insert(0, 'python');
638
639 # paranoia...
640 if dKeywordArgs.get('args') is not None:
641 dKeywordArgs['args'] = asArgs;
642 else:
643 aPositionalArgs = (asArgs,) + aPositionalArgs[1:];
644 return None;
645
646def processPopenSafe(*aPositionalArgs, **dKeywordArgs):
647 """
648 Wrapper for subprocess.Popen that's Ctrl-C safe on windows.
649 """
650 if getHostOs() == 'win':
651 if dKeywordArgs.get('creationflags', 0) == 0:
652 dKeywordArgs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP;
653 return subprocess.Popen(*aPositionalArgs, **dKeywordArgs);
654
655def processCall(*aPositionalArgs, **dKeywordArgs):
656 """
657 Wrapper around subprocess.call to deal with its absence in older
658 python versions.
659 Returns process exit code (see subprocess.poll).
660 """
661 assert dKeywordArgs.get('stdout') is None;
662 assert dKeywordArgs.get('stderr') is None;
663 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
664 oProcess = processPopenSafe(*aPositionalArgs, **dKeywordArgs);
665 return oProcess.wait();
666
667def processOutputChecked(*aPositionalArgs, **dKeywordArgs):
668 """
669 Wrapper around subprocess.check_output to deal with its absense in older
670 python versions.
671
672 Extra keywords for specifying now output is to be decoded:
673 sEncoding='utf-8
674 fIgnoreEncoding=True/False
675 """
676 sEncoding = dKeywordArgs.get('sEncoding');
677 if sEncoding is not None: del dKeywordArgs['sEncoding'];
678 else: sEncoding = 'utf-8';
679
680 fIgnoreEncoding = dKeywordArgs.get('fIgnoreEncoding');
681 if fIgnoreEncoding is not None: del dKeywordArgs['fIgnoreEncoding'];
682 else: fIgnoreEncoding = True;
683
684 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
685 oProcess = processPopenSafe(stdout=subprocess.PIPE, *aPositionalArgs, **dKeywordArgs);
686
687 sOutput, _ = oProcess.communicate();
688 iExitCode = oProcess.poll();
689
690 if iExitCode != 0:
691 asArgs = dKeywordArgs.get('args');
692 if asArgs is None:
693 asArgs = aPositionalArgs[0];
694 print(sOutput);
695 raise subprocess.CalledProcessError(iExitCode, asArgs);
696
697 if hasattr(sOutput, 'decode'):
698 sOutput = sOutput.decode(sEncoding, 'ignore' if fIgnoreEncoding else 'strict');
699 return sOutput;
700
701g_fOldSudo = None;
702def _sudoFixArguments(aPositionalArgs, dKeywordArgs, fInitialEnv = True):
703 """
704 Adds 'sudo' (or similar) to the args parameter, whereever it is.
705 """
706
707 # Are we root?
708 fIsRoot = True;
709 try:
710 fIsRoot = os.getuid() == 0; # pylint: disable=no-member
711 except:
712 pass;
713
714 # If not, prepend sudo (non-interactive, simulate initial login).
715 if fIsRoot is not True:
716 asArgs = dKeywordArgs.get('args');
717 if asArgs is None:
718 asArgs = aPositionalArgs[0];
719
720 # Detect old sudo.
721 global g_fOldSudo;
722 if g_fOldSudo is None:
723 try:
724 sVersion = str(processOutputChecked(['sudo', '-V']));
725 except:
726 sVersion = '1.7.0';
727 sVersion = sVersion.strip().split('\n')[0];
728 sVersion = sVersion.replace('Sudo version', '').strip();
729 g_fOldSudo = len(sVersion) >= 4 \
730 and sVersion[0] == '1' \
731 and sVersion[1] == '.' \
732 and sVersion[2] <= '6' \
733 and sVersion[3] == '.';
734
735 asArgs.insert(0, 'sudo');
736 if not g_fOldSudo:
737 asArgs.insert(1, '-n');
738 if fInitialEnv and not g_fOldSudo:
739 asArgs.insert(1, '-i');
740
741 # paranoia...
742 if dKeywordArgs.get('args') is not None:
743 dKeywordArgs['args'] = asArgs;
744 else:
745 aPositionalArgs = (asArgs,) + aPositionalArgs[1:];
746 return None;
747
748
749def sudoProcessCall(*aPositionalArgs, **dKeywordArgs):
750 """
751 sudo (or similar) + subprocess.call
752 """
753 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
754 _sudoFixArguments(aPositionalArgs, dKeywordArgs);
755 return processCall(*aPositionalArgs, **dKeywordArgs);
756
757def sudoProcessOutputChecked(*aPositionalArgs, **dKeywordArgs):
758 """
759 sudo (or similar) + subprocess.check_output.
760 """
761 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
762 _sudoFixArguments(aPositionalArgs, dKeywordArgs);
763 return processOutputChecked(*aPositionalArgs, **dKeywordArgs);
764
765def sudoProcessOutputCheckedNoI(*aPositionalArgs, **dKeywordArgs):
766 """
767 sudo (or similar) + subprocess.check_output, except '-i' isn't used.
768 """
769 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
770 _sudoFixArguments(aPositionalArgs, dKeywordArgs, False);
771 return processOutputChecked(*aPositionalArgs, **dKeywordArgs);
772
773def sudoProcessPopen(*aPositionalArgs, **dKeywordArgs):
774 """
775 sudo (or similar) + processPopenSafe.
776 """
777 _processFixPythonInterpreter(aPositionalArgs, dKeywordArgs);
778 _sudoFixArguments(aPositionalArgs, dKeywordArgs);
779 return processPopenSafe(*aPositionalArgs, **dKeywordArgs);
780
781
782#
783# Generic process stuff.
784#
785
786def processInterrupt(uPid):
787 """
788 Sends a SIGINT or equivalent to interrupt the specified process.
789 Returns True on success, False on failure.
790
791 On Windows hosts this may not work unless the process happens to be a
792 process group leader.
793 """
794 if sys.platform == 'win32':
795 try:
796 win32console.GenerateConsoleCtrlEvent(win32con.CTRL_BREAK_EVENT, # pylint: disable=no-member,c-extension-no-member
797 uPid);
798 fRc = True;
799 except:
800 fRc = False;
801 else:
802 try:
803 os.kill(uPid, signal.SIGINT);
804 fRc = True;
805 except:
806 fRc = False;
807 return fRc;
808
809def sendUserSignal1(uPid):
810 """
811 Sends a SIGUSR1 or equivalent to nudge the process into shutting down
812 (VBoxSVC) or something.
813 Returns True on success, False on failure or if not supported (win).
814
815 On Windows hosts this may not work unless the process happens to be a
816 process group leader.
817 """
818 if sys.platform == 'win32':
819 fRc = False;
820 else:
821 try:
822 os.kill(uPid, signal.SIGUSR1); # pylint: disable=no-member
823 fRc = True;
824 except:
825 fRc = False;
826 return fRc;
827
828def processTerminate(uPid):
829 """
830 Terminates the process in a nice manner (SIGTERM or equivalent).
831 Returns True on success, False on failure.
832 """
833 fRc = False;
834 if sys.platform == 'win32':
835 try:
836 hProcess = win32api.OpenProcess(win32con.PROCESS_TERMINATE, # pylint: disable=no-member,c-extension-no-member
837 False, uPid);
838 except:
839 pass;
840 else:
841 try:
842 win32process.TerminateProcess(hProcess, # pylint: disable=no-member,c-extension-no-member
843 0x40010004); # DBG_TERMINATE_PROCESS
844 fRc = True;
845 except:
846 pass;
847 hProcess.Close(); #win32api.CloseHandle(hProcess)
848 else:
849 try:
850 os.kill(uPid, signal.SIGTERM);
851 fRc = True;
852 except:
853 pass;
854 return fRc;
855
856def processKill(uPid):
857 """
858 Terminates the process with extreme prejudice (SIGKILL).
859 Returns True on success, False on failure.
860 """
861 if sys.platform == 'win32':
862 fRc = processTerminate(uPid);
863 else:
864 try:
865 os.kill(uPid, signal.SIGKILL); # pylint: disable=no-member
866 fRc = True;
867 except:
868 fRc = False;
869 return fRc;
870
871def processKillWithNameCheck(uPid, sName):
872 """
873 Like processKill(), but checks if the process name matches before killing
874 it. This is intended for killing using potentially stale pid values.
875
876 Returns True on success, False on failure.
877 """
878
879 if processCheckPidAndName(uPid, sName) is not True:
880 return False;
881 return processKill(uPid);
882
883
884def processExists(uPid):
885 """
886 Checks if the specified process exits.
887 This will only work if we can signal/open the process.
888
889 Returns True if it positively exists, False otherwise.
890 """
891 if sys.platform == 'win32':
892 fRc = False;
893 # We try open the process for waiting since this is generally only forbidden in a very few cases.
894 try:
895 hProcess = win32api.OpenProcess(win32con.SYNCHRONIZE, # pylint: disable=no-member,c-extension-no-member
896 False, uPid);
897 except pywintypes.error as oXcpt: # pylint: disable=no-member
898 if oXcpt.winerror == winerror.ERROR_ACCESS_DENIED:
899 fRc = True;
900 except Exception as oXcpt:
901 pass;
902 else:
903 hProcess.Close();
904 fRc = True;
905 else:
906 try:
907 os.kill(uPid, 0);
908 fRc = True;
909 except: ## @todo check error code.
910 fRc = False;
911 return fRc;
912
913def processCheckPidAndName(uPid, sName):
914 """
915 Checks if a process PID and NAME matches.
916 """
917 fRc = processExists(uPid);
918 if fRc is not True:
919 return False;
920
921 if sys.platform == 'win32':
922 try:
923 from win32com.client import GetObject; # pylint: disable=import-error
924 oWmi = GetObject('winmgmts:');
925 aoProcesses = oWmi.InstancesOf('Win32_Process');
926 for oProcess in aoProcesses:
927 if long(oProcess.Properties_("ProcessId").Value) == uPid:
928 sCurName = oProcess.Properties_("Name").Value;
929 #reporter.log2('uPid=%s sName=%s sCurName=%s' % (uPid, sName, sCurName));
930 sName = sName.lower();
931 sCurName = sCurName.lower();
932 if os.path.basename(sName) == sName:
933 sCurName = os.path.basename(sCurName);
934
935 if sCurName == sName \
936 or sCurName + '.exe' == sName \
937 or sCurName == sName + '.exe':
938 fRc = True;
939 break;
940 except:
941 #reporter.logXcpt('uPid=%s sName=%s' % (uPid, sName));
942 pass;
943 else:
944 if sys.platform in ('linux2', 'linux', 'linux3', 'linux4', 'linux5', 'linux6'):
945 asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
946 elif sys.platform in ('sunos5',):
947 asPsCmd = ['/usr/bin/ps', '-p', '%u' % (uPid,), '-o', 'fname='];
948 elif sys.platform in ('darwin',):
949 asPsCmd = ['/bin/ps', '-p', '%u' % (uPid,), '-o', 'ucomm='];
950 else:
951 asPsCmd = None;
952
953 if asPsCmd is not None:
954 try:
955 oPs = subprocess.Popen(asPsCmd, stdout=subprocess.PIPE);
956 sCurName = oPs.communicate()[0];
957 iExitCode = oPs.wait();
958 except:
959 #reporter.logXcpt();
960 return False;
961
962 # ps fails with non-zero exit code if the pid wasn't found.
963 if iExitCode != 0:
964 return False;
965 if sCurName is None:
966 return False;
967 sCurName = sCurName.strip();
968 if not sCurName:
969 return False;
970
971 if os.path.basename(sName) == sName:
972 sCurName = os.path.basename(sCurName);
973 elif os.path.basename(sCurName) == sCurName:
974 sName = os.path.basename(sName);
975
976 if sCurName != sName:
977 return False;
978
979 fRc = True;
980 return fRc;
981
982def processGetInfo(uPid, fSudo = False):
983 """
984 Tries to acquire state information of the given process.
985
986 Returns a string with the information on success or None on failure or
987 if the host is not supported.
988
989 Note that the format of the information is host system dependent and will
990 likely differ much between different hosts.
991 """
992 fRc = processExists(uPid);
993 if fRc is not True:
994 return None;
995
996 sHostOs = getHostOs();
997 if sHostOs in [ 'linux',]:
998 sGdb = '/usr/bin/gdb';
999 if not os.path.isfile(sGdb): sGdb = '/usr/local/bin/gdb';
1000 if not os.path.isfile(sGdb): sGdb = 'gdb';
1001 aasCmd = [
1002 [ sGdb, '-batch',
1003 '-ex', 'set pagination off',
1004 '-ex', 'thread apply all bt',
1005 '-ex', 'info proc mapping',
1006 '-ex', 'info sharedlibrary',
1007 '-p', '%u' % (uPid,), ],
1008 ];
1009 elif sHostOs == 'darwin':
1010 # LLDB doesn't work in batch mode when attaching to a process, at least
1011 # with macOS Sierra (10.12). GDB might not be installed. Use the sample
1012 # tool instead with a 1 second duration and 1000ms sampling interval to
1013 # get one stack trace. For the process mappings use vmmap.
1014 aasCmd = [
1015 [ '/usr/bin/sample', '-mayDie', '%u' % (uPid,), '1', '1000', ],
1016 [ '/usr/bin/vmmap', '%u' % (uPid,), ],
1017 ];
1018 elif sHostOs == 'solaris':
1019 aasCmd = [
1020 [ '/usr/bin/pstack', '%u' % (uPid,), ],
1021 [ '/usr/bin/pmap', '%u' % (uPid,), ],
1022 ];
1023 else:
1024 aasCmd = [];
1025
1026 sInfo = '';
1027 for asCmd in aasCmd:
1028 try:
1029 if fSudo:
1030 sThisInfo = sudoProcessOutputChecked(asCmd);
1031 else:
1032 sThisInfo = processOutputChecked(asCmd);
1033 if sThisInfo is not None:
1034 sInfo += sThisInfo;
1035 except:
1036 pass;
1037 if not sInfo:
1038 sInfo = None;
1039
1040 return sInfo;
1041
1042
1043class ProcessInfo(object):
1044 """Process info."""
1045 def __init__(self, iPid):
1046 self.iPid = iPid;
1047 self.iParentPid = None;
1048 self.sImage = None;
1049 self.sName = None;
1050 self.asArgs = None;
1051 self.sCwd = None;
1052 self.iGid = None;
1053 self.iUid = None;
1054 self.iProcGroup = None;
1055 self.iSessionId = None;
1056
1057 def loadAll(self):
1058 """Load all the info."""
1059 sOs = getHostOs();
1060 if sOs == 'linux':
1061 sProc = '/proc/%s/' % (self.iPid,);
1062 if self.sImage is None: self.sImage = noxcptReadLink(sProc + 'exe', None);
1063 if self.sCwd is None: self.sCwd = noxcptReadLink(sProc + 'cwd', None);
1064 if self.asArgs is None: self.asArgs = noxcptReadFile(sProc + 'cmdline', '').split('\x00');
1065 #elif sOs == 'solaris': - doesn't work for root processes, suid proces, and other stuff.
1066 # sProc = '/proc/%s/' % (self.iPid,);
1067 # if self.sImage is None: self.sImage = noxcptReadLink(sProc + 'path/a.out', None);
1068 # if self.sCwd is None: self.sCwd = noxcptReadLink(sProc + 'path/cwd', None);
1069 else:
1070 pass;
1071 if self.sName is None and self.sImage is not None:
1072 self.sName = self.sImage;
1073
1074 def windowsGrabProcessInfo(self, oProcess):
1075 """Windows specific loadAll."""
1076 try: self.sName = oProcess.Properties_("Name").Value;
1077 except: pass;
1078 try: self.sImage = oProcess.Properties_("ExecutablePath").Value;
1079 except: pass;
1080 try: self.asArgs = [oProcess.Properties_("CommandLine").Value]; ## @todo split it.
1081 except: pass;
1082 try: self.iParentPid = oProcess.Properties_("ParentProcessId").Value;
1083 except: pass;
1084 try: self.iSessionId = oProcess.Properties_("SessionId").Value;
1085 except: pass;
1086 if self.sName is None and self.sImage is not None:
1087 self.sName = self.sImage;
1088
1089 def getBaseImageName(self):
1090 """
1091 Gets the base image name if available, use the process name if not available.
1092 Returns image/process base name or None.
1093 """
1094 sRet = self.sImage if self.sName is None else self.sName;
1095 if sRet is None:
1096 self.loadAll();
1097 sRet = self.sImage if self.sName is None else self.sName;
1098 if sRet is None:
1099 if not self.asArgs:
1100 return None;
1101 sRet = self.asArgs[0];
1102 if not sRet:
1103 return None;
1104 return os.path.basename(sRet);
1105
1106 def getBaseImageNameNoExeSuff(self):
1107 """
1108 Same as getBaseImageName, except any '.exe' or similar suffix is stripped.
1109 """
1110 sRet = self.getBaseImageName();
1111 if sRet is not None and len(sRet) > 4 and sRet[-4] == '.':
1112 if (sRet[-4:]).lower() in [ '.exe', '.com', '.msc', '.vbs', '.cmd', '.bat' ]:
1113 sRet = sRet[:-4];
1114 return sRet;
1115
1116
1117def processListAll():
1118 """
1119 Return a list of ProcessInfo objects for all the processes in the system
1120 that the current user can see.
1121 """
1122 asProcesses = [];
1123
1124 sOs = getHostOs();
1125 if sOs == 'win':
1126 from win32com.client import GetObject; # pylint: disable=import-error
1127 oWmi = GetObject('winmgmts:');
1128 aoProcesses = oWmi.InstancesOf('Win32_Process');
1129 for oProcess in aoProcesses:
1130 try:
1131 iPid = int(oProcess.Properties_("ProcessId").Value);
1132 except:
1133 continue;
1134 oMyInfo = ProcessInfo(iPid);
1135 oMyInfo.windowsGrabProcessInfo(oProcess);
1136 asProcesses.append(oMyInfo);
1137 return asProcesses;
1138
1139 if sOs in [ 'linux', ]: # Not solaris, ps gets more info than /proc/.
1140 try:
1141 asDirs = os.listdir('/proc');
1142 except:
1143 asDirs = [];
1144 for sDir in asDirs:
1145 if sDir.isdigit():
1146 asProcesses.append(ProcessInfo(int(sDir),));
1147 return asProcesses;
1148
1149 #
1150 # The other OSes parses the output from the 'ps' utility.
1151 #
1152 asPsCmd = [
1153 '/bin/ps', # 0
1154 '-A', # 1
1155 '-o', 'pid=', # 2,3
1156 '-o', 'ppid=', # 4,5
1157 '-o', 'pgid=', # 6,7
1158 '-o', 'sid=', # 8,9
1159 '-o', 'uid=', # 10,11
1160 '-o', 'gid=', # 12,13
1161 '-o', 'comm=' # 14,15
1162 ];
1163
1164 if sOs == 'darwin':
1165 assert asPsCmd[9] == 'sid=';
1166 asPsCmd[9] = 'sess=';
1167 elif sOs == 'solaris':
1168 asPsCmd[0] = '/usr/bin/ps';
1169
1170 try:
1171 sRaw = processOutputChecked(asPsCmd);
1172 except:
1173 return asProcesses;
1174
1175 for sLine in sRaw.split('\n'):
1176 sLine = sLine.lstrip();
1177 if len(sLine) < 7 or not sLine[0].isdigit():
1178 continue;
1179
1180 iField = 0;
1181 off = 0;
1182 aoFields = [None, None, None, None, None, None, None];
1183 while iField < 7:
1184 # Eat whitespace.
1185 while off < len(sLine) and (sLine[off] == ' ' or sLine[off] == '\t'):
1186 off += 1;
1187
1188 # Final field / EOL.
1189 if iField == 6:
1190 aoFields[6] = sLine[off:];
1191 break;
1192 if off >= len(sLine):
1193 break;
1194
1195 # Generic field parsing.
1196 offStart = off;
1197 off += 1;
1198 while off < len(sLine) and sLine[off] != ' ' and sLine[off] != '\t':
1199 off += 1;
1200 try:
1201 if iField != 3:
1202 aoFields[iField] = int(sLine[offStart:off]);
1203 else:
1204 aoFields[iField] = long(sLine[offStart:off], 16); # sess is a hex address.
1205 except:
1206 pass;
1207 iField += 1;
1208
1209 if aoFields[0] is not None:
1210 oMyInfo = ProcessInfo(aoFields[0]);
1211 oMyInfo.iParentPid = aoFields[1];
1212 oMyInfo.iProcGroup = aoFields[2];
1213 oMyInfo.iSessionId = aoFields[3];
1214 oMyInfo.iUid = aoFields[4];
1215 oMyInfo.iGid = aoFields[5];
1216 oMyInfo.sName = aoFields[6];
1217 asProcesses.append(oMyInfo);
1218
1219 return asProcesses;
1220
1221
1222def processCollectCrashInfo(uPid, fnLog, fnCrashFile):
1223 """
1224 Looks for information regarding the demise of the given process.
1225 """
1226 sOs = getHostOs();
1227 if sOs == 'darwin':
1228 #
1229 # On darwin we look for crash and diagnostic reports.
1230 #
1231 asLogDirs = [
1232 u'/Library/Logs/DiagnosticReports/',
1233 u'/Library/Logs/CrashReporter/',
1234 u'~/Library/Logs/DiagnosticReports/',
1235 u'~/Library/Logs/CrashReporter/',
1236 ];
1237 for sDir in asLogDirs:
1238 sDir = os.path.expanduser(sDir);
1239 if not os.path.isdir(sDir):
1240 continue;
1241 try:
1242 asDirEntries = os.listdir(sDir);
1243 except:
1244 continue;
1245 for sEntry in asDirEntries:
1246 # Only interested in .crash files.
1247 _, sSuff = os.path.splitext(sEntry);
1248 if sSuff != '.crash':
1249 continue;
1250
1251 # The pid can be found at the end of the first line.
1252 sFull = os.path.join(sDir, sEntry);
1253 try:
1254 oFile = open(sFull, 'r');
1255 sFirstLine = oFile.readline();
1256 oFile.close();
1257 except:
1258 continue;
1259 if len(sFirstLine) <= 4 or sFirstLine[-2] != ']':
1260 continue;
1261 offPid = len(sFirstLine) - 3;
1262 while offPid > 1 and sFirstLine[offPid - 1].isdigit():
1263 offPid -= 1;
1264 try: uReportPid = int(sFirstLine[offPid:-2]);
1265 except: continue;
1266
1267 # Does the pid we found match?
1268 if uReportPid == uPid:
1269 fnLog('Found crash report for %u: %s' % (uPid, sFull,));
1270 fnCrashFile(sFull, False);
1271 elif sOs == 'win':
1272 #
1273 # Getting WER reports would be great, however we have trouble match the
1274 # PID to those as they seems not to mention it in the brief reports.
1275 # Instead we'll just look for crash dumps in C:\CrashDumps (our custom
1276 # location - see the windows readme for the testbox script) and what
1277 # the MSDN article lists for now.
1278 #
1279 # It's been observed on Windows server 2012 that the dump files takes
1280 # the form: <processimage>.<decimal-pid>.dmp
1281 #
1282 asDmpDirs = [
1283 u'%SystemDrive%/CrashDumps/', # Testboxes.
1284 u'%LOCALAPPDATA%/CrashDumps/', # MSDN example.
1285 u'%WINDIR%/ServiceProfiles/LocalServices/', # Local and network service.
1286 u'%WINDIR%/ServiceProfiles/NetworkSerices/',
1287 u'%WINDIR%/ServiceProfiles/',
1288 u'%WINDIR%/System32/Config/SystemProfile/', # System services.
1289 ];
1290 sMatchSuffix = '.%u.dmp' % (uPid,);
1291
1292 for sDir in asDmpDirs:
1293 sDir = os.path.expandvars(sDir);
1294 if not os.path.isdir(sDir):
1295 continue;
1296 try:
1297 asDirEntries = os.listdir(sDir);
1298 except:
1299 continue;
1300 for sEntry in asDirEntries:
1301 if sEntry.endswith(sMatchSuffix):
1302 sFull = os.path.join(sDir, sEntry);
1303 fnLog('Found crash dump for %u: %s' % (uPid, sFull,));
1304 fnCrashFile(sFull, True);
1305
1306 else:
1307 pass; ## TODO
1308 return None;
1309
1310
1311#
1312# Time.
1313#
1314
1315#
1316# The following test case shows how time.time() only have ~ms resolution
1317# on Windows (tested W10) and why it therefore makes sense to try use
1318# performance counters.
1319#
1320# Note! We cannot use time.clock() as the timestamp must be portable across
1321# processes. See timeout testcase problem on win hosts (no logs).
1322#
1323#import sys;
1324#import time;
1325#from common import utils;
1326#
1327#atSeries = [];
1328#for i in xrange(1,160):
1329# if i == 159: time.sleep(10);
1330# atSeries.append((utils.timestampNano(), long(time.clock() * 1000000000), long(time.time() * 1000000000)));
1331#
1332#tPrev = atSeries[0]
1333#for tCur in atSeries:
1334# print 't1=%+22u, %u' % (tCur[0], tCur[0] - tPrev[0]);
1335# print 't2=%+22u, %u' % (tCur[1], tCur[1] - tPrev[1]);
1336# print 't3=%+22u, %u' % (tCur[2], tCur[2] - tPrev[2]);
1337# print '';
1338# tPrev = tCur
1339#
1340#print 't1=%u' % (atSeries[-1][0] - atSeries[0][0]);
1341#print 't2=%u' % (atSeries[-1][1] - atSeries[0][1]);
1342#print 't3=%u' % (atSeries[-1][2] - atSeries[0][2]);
1343
1344g_fWinUseWinPerfCounter = sys.platform == 'win32';
1345g_fpWinPerfCounterFreq = None;
1346g_oFuncwinQueryPerformanceCounter = None;
1347
1348def _winInitPerfCounter():
1349 """ Initializes the use of performance counters. """
1350 global g_fWinUseWinPerfCounter, g_fpWinPerfCounterFreq, g_oFuncwinQueryPerformanceCounter
1351
1352 uFrequency = ctypes.c_ulonglong(0);
1353 if ctypes.windll.kernel32.QueryPerformanceFrequency(ctypes.byref(uFrequency)):
1354 if uFrequency.value >= 1000:
1355 #print 'uFrequency = %s' % (uFrequency,);
1356 #print 'type(uFrequency) = %s' % (type(uFrequency),);
1357 g_fpWinPerfCounterFreq = float(uFrequency.value);
1358
1359 # Check that querying the counter works too.
1360 global g_oFuncwinQueryPerformanceCounter
1361 g_oFuncwinQueryPerformanceCounter = ctypes.windll.kernel32.QueryPerformanceCounter;
1362 uCurValue = ctypes.c_ulonglong(0);
1363 if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
1364 if uCurValue.value > 0:
1365 return True;
1366 g_fWinUseWinPerfCounter = False;
1367 return False;
1368
1369def _winFloatTime():
1370 """ Gets floating point time on windows. """
1371 if g_fpWinPerfCounterFreq is not None or _winInitPerfCounter():
1372 uCurValue = ctypes.c_ulonglong(0);
1373 if g_oFuncwinQueryPerformanceCounter(ctypes.byref(uCurValue)):
1374 return float(uCurValue.value) / g_fpWinPerfCounterFreq;
1375 return time.time();
1376
1377def timestampNano():
1378 """
1379 Gets a nanosecond timestamp.
1380 """
1381 if g_fWinUseWinPerfCounter is True:
1382 return long(_winFloatTime() * 1000000000);
1383 return long(time.time() * 1000000000);
1384
1385def timestampMilli():
1386 """
1387 Gets a millisecond timestamp.
1388 """
1389 if g_fWinUseWinPerfCounter is True:
1390 return long(_winFloatTime() * 1000);
1391 return long(time.time() * 1000);
1392
1393def timestampSecond():
1394 """
1395 Gets a second timestamp.
1396 """
1397 if g_fWinUseWinPerfCounter is True:
1398 return long(_winFloatTime());
1399 return long(time.time());
1400
1401def secondsSinceUnixEpoch():
1402 """
1403 Returns unix time, floating point second count since 1970-01-01T00:00:00Z
1404 """
1405 ## ASSUMES This returns unix epoch time on all systems we care about...
1406 return time.time();
1407
1408def getTimePrefix():
1409 """
1410 Returns a timestamp prefix, typically used for logging. UTC.
1411 """
1412 try:
1413 oNow = datetime.datetime.utcnow();
1414 sTs = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
1415 except:
1416 sTs = 'getTimePrefix-exception';
1417 return sTs;
1418
1419def getTimePrefixAndIsoTimestamp():
1420 """
1421 Returns current UTC as log prefix and iso timestamp.
1422 """
1423 try:
1424 oNow = datetime.datetime.utcnow();
1425 sTsPrf = '%02u:%02u:%02u.%06u' % (oNow.hour, oNow.minute, oNow.second, oNow.microsecond);
1426 sTsIso = formatIsoTimestamp(oNow);
1427 except:
1428 sTsPrf = sTsIso = 'getTimePrefix-exception';
1429 return (sTsPrf, sTsIso);
1430
1431def formatIsoTimestamp(oNow):
1432 """Formats the datetime object as an ISO timestamp."""
1433 assert oNow.tzinfo is None;
1434 sTs = '%s.%09uZ' % (oNow.strftime('%Y-%m-%dT%H:%M:%S'), oNow.microsecond * 1000);
1435 return sTs;
1436
1437def getIsoTimestamp():
1438 """Returns the current UTC timestamp as a string."""
1439 return formatIsoTimestamp(datetime.datetime.utcnow());
1440
1441
1442def getLocalHourOfWeek():
1443 """ Local hour of week (0 based). """
1444 oNow = datetime.datetime.now();
1445 return (oNow.isoweekday() - 1) * 24 + oNow.hour;
1446
1447
1448def formatIntervalSeconds(cSeconds):
1449 """ Format a seconds interval into a nice 01h 00m 22s string """
1450 # Two simple special cases.
1451 if cSeconds < 60:
1452 return '%ss' % (cSeconds,);
1453 if cSeconds < 3600:
1454 cMins = cSeconds // 60;
1455 cSecs = cSeconds % 60;
1456 if cSecs == 0:
1457 return '%sm' % (cMins,);
1458 return '%sm %ss' % (cMins, cSecs,);
1459
1460 # Generic and a bit slower.
1461 cDays = cSeconds // 86400;
1462 cSeconds %= 86400;
1463 cHours = cSeconds // 3600;
1464 cSeconds %= 3600;
1465 cMins = cSeconds // 60;
1466 cSecs = cSeconds % 60;
1467 sRet = '';
1468 if cDays > 0:
1469 sRet = '%sd ' % (cDays,);
1470 if cHours > 0:
1471 sRet += '%sh ' % (cHours,);
1472 if cMins > 0:
1473 sRet += '%sm ' % (cMins,);
1474 if cSecs > 0:
1475 sRet += '%ss ' % (cSecs,);
1476 assert sRet; assert sRet[-1] == ' ';
1477 return sRet[:-1];
1478
1479def formatIntervalSeconds2(oSeconds):
1480 """
1481 Flexible input version of formatIntervalSeconds for use in WUI forms where
1482 data is usually already string form.
1483 """
1484 if isinstance(oSeconds, (int, long)):
1485 return formatIntervalSeconds(oSeconds);
1486 if not isString(oSeconds):
1487 try:
1488 lSeconds = long(oSeconds);
1489 except:
1490 pass;
1491 else:
1492 if lSeconds >= 0:
1493 return formatIntervalSeconds2(lSeconds);
1494 return oSeconds;
1495
1496def parseIntervalSeconds(sString):
1497 """
1498 Reverse of formatIntervalSeconds.
1499
1500 Returns (cSeconds, sError), where sError is None on success.
1501 """
1502
1503 # We might given non-strings, just return them without any fuss.
1504 if not isString(sString):
1505 if isinstance(sString, (int, long)) or sString is None:
1506 return (sString, None);
1507 ## @todo time/date objects?
1508 return (int(sString), None);
1509
1510 # Strip it and make sure it's not empty.
1511 sString = sString.strip();
1512 if not sString:
1513 return (0, 'Empty interval string.');
1514
1515 #
1516 # Split up the input into a list of 'valueN, unitN, ...'.
1517 #
1518 # Don't want to spend too much time trying to make re.split do exactly what
1519 # I need here, so please forgive the extra pass I'm making here.
1520 #
1521 asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
1522 asParts = [];
1523 for sPart in asRawParts:
1524 sPart = sPart.strip();
1525 if sPart:
1526 asParts.append(sPart);
1527 if not asParts:
1528 return (0, 'Empty interval string or something?');
1529
1530 #
1531 # Process them one or two at the time.
1532 #
1533 cSeconds = 0;
1534 asErrors = [];
1535 i = 0;
1536 while i < len(asParts):
1537 sNumber = asParts[i];
1538 i += 1;
1539 if sNumber.isdigit():
1540 iNumber = int(sNumber);
1541
1542 sUnit = 's';
1543 if i < len(asParts) and not asParts[i].isdigit():
1544 sUnit = asParts[i];
1545 i += 1;
1546
1547 sUnitLower = sUnit.lower();
1548 if sUnitLower in [ 's', 'se', 'sec', 'second', 'seconds' ]:
1549 pass;
1550 elif sUnitLower in [ 'm', 'mi', 'min', 'minute', 'minutes' ]:
1551 iNumber *= 60;
1552 elif sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
1553 iNumber *= 3600;
1554 elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
1555 iNumber *= 86400;
1556 elif sUnitLower in [ 'w', 'week', 'weeks' ]:
1557 iNumber *= 7 * 86400;
1558 else:
1559 asErrors.append('Unknown unit "%s".' % (sUnit,));
1560 cSeconds += iNumber;
1561 else:
1562 asErrors.append('Bad number "%s".' % (sNumber,));
1563 return (cSeconds, None if not asErrors else ' '.join(asErrors));
1564
1565def formatIntervalHours(cHours):
1566 """ Format a hours interval into a nice 1w 2d 1h string. """
1567 # Simple special cases.
1568 if cHours < 24:
1569 return '%sh' % (cHours,);
1570
1571 # Generic and a bit slower.
1572 cWeeks = cHours / (7 * 24);
1573 cHours %= 7 * 24;
1574 cDays = cHours / 24;
1575 cHours %= 24;
1576 sRet = '';
1577 if cWeeks > 0:
1578 sRet = '%sw ' % (cWeeks,);
1579 if cDays > 0:
1580 sRet = '%sd ' % (cDays,);
1581 if cHours > 0:
1582 sRet += '%sh ' % (cHours,);
1583 assert sRet; assert sRet[-1] == ' ';
1584 return sRet[:-1];
1585
1586def parseIntervalHours(sString):
1587 """
1588 Reverse of formatIntervalHours.
1589
1590 Returns (cHours, sError), where sError is None on success.
1591 """
1592
1593 # We might given non-strings, just return them without any fuss.
1594 if not isString(sString):
1595 if isinstance(sString, (int, long)) or sString is None:
1596 return (sString, None);
1597 ## @todo time/date objects?
1598 return (int(sString), None);
1599
1600 # Strip it and make sure it's not empty.
1601 sString = sString.strip();
1602 if not sString:
1603 return (0, 'Empty interval string.');
1604
1605 #
1606 # Split up the input into a list of 'valueN, unitN, ...'.
1607 #
1608 # Don't want to spend too much time trying to make re.split do exactly what
1609 # I need here, so please forgive the extra pass I'm making here.
1610 #
1611 asRawParts = re.split(r'\s*([0-9]+)\s*([^0-9,;]*)[\s,;]*', sString);
1612 asParts = [];
1613 for sPart in asRawParts:
1614 sPart = sPart.strip();
1615 if sPart:
1616 asParts.append(sPart);
1617 if not asParts:
1618 return (0, 'Empty interval string or something?');
1619
1620 #
1621 # Process them one or two at the time.
1622 #
1623 cHours = 0;
1624 asErrors = [];
1625 i = 0;
1626 while i < len(asParts):
1627 sNumber = asParts[i];
1628 i += 1;
1629 if sNumber.isdigit():
1630 iNumber = int(sNumber);
1631
1632 sUnit = 'h';
1633 if i < len(asParts) and not asParts[i].isdigit():
1634 sUnit = asParts[i];
1635 i += 1;
1636
1637 sUnitLower = sUnit.lower();
1638 if sUnitLower in [ 'h', 'ho', 'hou', 'hour', 'hours' ]:
1639 pass;
1640 elif sUnitLower in [ 'd', 'da', 'day', 'days' ]:
1641 iNumber *= 24;
1642 elif sUnitLower in [ 'w', 'week', 'weeks' ]:
1643 iNumber *= 7 * 24;
1644 else:
1645 asErrors.append('Unknown unit "%s".' % (sUnit,));
1646 cHours += iNumber;
1647 else:
1648 asErrors.append('Bad number "%s".' % (sNumber,));
1649 return (cHours, None if not asErrors else ' '.join(asErrors));
1650
1651
1652#
1653# Introspection.
1654#
1655
1656def getCallerName(oFrame=None, iFrame=2):
1657 """
1658 Returns the name of the caller's caller.
1659 """
1660 if oFrame is None:
1661 try:
1662 raise Exception();
1663 except:
1664 oFrame = sys.exc_info()[2].tb_frame.f_back;
1665 while iFrame > 1:
1666 if oFrame is not None:
1667 oFrame = oFrame.f_back;
1668 iFrame = iFrame - 1;
1669 if oFrame is not None:
1670 sName = '%s:%u' % (oFrame.f_code.co_name, oFrame.f_lineno);
1671 return sName;
1672 return "unknown";
1673
1674
1675def getXcptInfo(cFrames = 1):
1676 """
1677 Gets text detailing the exception. (Good for logging.)
1678 Returns list of info strings.
1679 """
1680
1681 #
1682 # Try get exception info.
1683 #
1684 try:
1685 oType, oValue, oTraceback = sys.exc_info();
1686 except:
1687 oType = oValue = oTraceback = None;
1688 if oType is not None:
1689
1690 #
1691 # Try format the info
1692 #
1693 asRet = [];
1694 try:
1695 try:
1696 asRet = asRet + traceback.format_exception_only(oType, oValue);
1697 asTraceBack = traceback.format_tb(oTraceback);
1698 if cFrames is not None and cFrames <= 1:
1699 asRet.append(asTraceBack[-1]);
1700 else:
1701 asRet.append('Traceback:')
1702 for iFrame in range(min(cFrames, len(asTraceBack))):
1703 asRet.append(asTraceBack[-iFrame - 1]);
1704 asRet.append('Stack:')
1705 asRet = asRet + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
1706 except:
1707 asRet.append('internal-error: Hit exception #2! %s' % (traceback.format_exc(),));
1708
1709 if not asRet:
1710 asRet.append('No exception info...');
1711 except:
1712 asRet.append('internal-error: Hit exception! %s' % (traceback.format_exc(),));
1713 else:
1714 asRet = ['Couldn\'t find exception traceback.'];
1715 return asRet;
1716
1717
1718def getObjectTypeName(oObject):
1719 """
1720 Get the type name of the given object.
1721 """
1722 if oObject is None:
1723 return 'None';
1724
1725 # Get the type object.
1726 try:
1727 oType = type(oObject);
1728 except:
1729 return 'type-throws-exception';
1730
1731 # Python 2.x only: Handle old-style object wrappers.
1732 if sys.version_info[0] < 3:
1733 try:
1734 from types import InstanceType; # pylint: disable=no-name-in-module
1735 if oType == InstanceType:
1736 oType = oObject.__class__;
1737 except:
1738 pass;
1739
1740 # Get the name.
1741 try:
1742 return oType.__name__;
1743 except:
1744 return '__type__-throws-exception';
1745
1746
1747def chmodPlusX(sFile):
1748 """
1749 Makes the specified file or directory executable.
1750 Returns success indicator, no exceptions.
1751
1752 Note! Symbolic links are followed and the target will be changed.
1753 """
1754 try:
1755 oStat = os.stat(sFile);
1756 except:
1757 return False;
1758 try:
1759 os.chmod(sFile, oStat.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH);
1760 except:
1761 return False;
1762 return True;
1763
1764
1765#
1766# TestSuite stuff.
1767#
1768
1769def isRunningFromCheckout(cScriptDepth = 1):
1770 """
1771 Checks if we're running from the SVN checkout or not.
1772 """
1773
1774 try:
1775 sFile = __file__;
1776 cScriptDepth = 1;
1777 except:
1778 sFile = sys.argv[0];
1779
1780 sDir = os.path.abspath(sFile);
1781 while cScriptDepth >= 0:
1782 sDir = os.path.dirname(sDir);
1783 if os.path.exists(os.path.join(sDir, 'Makefile.kmk')) \
1784 or os.path.exists(os.path.join(sDir, 'Makefile.kup')):
1785 return True;
1786 cScriptDepth -= 1;
1787
1788 return False;
1789
1790
1791#
1792# Bourne shell argument fun.
1793#
1794
1795
1796def argsSplit(sCmdLine):
1797 """
1798 Given a bourne shell command line invocation, split it up into arguments
1799 assuming IFS is space.
1800 Returns None on syntax error.
1801 """
1802 ## @todo bourne shell argument parsing!
1803 return sCmdLine.split(' ');
1804
1805def argsGetFirst(sCmdLine):
1806 """
1807 Given a bourne shell command line invocation, get return the first argument
1808 assuming IFS is space.
1809 Returns None on invalid syntax, otherwise the parsed and unescaped argv[0] string.
1810 """
1811 asArgs = argsSplit(sCmdLine);
1812 if not asArgs:
1813 return None;
1814
1815 return asArgs[0];
1816
1817#
1818# String helpers.
1819#
1820
1821def stricmp(sFirst, sSecond):
1822 """
1823 Compares to strings in an case insensitive fashion.
1824
1825 Python doesn't seem to have any way of doing the correctly, so this is just
1826 an approximation using lower.
1827 """
1828 if sFirst == sSecond:
1829 return 0;
1830 sLower1 = sFirst.lower();
1831 sLower2 = sSecond.lower();
1832 if sLower1 == sLower2:
1833 return 0;
1834 if sLower1 < sLower2:
1835 return -1;
1836 return 1;
1837
1838
1839def versionCompare(sVer1, sVer2):
1840 """
1841 Compares to version strings in a fashion similar to RTStrVersionCompare.
1842 """
1843
1844 ## @todo implement me!!
1845
1846 if sVer1 == sVer2:
1847 return 0;
1848 if sVer1 < sVer2:
1849 return -1;
1850 return 1;
1851
1852
1853def formatNumber(lNum, sThousandSep = ' '):
1854 """
1855 Formats a decimal number with pretty separators.
1856 """
1857 sNum = str(lNum);
1858 sRet = sNum[-3:];
1859 off = len(sNum) - 3;
1860 while off > 0:
1861 off -= 3;
1862 sRet = sNum[(off if off >= 0 else 0):(off + 3)] + sThousandSep + sRet;
1863 return sRet;
1864
1865
1866def formatNumberNbsp(lNum):
1867 """
1868 Formats a decimal number with pretty separators.
1869 """
1870 sRet = formatNumber(lNum);
1871 return unicode(sRet).replace(' ', u'\u00a0');
1872
1873
1874def isString(oString):
1875 """
1876 Checks if the object is a string object, hiding difference between python 2 and 3.
1877
1878 Returns True if it's a string of some kind.
1879 Returns False if not.
1880 """
1881 if sys.version_info[0] >= 3:
1882 return isinstance(oString, str);
1883 return isinstance(oString, basestring); # pylint: disable=undefined-variable
1884
1885
1886def hasNonAsciiCharacters(sText):
1887 """
1888 Returns True is specified string has non-ASCII characters, False if ASCII only.
1889 """
1890 if isString(sText):
1891 for ch in sText:
1892 if ord(ch) >= 128:
1893 return True;
1894 else:
1895 # Probably byte array or some such thing.
1896 for ch in sText:
1897 if ch >= 128 or ch < 0:
1898 return True;
1899 return False;
1900
1901
1902#
1903# Unpacking.
1904#
1905
1906def unpackZipFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
1907 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
1908 """
1909 Worker for unpackFile that deals with ZIP files, same function signature.
1910 """
1911 import zipfile
1912 if fnError is None:
1913 fnError = fnLog;
1914
1915 fnLog('Unzipping "%s" to "%s"...' % (sArchive, sDstDir));
1916
1917 # Open it.
1918 try: oZipFile = zipfile.ZipFile(sArchive, 'r')
1919 except Exception as oXcpt:
1920 fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
1921 return None;
1922
1923 # Extract all members.
1924 asMembers = [];
1925 try:
1926 for sMember in oZipFile.namelist():
1927 if fnFilter is None or fnFilter(sMember) is not False:
1928 if sMember.endswith('/'):
1929 os.makedirs(os.path.join(sDstDir, sMember.replace('/', os.path.sep)), 0x1fd); # octal: 0775 (python 3/2)
1930 else:
1931 oZipFile.extract(sMember, sDstDir);
1932 asMembers.append(os.path.join(sDstDir, sMember.replace('/', os.path.sep)));
1933 except Exception as oXcpt:
1934 fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
1935 asMembers = None;
1936
1937 # close it.
1938 try: oZipFile.close();
1939 except Exception as oXcpt:
1940 fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
1941 asMembers = None;
1942
1943 return asMembers;
1944
1945
1946## Set if we've replaced tarfile.copyfileobj with __mytarfilecopyfileobj already.
1947g_fTarCopyFileObjOverriddend = False;
1948
1949def __mytarfilecopyfileobj(src, dst, length = None, exception = OSError, bufsize = None):
1950 """ tarfile.copyfileobj with different buffer size (16384 is slow on windows). """
1951 _ = bufsize;
1952 if length is None:
1953 __myshutilcopyfileobj(src, dst, g_cbGoodBufferSize);
1954 elif length > 0:
1955 cFull, cbRemainder = divmod(length, g_cbGoodBufferSize);
1956 for _ in xrange(cFull):
1957 abBuffer = src.read(g_cbGoodBufferSize);
1958 dst.write(abBuffer);
1959 if len(abBuffer) != g_cbGoodBufferSize:
1960 raise exception('unexpected end of source file');
1961 if cbRemainder > 0:
1962 abBuffer = src.read(cbRemainder);
1963 dst.write(abBuffer);
1964 if len(abBuffer) != cbRemainder:
1965 raise exception('unexpected end of source file');
1966
1967
1968def unpackTarFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
1969 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
1970 """
1971 Worker for unpackFile that deals with tarballs, same function signature.
1972 """
1973 import shutil;
1974 import tarfile;
1975 if fnError is None:
1976 fnError = fnLog;
1977
1978 fnLog('Untarring "%s" to "%s"...' % (sArchive, sDstDir));
1979
1980 #
1981 # Default buffer sizes of 16384 bytes is causing too many syscalls on Windows.
1982 # 60%+ speedup for python 2.7 and 50%+ speedup for python 3.5, both on windows with PDBs.
1983 # 20%+ speedup for python 2.7 and 15%+ speedup for python 3.5, both on windows skipping PDBs.
1984 #
1985 if True is True: # pylint: disable=comparison-with-itself
1986 __installShUtilHacks(shutil);
1987 global g_fTarCopyFileObjOverriddend;
1988 if g_fTarCopyFileObjOverriddend is False:
1989 g_fTarCopyFileObjOverriddend = True;
1990 #if sys.hexversion < 0x03060000:
1991 tarfile.copyfileobj = __mytarfilecopyfileobj;
1992
1993 #
1994 # Open it.
1995 #
1996 # Note! We not using 'r:*' because we cannot allow seeking compressed files!
1997 # That's how we got a 13 min unpack time for VBoxAll on windows (hardlinked pdb).
1998 #
1999 try:
2000 if sys.hexversion >= 0x03060000:
2001 oTarFile = tarfile.open(sArchive, 'r|*', bufsize = g_cbGoodBufferSize, copybufsize = g_cbGoodBufferSize);
2002 else:
2003 oTarFile = tarfile.open(sArchive, 'r|*', bufsize = g_cbGoodBufferSize);
2004 except Exception as oXcpt:
2005 fnError('Error opening "%s" for unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt,));
2006 return None;
2007
2008 # Extract all members.
2009 asMembers = [];
2010 try:
2011 for oTarInfo in oTarFile:
2012 try:
2013 if fnFilter is None or fnFilter(oTarInfo.name) is not False:
2014 if oTarInfo.islnk():
2015 # Links are trouble, especially on Windows. We must avoid the falling that will end up seeking
2016 # in the compressed tar stream. So, fall back on shutil.copy2 instead.
2017 sLinkFile = os.path.join(sDstDir, oTarInfo.name.rstrip('/').replace('/', os.path.sep));
2018 sLinkTarget = os.path.join(sDstDir, oTarInfo.linkname.rstrip('/').replace('/', os.path.sep));
2019 sParentDir = os.path.dirname(sLinkFile);
2020 try: os.unlink(sLinkFile);
2021 except: pass;
2022 if sParentDir and not os.path.exists(sParentDir):
2023 os.makedirs(sParentDir);
2024 try: os.link(sLinkTarget, sLinkFile);
2025 except: shutil.copy2(sLinkTarget, sLinkFile);
2026 else:
2027 if oTarInfo.isdir():
2028 # Just make sure the user (we) got full access to dirs. Don't bother getting it 100% right.
2029 oTarInfo.mode |= 0x1c0; # (octal: 0700)
2030 oTarFile.extract(oTarInfo, sDstDir);
2031 asMembers.append(os.path.join(sDstDir, oTarInfo.name.replace('/', os.path.sep)));
2032 except Exception as oXcpt:
2033 fnError('Error unpacking "%s" member "%s" into "%s": %s' % (sArchive, oTarInfo.name, sDstDir, oXcpt));
2034 for sAttr in [ 'name', 'linkname', 'type', 'mode', 'size', 'mtime', 'uid', 'uname', 'gid', 'gname' ]:
2035 fnError('Info: %8s=%s' % (sAttr, getattr(oTarInfo, sAttr),));
2036 for sFn in [ 'isdir', 'isfile', 'islnk', 'issym' ]:
2037 fnError('Info: %8s=%s' % (sFn, getattr(oTarInfo, sFn)(),));
2038 asMembers = None;
2039 break;
2040 except Exception as oXcpt:
2041 fnError('Error unpacking "%s" into "%s": %s' % (sArchive, sDstDir, oXcpt));
2042 asMembers = None;
2043
2044 #
2045 # Finally, close it.
2046 #
2047 try: oTarFile.close();
2048 except Exception as oXcpt:
2049 fnError('Error closing "%s" after unpacking into "%s": %s' % (sArchive, sDstDir, oXcpt));
2050 asMembers = None;
2051
2052 return asMembers;
2053
2054
2055def unpackFile(sArchive, sDstDir, fnLog, fnError = None, fnFilter = None):
2056 # type: (string, string, (string) -> None, (string) -> None, (string) -> bool) -> list[string]
2057 """
2058 Unpacks the given file if it has a know archive extension, otherwise do
2059 nothing.
2060
2061 fnLog & fnError both take a string parameter.
2062
2063 fnFilter takes a member name (string) and returns True if it's included
2064 and False if excluded.
2065
2066 Returns list of the extracted files (full path) on success.
2067 Returns empty list if not a supported archive format.
2068 Returns None on failure. Raises no exceptions.
2069 """
2070 sBaseNameLower = os.path.basename(sArchive).lower();
2071
2072 #
2073 # Zip file?
2074 #
2075 if sBaseNameLower.endswith('.zip'):
2076 return unpackZipFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
2077
2078 #
2079 # Tarball?
2080 #
2081 if sBaseNameLower.endswith('.tar') \
2082 or sBaseNameLower.endswith('.tar.gz') \
2083 or sBaseNameLower.endswith('.tgz') \
2084 or sBaseNameLower.endswith('.tar.bz2'):
2085 return unpackTarFile(sArchive, sDstDir, fnLog, fnError, fnFilter);
2086
2087 #
2088 # Cannot classify it from the name, so just return that to the caller.
2089 #
2090 fnLog('Not unpacking "%s".' % (sArchive,));
2091 return [];
2092
2093
2094#
2095# Misc.
2096#
2097def areBytesEqual(oLeft, oRight):
2098 """
2099 Compares two byte arrays, strings or whatnot.
2100
2101 returns true / false accordingly.
2102 """
2103
2104 # If both are None, consider them equal (bogus?):
2105 if oLeft is None and oRight is None:
2106 return True;
2107
2108 # If just one is None, they can't match:
2109 if oLeft is None or oRight is None:
2110 return False;
2111
2112 # If both have the same type, use the compare operator of the class:
2113 if type(oLeft) is type(oRight):
2114 #print('same type: %s' % (oLeft == oRight,));
2115 return oLeft == oRight;
2116
2117 # On the offchance that they're both strings, but of different types.
2118 if isString(oLeft) and isString(oRight):
2119 #print('string compare: %s' % (oLeft == oRight,));
2120 return oLeft == oRight;
2121
2122 #
2123 # See if byte/buffer stuff that can be compared directory. If not convert
2124 # strings to bytes.
2125 #
2126 # Note! For 2.x, we must convert both sides to the buffer type or the
2127 # comparison may fail despite it working okay in test cases.
2128 #
2129 if sys.version_info[0] >= 3:
2130 if isinstance(oLeft, (bytearray, memoryview, bytes)) and isinstance(oRight, (bytearray, memoryview, bytes)): # pylint: disable=undefined-variable
2131 return oLeft == oRight;
2132
2133 if isString(oLeft):
2134 try: oLeft = bytes(oLeft, 'utf-8');
2135 except: pass;
2136 if isString(oRight):
2137 try: oRight = bytes(oRight, 'utf-8');
2138 except: pass;
2139 else:
2140 if isinstance(oLeft, (bytearray, buffer)) and isinstance(oRight, (bytearray, buffer)): # pylint: disable=undefined-variable
2141 if isinstance(oLeft, bytearray):
2142 oLeft = buffer(oLeft); # pylint: disable=redefined-variable-type,undefined-variable
2143 else:
2144 oRight = buffer(oRight); # pylint: disable=redefined-variable-type,undefined-variable
2145 #print('buf/byte #1 compare: %s (%s vs %s)' % (oLeft == oRight, type(oLeft), type(oRight),));
2146 return oLeft == oRight;
2147
2148 if isString(oLeft):
2149 try: oLeft = bytearray(oLeft, 'utf-8'); # pylint: disable=redefined-variable-type
2150 except: pass;
2151 if isString(oRight):
2152 try: oRight = bytearray(oRight, 'utf-8'); # pylint: disable=redefined-variable-type
2153 except: pass;
2154
2155 # Check if we now have the same type for both:
2156 if type(oLeft) is type(oRight):
2157 #print('same type now: %s' % (oLeft == oRight,));
2158 return oLeft == oRight;
2159
2160 # Check if we now have buffer/memoryview vs bytes/bytesarray again.
2161 if sys.version_info[0] >= 3:
2162 if isinstance(oLeft, (bytearray, memoryview, bytes)) and isinstance(oRight, (bytearray, memoryview, bytes)): # pylint: disable=undefined-variable
2163 return oLeft == oRight;
2164 else:
2165 if isinstance(oLeft, (bytearray, buffer)) and isinstance(oRight, (bytearray, buffer)): # pylint: disable=undefined-variable
2166 if isinstance(oLeft, bytearray):
2167 oLeft = buffer(oLeft); # pylint: disable=redefined-variable-type,undefined-variable
2168 else:
2169 oRight = buffer(oRight); # pylint: disable=redefined-variable-type,undefined-variable
2170 #print('buf/byte #2 compare: %s (%s vs %s)' % (oLeft == oRight, type(oLeft), type(oRight),));
2171 return oLeft == oRight;
2172
2173 # Do item by item comparison:
2174 if len(oLeft) != len(oRight):
2175 #print('different length: %s vs %s' % (len(oLeft), len(oRight)));
2176 return False;
2177 i = len(oLeft);
2178 while i > 0:
2179 i = i - 1;
2180
2181 iElmLeft = oLeft[i];
2182 if not isinstance(iElmLeft, int) and not isinstance(iElmLeft, long):
2183 iElmLeft = ord(iElmLeft);
2184
2185 iElmRight = oRight[i];
2186 if not isinstance(iElmRight, int) and not isinstance(iElmRight, long):
2187 iElmRight = ord(iElmRight);
2188
2189 if iElmLeft != iElmRight:
2190 #print('element %d differs: %x %x' % (i, iElmLeft, iElmRight,));
2191 return False;
2192 return True;
2193
2194
2195#
2196# Unit testing.
2197#
2198
2199# pylint: disable=missing-docstring
2200# pylint: disable=undefined-variable
2201class BuildCategoryDataTestCase(unittest.TestCase):
2202 def testIntervalSeconds(self):
2203 self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(3600)), (3600, None));
2204 self.assertEqual(parseIntervalSeconds(formatIntervalSeconds(1209438593)), (1209438593, None));
2205 self.assertEqual(parseIntervalSeconds('123'), (123, None));
2206 self.assertEqual(parseIntervalSeconds(123), (123, None));
2207 self.assertEqual(parseIntervalSeconds(99999999999), (99999999999, None));
2208 self.assertEqual(parseIntervalSeconds(''), (0, 'Empty interval string.'));
2209 self.assertEqual(parseIntervalSeconds('1X2'), (3, 'Unknown unit "X".'));
2210 self.assertEqual(parseIntervalSeconds('1 Y3'), (4, 'Unknown unit "Y".'));
2211 self.assertEqual(parseIntervalSeconds('1 Z 4'), (5, 'Unknown unit "Z".'));
2212 self.assertEqual(parseIntervalSeconds('1 hour 2m 5second'), (3725, None));
2213 self.assertEqual(parseIntervalSeconds('1 hour,2m ; 5second'), (3725, None));
2214
2215 def testHasNonAsciiChars(self):
2216 self.assertEqual(hasNonAsciiCharacters(''), False);
2217 self.assertEqual(hasNonAsciiCharacters('asdfgebASDFKJ@#$)(!@#UNASDFKHB*&$%&)@#(!)@(#!(#$&*#$&%*Y@#$IQWN---00;'), False);
2218 self.assertEqual(hasNonAsciiCharacters('\x80 '), True);
2219 self.assertEqual(hasNonAsciiCharacters('\x79 '), False);
2220 self.assertEqual(hasNonAsciiCharacters(u'12039889y!@#$%^&*()0-0asjdkfhoiuyweasdfASDFnvV'), False);
2221 self.assertEqual(hasNonAsciiCharacters(u'\u0079'), False);
2222 self.assertEqual(hasNonAsciiCharacters(u'\u0080'), True);
2223 self.assertEqual(hasNonAsciiCharacters(u'\u0081 \u0100'), True);
2224 self.assertEqual(hasNonAsciiCharacters(b'\x20\x20\x20'), False);
2225 self.assertEqual(hasNonAsciiCharacters(b'\x20\x81\x20'), True);
2226
2227 def testAreBytesEqual(self):
2228 self.assertEqual(areBytesEqual(None, None), True);
2229 self.assertEqual(areBytesEqual(None, ''), False);
2230 self.assertEqual(areBytesEqual('', ''), True);
2231 self.assertEqual(areBytesEqual('1', '1'), True);
2232 self.assertEqual(areBytesEqual('12345', '1234'), False);
2233 self.assertEqual(areBytesEqual('1234', '1234'), True);
2234 self.assertEqual(areBytesEqual('1234', b'1234'), True);
2235 self.assertEqual(areBytesEqual(b'1234', b'1234'), True);
2236 self.assertEqual(areBytesEqual(b'1234', '1234'), True);
2237 self.assertEqual(areBytesEqual(b'1234', bytearray([0x31,0x32,0x33,0x34])), True);
2238 self.assertEqual(areBytesEqual('1234', bytearray([0x31,0x32,0x33,0x34])), True);
2239 self.assertEqual(areBytesEqual(u'1234', bytearray([0x31,0x32,0x33,0x34])), True);
2240 self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), bytearray([0x31,0x32,0x33,0x34])), True);
2241 self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), '1224'), False);
2242 self.assertEqual(areBytesEqual(bytearray([0x31,0x32,0x33,0x34]), bytearray([0x31,0x32,0x32,0x34])), False);
2243 if sys.version_info[0] >= 3:
2244 pass;
2245 else:
2246 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2247 bytearray([0x31,0x32,0x33,0x34])), True);
2248 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2249 bytearray([0x99,0x32,0x32,0x34])), False);
2250 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2251 buffer(bytearray([0x31,0x32,0x33,0x34,0x34]), 0, 4)), True);
2252 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1),
2253 buffer(bytearray([0x99,0x32,0x33,0x34,0x34]), 0, 4)), False);
2254 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), b'1234'), True);
2255 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), '1234'), True);
2256 self.assertEqual(areBytesEqual(buffer(bytearray([0x30,0x31,0x32,0x33,0x34]), 1), u'1234'), True);
2257
2258if __name__ == '__main__':
2259 unittest.main();
2260 # not reached.
2261
Note: See TracBrowser for help on using the repository browser.

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