98_Analyze - Modul zur Systemanalyse (RAM-Verbrauch, Leaks, und mehr)

Begonnen von DS_Starter, 06 November 2026, 23:50:06

Vorheriges Thema - Nächstes Thema

DS_Starter

Hallo zusammen,

ich habe das (neue) Modul 98_Analyze eingecheckt und ist morgen früh im Update enthalten.
Es gab es bereits schon seit langer Zeit in meinem contrib in einer Alpha-Version. Ich habe es nun komplett erneuert und erweitert.
Dieses Modul gehört zu dem Trio von FHEM System Management Modulen:

- Installer
- MemSaver
- Analyze


Was macht das Modul und wie hilft es:

Analysiert den Speicherverbrauch des FHEM-Prozesses: die Größe von Devices und ihrer Teilstrukturen, von Modul-Hashes und von den Kern-Hashes von FHEM, den Prozessspeicher, Timer, Filedeskriptoren und Kindprozesse. Snapshots können gespeichert und miteinander verglichen werden. Strukturen, die über die Zeit kontinuierlich wachsen, werden als Speicherleck-Kandidaten gemeldet. Das Modul hilft dabei, das Device, das Modul oder die Datenstruktur zu finden, die für einen wachsenden Speicherverbrauch verantwortlich ist.
Analyze ist einer von mehreren Bausteinen für die Systempflege und Analyse: MemSaver hält den RAM von FHEM so weit wie möglich sauber und klein, Analyze zeigt, wo der Speicher verwendet wird und welches Modul, Device oder welche Datenstruktur für sein Wachstum verantwortlich ist, und Installer sorgt für saubere Installationen und Updates der Perl-Pakete, die FHEM-Module benötigen, einschließlich der Voraussetzungen dieses Moduls.
Alle Messungen laufen in einem eigenen Prozess (BlockingCall). FHEM wird nicht blockiert, und ein Absturz von Devel::Size bei einem einzelnen Device reißt FHEM nicht mit.


Grundlagen

- Analyze misst und berichtet nur, es gibt keinen Speicher frei. Um den RAM klein zu halten, dient MemSaver.

- Der eigene Prozess ist eine Kopie von FHEM zum Zeitpunkt des Aufrufs. Alle Größen beschreiben deshalb genau diesen Zeitpunkt.

- Größen werden exklusiv gezählt: Jede Speicheradresse wird nur einmal gezählt, Referenzen auf andere Devices (zum Beispiel IODev) und auf die Kern-Hashes von FHEM werden nicht verfolgt. Anders als bei Devel::Size::total_size wird nichts doppelt gezählt, die Größen der Devices lassen sich also addieren. Die Werte sind Näherungen mit wenigen Prozent Abweichung.

- Die Snapshot-Ablage des Moduls selbst wird nicht mitgemessen, sonst würde das Modul immer als wachsendes Objekt erscheinen.

- Die Summe aller gemessenen Strukturen wird als attributed (zugeordnet) ausgewiesen. Der Rest der Prozessgröße (rss) erscheint als not attributable (nicht zuordenbar). Er besteht aus dem Perl-Interpreter, geladenem Programmcode, Shared Libraries, Speicherfragmentierung und - ohne deepScan - den file-scope Variablen der Module.

- Die Prozessgröße (rss) wächst nach dem Start von FHEM üblicherweise, bis der Perl-Speicher "warmgelaufen" ist, und schrumpft danach selten. Snapshots sollten deshalb nach einigen Stunden Laufzeit erstellt werden.

-  private, pss und shared ändern sich immer dann, wenn Kindprozesse (BlockingCall) starten oder enden. Für Trends wird rss verwendet, die anderen Werte sind nur bei gleicher Anzahl Kindprozesse vergleichbar.


Typischer Ablauf zur Analyse:

    set <name> snapshot t0
    ... einige Stunden warten ...
    set <name> snapshot t1
    get <name> diff t0 t1 - was hat sich zwischen zwei Snapshots verändert
    get <name> leakCandidates - ab 3 Snapshots: Objekte, die kontinuierlich wachsen
    set <name> xHashDetail $defs{myDevice}{helper} - ein auffälliges Objekt genauer ansehen

    Alternativ erstellt attr <name> autoSnapshot 6 automatisch alle 6 Stunden einen Snapshot.


Voraussetzungen:

