Test Failed
Pull Request — develop (#3389)
by
unknown
02:34
created

PluginModel.__init__()   A

Complexity

Conditions 3

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
nop 3
dl 0
loc 32
rs 9.45
c 0
b 0
f 0
1
2
"""Profiler plugin."""
3
4
import sys
5
from collections import Counter
6
from glances.plugins.plugin.model import GlancesPluginModel
7
from glances.logger import logger
8
9
# Constants for sys.monitoring
10
TOOL_ID = 2  # ID 0 is reserved, 1 was used in test, 2 should be safe
11
# We will use PY_START to count function entries
12
EVENT_ID = getattr(sys.monitoring.events, 'PY_START', None) if hasattr(sys, 'monitoring') else None
13
14
class PluginModel(GlancesPluginModel):
15
    """Glances' Profiler Plugin.
16
17
    stats is a list of dict (function name, count)
18
    """
19
20
    def __init__(self, args=None, config=None):
21
        """Init the plugin."""
22
        super(PluginModel, self).__init__(args=args, config=config)
23
24
        # We want to display the stats in the UI
25
        self.args = args
26
27
        # Init the internal counter
28
        self._counts = Counter()
29
        self._monitoring_active = False
30
31
        # Check availability
32
        if not hasattr(sys, 'monitoring'):
33
            logger.warning("sys.monitoring not available. Profiler plugin disabled.")
34
            self.actions.disable()
35
            return
36
37
        try:
38
            sys.monitoring.use_tool_id(TOOL_ID, "glances_profiler")
39
            logger.info(f"sys.monitoring tool ID {TOOL_ID} registered.")
40
            self._monitoring_active = True
41
42
            # Register callback
43
            sys.monitoring.register_callback(TOOL_ID, EVENT_ID, self._callback)
44
            
45
            # Enable events
46
            sys.monitoring.set_events(TOOL_ID, EVENT_ID)
47
            
48
        except ValueError as e:
49
            logger.error(f"Failed to register sys.monitoring tool: {e}")
50
            self.actions.disable()
51
            self._monitoring_active = False
52
53
    def exit(self):
54
        """Stop monitoring."""
55
        if self._monitoring_active and hasattr(sys, 'monitoring'):
56
            sys.monitoring.set_events(TOOL_ID, 0)
57
            sys.monitoring.free_tool_id(TOOL_ID)
58
        super(PluginModel, self).exit()
59
60
    def _callback(self, code, instruction_offset):
61
        """Callback for sys.monitoring."""
62
        # This is called VERY frqeuently. Keep it minimal.
63
        # We just increment the counter for the code object name.
64
        self._counts[code.co_name] += 1
65
        return sys.monitoring.DISABLE
66
67
    def get_key(self):
68
        """Return the key of the list."""
69
        return 'function'
70
71
    def update_views(self):
72
        """Update the views."""
73
        # Standard table view
74
        self.views = {}
75
        if not self.stats:
76
            return self.views
77
78
        for i in self.stats:
79
            self.views[i[self.get_key()]] = {'hidden': False}
80
81
        return self.views
82
83
    def update(self):
84
        """Update stats."""
85
        # Reset stats
86
        self.reset()
87
88
        if not self._monitoring_active:
89
            return self.stats
90
91
        # Get the top 10 most frequent functions
92
        # We take the counter snapshot and reset it maybe? 
93
        # Or just show cumulative? Let's show rate (per second/update) if possible.
94
        # For now, let's just show top N in the current interval.
95
        
96
        # NOTE: To show rate, we would need to diff with previous.
97
        # But for simplicity V1, let's just show the accumulated counts since start (or allow reset).
98
        # Actually, showing "Hot functions right now" implying per-update interval is better.
99
        
100
        # Snapshot and reset internal counter for the next interval?
101
        # WARNING: _callback runs in another thread/context potentially?
102
        # In simple Python (GIL), it is safe-ish, but let's be careful.
103
        # sys.monitoring callback runs synchronously.
104
        
105
        # Let's copy the current state
106
        current_counts = self._counts.copy()
107
        # self._counts.clear() # If we want per-interval stats, we should clear.
108
        
109
        # Sort by count desc
110
        top_n = current_counts.most_common(10)
111
        
112
        for func_name, count in top_n:
113
            stat = {
114
                'function': func_name,
115
                'count': count
116
            }
117
            self.stats.append(stat)
118
            
119
        return self.stats
120