Skip to content

Commit abc0cdc

Browse files
committed
#267 Add SCANCODE_EXPERIMENTAL_MAX_MEMORY feature switch
* if the SCANCODE_EXPERIMENTAL_MAX_MEMORY env var exists then the memory limit will be enforced. Otherwise only timeouts are enforced Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
1 parent 0cd22d0 commit abc0cdc

1 file changed

Lines changed: 36 additions & 6 deletions

File tree

src/scancode/cli.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@
3131
# FIXME: unknown license
3232
###########################################################################
3333
from multiprocessing.pool import IMapIterator, IMapUnorderedIterator
34-
from commoncode import filetype
34+
3535
def wrapped(func):
3636
def wrap(self, timeout=None):
3737
return func(self, timeout=timeout or 1e10)
3838
return wrap
39+
3940
IMapIterator.next = wrapped(IMapIterator.next)
4041
IMapIterator.__next__ = IMapIterator.next
4142
IMapUnorderedIterator.next = wrapped(IMapUnorderedIterator.next)
@@ -61,10 +62,12 @@ def wrap(self, timeout=None):
6162

6263
from commoncode import ignore
6364
from commoncode import fileutils
65+
from commoncode import filetype
6466

6567
from scancode import __version__ as version
6668

6769
from scancode.interrupt import interruptible
70+
from scancode.interrupt import time_and_ram_interruptible
6871
from scancode import utils
6972

7073
from scancode.cache import get_scans_cache_class
@@ -403,7 +406,7 @@ def scan_event(item):
403406
click.secho(' ' + errored_path, fg='red', err=to_stdout)
404407

405408
click.secho('Scan statistics: %(files_count)d files scanned in %(total_time)ds.' % locals(), err=to_stdout)
406-
click.secho('Scan options: %(_scans)s with %(processes)d processes.' % locals(), err=to_stdout)
409+
click.secho('Scan options: %(_scans)s with %(processes)d processe(s).' % locals(), err=to_stdout)
407410
click.secho('Scanning speed: {:.2} files per sec.'.format(files_scanned_per_second), err=to_stdout)
408411
click.secho('Scanning time: %(scanning_time)ds.' % locals(), err=to_stdout, reset=True,)
409412
click.secho('Indexing time: %(indexing_time)ds.' % locals(), err=to_stdout)
@@ -416,11 +419,17 @@ def scan_event(item):
416419
TEST_TIMEOUT = 0
417420
MAX_SCAN_TIMEOUT = 600
418421

422+
TEST_MAX_MEMORY = 0 #1024 * 1024 * 1024 * 80 # 100MB
423+
MAX_SCAN_MEMORY = 1024 * 1024 * 1024 * 1024 # 1GB
424+
425+
SCANCODE_EXPERIMENTAL_MAX_MEMORY = os.environ.get('SCANCODE_EXPERIMENTAL_MAX_MEMORY', False)
426+
419427

420428
def scan_timeout(size):
421429
"""
422-
Return a timeout in seconds computed based on a file size.
430+
Return a scan timeout in seconds computed based on a file size.
423431
"""
432+
# at least 60 seconds
424433
timeout = 60
425434
if size > 1024 * 1024 * 1024:
426435
# add extra seconds for each megabyte
@@ -430,6 +439,20 @@ def scan_timeout(size):
430439
return timeout
431440

432441

442+
def scan_max_memory(size):
443+
"""
444+
Return a scan not-to-exceed maximum memory in bytes computed based on a file size.
445+
"""
446+
447+
max_memory = 1024 * 1024 * 1024 * 700 # 700MB
448+
if size > 1024 * 1024 * 1024:
449+
# add extra quota for each byte: 5x
450+
max_memory += size * 5
451+
452+
max_memory = min((max_memory, MAX_SCAN_MEMORY))
453+
return max_memory
454+
455+
433456
def _scanit(paths, scanners, scans_cache_class):
434457
"""
435458
Run scans and cache results. Used as an execution unit for parallel processing.
@@ -449,11 +472,18 @@ def _scanit(paths, scanners, scans_cache_class):
449472
# ENSURE we only do tghis for files not directories
450473
if not is_cached:
451474
# run the scan as an interruptiple task
452-
# use TEST_TIMEOUT for tests if provided
453-
timeout = TEST_TIMEOUT or scan_timeout(infos.get('size', 0))
454475
scans_runner = partial(scan_one, abs_path, scanners)
455-
success, scan_result = interruptible(scans_runner, timeout=timeout)
456476

477+
file_size = infos.get('size', 0)
478+
# use TEST_TIMEOUT for tests if provided
479+
timeout = TEST_TIMEOUT or scan_timeout(file_size)
480+
# feature switch
481+
if SCANCODE_EXPERIMENTAL_MAX_MEMORY:
482+
# use TEST_MAX_MEMORY for tests if provided
483+
max_memory = TEST_MAX_MEMORY or scan_max_memory(file_size)
484+
success, scan_result = time_and_ram_interruptible(scans_runner, timeout=timeout, max_memory=max_memory)
485+
else:
486+
success, scan_result = interruptible(scans_runner, timeout=timeout)
457487
if not success:
458488
# Use scan errors as the scan result for that file on failure
459489
scan_result = dict(scan_errors=[scan_result, ''])

0 commit comments

Comments
 (0)