- Devel::Size - zwingend erforderlich. Ohne dieses Modul kann kein Device angelegt werden.
- Optional: PadWalker - damit erfasst deepScan auch die file-scope my Variablen der Modul-Dateien. Ob es verfügbar ist, zeigt das Internal PADWALKER.
- Die Perl-Module Storable, MIME::Base64, Scalar::Util, Time::HiRes, POSIX und B (alle Bestandteil von Perl) sowie das FHEM-Modul Blocking.



Ich hoffe, dass dieses Modul hilfreich sein wird Module und Strukturen zu finden, die zu einem stetig steigenden Speicherverbrauch beitragen und wie hoch dieser Anteil ist. Manche Module können durch Datensammlung Speicher akkumulieren was in diesen Fällen keinen Fehler darstellt. Die stetigen Anteile sollten allerdings gering sein nachdem FHEM nach dem Systemstart ein Plateau erreicht hat und alle Strukturen für den normalen Betrieb geladen wurden.
Durch regelmäßige Snapshots kann man das Speicherwachstum zu bestimmten Zeiten (z.B. Tageswechsel) analysieren.
Über die Snapshots lässt sich das Speicherwachstum über einen längeren Zeutraum verfolgen.

Im Anhang eine Beispielausgabe von "get ... leakCandidates".


LG,
Heiko

Proxmox+Debian+MariaDB, PV: SMA, Victron+Pylontech
Maintainer:SSCam,SSChatBot,SSCal,SSFile,DbLog,DbRep,Log2Syslog,SolarForecast,Watches,Dashboard,PylonLowVoltage,MemSaver
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

Nobbynews

#1
Hallo Heiko,

ich habe mal Dein neues Modul installiert und hier ein Ergebnis.
Würdest Du das schon als Speicher-Leak bezeichnen?
deepScan habe ich aktiviert.

Leak candidates: objects growing in at least 12 of 15 intervals without ever shrinking
Snapshots: auto_20261107_185930, auto_20261107_215809, auto_20261108_005809, auto_20261108_035809, auto_20261108_065809, auto_20261108_095809, auto_20261108_125809, auto_20261108_155809, auto_20261108_185809, auto_20261108_215809, auto_20261109_005809, auto_20261109_035809, auto_20261109_065809, auto_20261109_095809, auto_20261109_125809, auto_20261109_155809   period: 45.0 h

Parts of devices ($defs{device}{key}):
     +286.9 kB  $defs{FritzOG}{fhem}                          74.8 kB -> 93.3 kB -> 111.4 kB -> 127.5 kB -> 141.9 kB -> 164.7 kB -> 178.2 kB -> 193.9 kB -> 211.0 kB -> 229.0 kB -> 247.2 kB -> 279.1 kB -> 298.9 kB -> 319.6 kB -> 341.0 kB -> 361.7 kB  (6.4 kB/h)
     +279.0 kB  $defs{FritzWohnzimmer}{fhem}                  108.5 kB -> 132.4 kB -> 149.5 kB -> 165.8 kB -> 189.2 kB -> 203.5 kB -> 218.3 kB -> 234.0 kB -> 250.3 kB -> 265.4 kB -> 298.5 kB -> 314.6 kB -> 333.6 kB -> 351.6 kB -> 369.2 kB -> 387.5 kB  (6.2 kB/h)
      +35.2 kB  $defs{FritzBox}{fhem}                         82.9 kB -> 92.1 kB -> 94.7 kB -> 97.2 kB -> 99.2 kB -> 101.3 kB -> 102.9 kB -> 104.7 kB -> 106.4 kB -> 108.0 kB -> 109.9 kB -> 111.3 kB -> 113.0 kB -> 114.7 kB -> 116.4 kB -> 118.1 kB  (800 B/h)
      +28.9 kB  $defs{FritzDach}{fhem}                        71.1 kB -> 73.0 kB -> 74.7 kB -> 76.7 kB -> 78.4 kB -> 80.6 kB -> 84.3 kB -> 86.0 kB -> 87.8 kB -> 89.5 kB -> 91.2 kB -> 93.0 kB -> 94.9 kB -> 96.6 kB -> 98.3 kB -> 100.0 kB  (657 B/h)

