diff --git a/glances/__init__.py b/glances/__init__.py index 27bd1282..7c39b54a 100644 --- a/glances/__init__.py +++ b/glances/__init__.py @@ -97,7 +97,7 @@ def start(config, args): # Load mode global mode - if args.trace_malloc: + if args.trace_malloc or args.memory_leak: tracemalloc.start() start_duration = Counter() @@ -120,6 +120,17 @@ def start(config, args): # Start the main loop logger.debug("Glances started in {} seconds".format(start_duration.get())) + if args.stop_after: + logger.info('Glances will be stopped in ~{} seconds'.format( + args.stop_after * args.time * args.memory_leak * 2)) + + if args.memory_leak: + print('Memory leak detection, please wait...') + # First run without dump to fill the memory + mode.serve_n(args.stop_after) + # Then start the memory-leak loop + snapshot_begin = tracemalloc.take_snapshot() + if args.stdout_issue or args.stdout_apidoc: # Serve once for issue/test mode mode.serve_issue() @@ -127,10 +138,20 @@ def start(config, args): # Serve forever mode.serve_forever() - if args.trace_malloc: + if args.memory_leak: + snapshot_end = tracemalloc.take_snapshot() + snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename') + memory_leak = sum([s.size_diff for s in snapshot_diff]) + print("Memory comsumption for {} refreshs: {}KB (see log for details)".format( + args.stop_after, + memory_leak / 1000)) + logger.info("Memory consumption (top 5):") + for stat in snapshot_diff[:5]: + logger.info(stat) + elif args.trace_malloc: # See more options here: https://docs.python.org/3/library/tracemalloc.html snapshot = tracemalloc.take_snapshot() - top_stats = snapshot.statistics("lineno") + top_stats = snapshot.statistics("filename") print("[ Trace malloc - Top 10 ]") for stat in top_stats[:10]: print(stat) diff --git a/glances/main.py b/glances/main.py index 547ffcdc..fe00132f 100644 --- a/glances/main.py +++ b/glances/main.py @@ -427,7 +427,14 @@ Examples of use: default=False, action='store_true', dest='trace_malloc', - help='test memory leak with tracemalloc (python 3.4 or higher needed)', + help='trace memory allocation and display it at the end of the process (python 3.4 or higher needed)', + ) + parser.add_argument( + '--memory-leak', + default=False, + action='store_true', + dest='memory_leak', + help='test memory leak (python 3.4 or higher needed)', ) parser.add_argument( '--api-doc', default=None, action='store_true', dest='stdout_apidoc', help='display fields descriptions' @@ -685,11 +692,21 @@ Examples of use: disable(self.args, 'hddtemp') logger.debug("Sensors and HDDTemp are disabled") - if getattr(self.args, 'trace_malloc', True) and not PY3: - self.args.trace_malloc = False - logger.critical("trace_malloc is only available with Python 3") + if getattr(self.args, 'trace_malloc', True) and not (PY3 or self.is_standalone()): + logger.critical("Option --trace-malloc is only available with Python 3 and terminal mode") sys.exit(2) + if getattr(self.args, 'memory_leak', True) and not (PY3 or self.is_standalone()): + logger.critical("Option --memory-leak is only available with Python 3 and terminal mode") + sys.exit(2) + else: + logger.info('Memory leak detection enabled') + self.args.quiet = True + if not self.args.stop_after: + self.args.stop_after = 60 + self.args.time = 1 + self.disable_history = True + # Let the plugins known the Glances mode self.args.is_standalone = self.is_standalone() self.args.is_client = self.is_client() diff --git a/glances/standalone.py b/glances/standalone.py index 0df44e63..2240ecf9 100644 --- a/glances/standalone.py +++ b/glances/standalone.py @@ -172,7 +172,14 @@ class GlancesStandalone(object): else: while self.__serve_once(): pass - self.end() + # self.end() + + def serve_n(self, n=1): + """Serve n time.""" + for _ in range(n): + if not self.__serve_once(): + break + # self.end() def end(self): """End of the standalone CLI."""