Devices:
     +290.8 kB  FritzOG                                       203.1 kB -> 223.4 kB -> 241.5 kB -> 259.2 kB -> 272.5 kB -> 295.8 kB -> 309.9 kB -> 325.5 kB -> 342.1 kB -> 360.1 kB -> 378.8 kB -> 410.8 kB -> 430.5 kB -> 449.5 kB -> 471.0 kB -> 493.9 kB  (6.5 kB/h)
     +274.5 kB  FritzWohnzimmer                               242.3 kB -> 263.9 kB -> 281.0 kB -> 295.6 kB -> 319.6 kB -> 335.0 kB -> 348.7 kB -> 365.0 kB -> 381.8 kB -> 396.4 kB -> 429.5 kB -> 445.6 kB -> 465.1 kB -> 480.9 kB -> 498.5 kB -> 516.8 kB  (6.1 kB/h)
      +29.5 kB  FritzDach                                     173.9 kB -> 175.7 kB -> 177.4 kB -> 180.0 kB -> 181.1 kB -> 183.8 kB -> 187.0 kB -> 188.7 kB -> 191.1 kB -> 192.2 kB -> 194.0 kB -> 195.7 kB -> 198.1 kB -> 199.9 kB -> 201.6 kB -> 203.3 kB  (670 B/h)

Device types (sum of devices):
     +636.4 kB  FritzSmart                                    1.19 MB -> 1.25 MB -> 1.29 MB -> 1.33 MB -> 1.36 MB -> 1.41 MB -> 1.44 MB -> 1.48 MB -> 1.52 MB -> 1.55 MB -> 1.60 MB -> 1.65 MB -> 1.70 MB -> 1.73 MB -> 1.77 MB -> 1.82 MB  (14.2 kB/h)

Interpretation:
  part/dev/type  : data inside a device grows (readings, helper hashes, queues, caches) -> look at the TYPE and use "set  xHashDetail \$defs{device}{key}".
  var/pkgvar/file: global or file-scope variables of a module grow (needs deepScan, PadWalker for file-scope variables).
  timer/timerdev : timers accumulate -> a module sets timers without removing old ones.
  cnt fds/kids   : file descriptor or child process leak (e.g. BlockingCall children not ending).
  proc unattributed grows but no structure does: memory outside the walked structures (XS/C libraries, fragmentation, file-scope variables without deepScan).

OK

Diff der beiden letzten Snapshots:

count_fds
299
2026-11-09 15:58:15
count_kids
1
2026-11-09 15:58:15
count_timers
230
2026-11-09 15:58:15
crashListCount
0
2026-11-07 18:57:50
proc_private_kB
53540
2026-11-09 15:58:15
proc_pss_kB
166748
2026-11-09 15:58:15
proc_rssPeak_kB
284520
2026-11-09 15:58:27
proc_rssTrend_kBperHour
687.7
2026-11-09 15:58:27
proc_rss_kB
284520
2026-11-09 15:58:27
proc_swap_kB
0
2026-11-09 15:58:15
snapshot_last
auto_20261109_155809
2026-11-09 15:58:15
snapshots
16
2026-11-09 15:58:15
state
done
2026-11-09 15:58:15
unattributed_kB
240991
2026-11-09 15:58:15
myAnalyze
room
System
Attributes
autoSnapshot
3
deleteattr
deepScan
1
deleteattr
maxSnapshots
17
deleteattr
room
System
deleteattr
Help for Analyze Copy for forum.fhem.de Raw definition Select icon Extend devStateIcon Event Monitor (filtered) Delete myAnalyze

 
Comparison 'auto_20261109_125809' -> 'auto_20261109_155809'   period: 3.00 h

Process memory:
  rss            279968 kB ->   282268 kB      +2300 kB  (+767 kB/h)
  anon           267732 kB ->   270032 kB      +2300 kB  (+767 kB/h)
  pss            161956 kB ->   166748 kB      +4792 kB  (+1597 kB/h)
  priv            46256 kB ->    53540 kB      +7284 kB  (+2428 kB/h)
  shared         233712 kB ->   228728 kB      -4984 kB  (-1661 kB/h)
  swap                0 kB ->        0 kB         +0 kB  (+0 kB/h)
  attributed       +143 kB   (attributed to FHEM structures)
  unattributed    +2157 kB   (rest: interpreter, code, fragmentation, file-scope variables)

Counters:
  defs       1160 ->   1157  (-3)
  fds         302 ->    299  (-3)
  kids          1 ->      1  (+0)
  ready         0 ->      0  (+0)
  select       57 ->     54  (-3)
  timers      237 ->    230  (-7)

Timers by function - growth (top 15):
            +1  BlockingKill  (1 -> 2)
            +1  CUL_MAX_SQH  (0 -> 1)

Timers by device - growth (top 15):
            +1  cm  (2 -> 3)

Readings per device - growth (top 15):
            +4  FritzOG  (80 -> 84)
            +1  FritzBox  (157 -> 158)
            +1  MQTT2_Server_192.168.2.217_14887  (0 -> 1)
            +1  MQTT2_Server_192.168.2.217_23695  (0 -> 1)
            +1  MQTT2_Server_192.168.2.217_5254  (0 -> 1)
            +1  MQTT2_Server_192.168.2.217_5887  (0 -> 1)
            +1  telnetPort_127.0.0.1_48906  (0 -> 1)

Parts of devices ($defs{device}{key}) - growth (top 15):
      +20.7 kB  $defs{FritzOG}{fhem}  (341.0 kB -> 361.7 kB)
      +18.3 kB  $defs{FritzWohnzimmer}{fhem}  (369.2 kB -> 387.5 kB)
       +2.3 kB  $defs{FritzOG}{READINGS}  (45.7 kB -> 48.0 kB)
       +2.0 kB  $defs{cm}{sendQueue}  (0 B -> 2.0 kB)
       +1.7 kB  $defs{FritzDach}{fhem}  (98.3 kB -> 100.0 kB)
       +1.7 kB  $defs{FritzBox}{fhem}  (116.4 kB -> 118.1 kB)
       +1.4 kB  $defs{MQTT2_Server_192.168.2.217_14887}{subscriptions}  (0 B -> 1.4 kB)
       +1.4 kB  $defs{MQTT2_Server_192.168.2.217_23695}{subscriptions}  (0 B -> 1.4 kB)
       +1.4 kB  $defs{MQTT2_Server_192.168.2.217_5254}{subscriptions}  (0 B -> 1.4 kB)
       +1.4 kB  $defs{MQTT2_Server_192.168.2.217_5887}{subscriptions}  (0 B -> 1.4 kB)
       +1.2 kB  $defs{diULANZI}{helper}  (3.0 kB -> 4.2 kB)

Devices - growth (top 15):
      +23.0 kB  FritzOG  (471.0 kB -> 493.9 kB)
      +18.3 kB  FritzWohnzimmer  (498.5 kB -> 516.8 kB)
       +6.1 kB  MQTT2_Server_192.168.2.217_14887  (0 B -> 6.1 kB)
       +6.1 kB  MQTT2_Server_192.168.2.217_23695  (0 B -> 6.1 kB)
       +6.1 kB  MQTT2_Server_192.168.2.217_5254  (0 B -> 6.1 kB)
       +6.1 kB  MQTT2_Server_192.168.2.217_5887  (0 B -> 6.1 kB)
       +3.2 kB  telnetPort_127.0.0.1_48906  (0 B -> 3.2 kB)
       +2.2 kB  FritzBox  (288.2 kB -> 290.4 kB)
       +1.9 kB  cm  (5.8 kB -> 7.7 kB)
       +1.7 kB  FritzDach  (201.6 kB -> 203.3 kB)
       +1.2 kB  diULANZI  (30.9 kB -> 32.1 kB)

Device types (sum of devices) - growth (top 15):
      +44.5 kB  FritzSmart  (1.77 MB -> 1.82 MB)
      +18.6 kB  MQTT2_SERVER  (713.5 kB -> 732.1 kB)
       +1.9 kB  CUL_MAX  (5.8 kB -> 7.7 kB)

Number of devices per type - growth (top 15):
            +3  MQTT2_SERVER  (30 -> 33)

File-scope variables of module files (PadWalker) - growth (top 15):
     +144.4 kB  01_FHEMWEB.pm  %FW_svgData  (12.18 MB -> 12.32 MB)

Module files (file-scope variables, PadWalker) - growth (top 15):
     +144.1 kB  01_FHEMWEB.pm  (13.11 MB -> 13.25 MB)

Hint: for a reliable verdict take at least 3 snapshots some hours apart and use "get  leakCandidates".

Einen Screenshot zur Speicherauswertung habe ich mal angehängt.
Dasuntere Diagramm kommt von sysmon.