https://bugs.kde.org/show_bug.cgi?id=521757

[email protected] changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |[email protected]

--- Comment #5 from [email protected] ---
Answering the request in comment for dbus-monitor / bustle output showing why
resultRemoved() fires so often. The signal storm has a specific cause:
DeleteStatsForResource can never delete a resource whose path contains a
backslash, so the caller retries it forever.

SOFTWARE/OS VERSIONS
Operating System: Arch Linux
KDE Plasma Version: 6.7.4
plasma-workspace: 6.7.4-3
kactivitymanagerd: 6.7.4-1
plasma-activities: 6.7.4-1
plasma-activities-stats: 6.7.4-1
Qt Version: 6.11.2
SQLite: 3.53.4
Kernel: 7.0.3-arch1-2
Graphics Platform: Wayland


THE DBUS EVIDENCE

dbus-monitor --session captured for 10 seconds while one core was pegged:

    1345 member=ResourceScoreDeleted
    1344 member=DeleteStatsForResource

13277 session bus messages in 10 seconds, of which 2689 are this loop. That is
roughly 134 delete attempts per second, sustained. All 1344 calls carried an
identical argument set:

    method call sender=:1.31 -> destination=org.kde.ActivityManager
      path=/ActivityManager/Resources/Scoring;
      interface=org.kde.ActivityManager.ResourcesScoring;
      member=DeleteStatsForResource
       string ":current"
       string ":any"
       string
"/home/too/SharedRDP/temp/cypress\snapshots/3879-swa-question-text.snap.png"

    signal sender=:1.29 -> destination=(null destination)
      interface=org.kde.ActivityManager.ResourcesScoring;
      member=ResourceScoreDeleted

:1.31 is plasmashell, :1.29 is kactivitymanagerd. The file does not exist on
disk. The row for it was still in ResourceScoreCache after 37 days of this.


ROOT CAUSE

Note the backslash in the path. In
/usr/lib/qt6/plugins/kactivitymanagerd1/org.kde.ActivityManager.ResourceScoring.so:

    DELETE FROM ResourceInfo WHERE targettedResource LIKE :targettedResource
ESCAPE '\'

and the same predicate:

    targettedResource LIKE :targettedResource ESCAPE '\'

is reused for the other resource tables. ESCAPE '\' is presumably there so
callers can pass wildcard patterns. The side effect is that a backslash
occurring literally inside a stored path is reinterpreted as an escape
introducer: cypress\snapshots is matched as cypresssnapshots, and the statement
affects zero rows.

Verified against a copy of the live database:

    sqlite> select count(*) from ResourceScoreCache
       ...> where targettedResource =
'/home/too/SharedRDP/temp/cypress\snapshots/3879-swa-question-text.snap.png';
    1

    sqlite> select count(*) from ResourceScoreCache
       ...> where targettedResource LIKE
'/home/too/SharedRDP/temp/cypress\snapshots/3879-swa-question-text.snap.png'
ESCAPE '\';
    0

The row is reachable by equality and unreachable by the predicate the service
actually uses.


WHY IT NEVER TERMINATES

1. The consumer in plasmashell holds a resource entry for a file that no longer
   exists and calls DeleteStatsForResource on it.
2. kactivitymanagerd runs the LIKE ... ESCAPE delete. Zero rows affected, but
   ResourceScoreDeleted is emitted unconditionally.
3. The consumer treats the signal as a change notification, re-reads, finds the
   entry still present, and returns to step 1.

Neither side checks the affected row count, and neither has a retry limit or
backoff, so this does not converge. This is why
KActivities::Stats::ResultWatcher::resultRemoved() re-runs its full SQLite
query
millions of times and produces the allocation profile reported in this bug. The
139051509 allocation calls are the symptom, not the cause.


MEASURED COST (before clearing the database)

                                  kactivitymanagerd      plasmashell
    read syscalls/sec                       202,702           72,457
    read throughput                        790 MB/s         280 MB/s
    lifetime rchar                           18.3 TB          6.7 TB
    lifetime syscr                      4.49 billion     1.64 billion
    CPU consumed in 37 day boot             3h 44m    2h 13m hot thread

The database is 9 MB, so 790 MB/s means it was being re-scanned from page cache
roughly 85 times per second. read_bytes was only 225 MB, so almost none of it
reached the disk.

Two secondary effects:
  - The SQLite WAL grew to 12 MB, larger than the 9 MB database itself, because
    continuous reader traffic prevented checkpointing.
  - plasmashell had written 27.5 GB to disk over the boot.

An earlier boot shows the same signature in the journal:

    plasma-kactivitymanagerd.service: Consumed 15h 42min 42.473s CPU time over
    2w 3d 5h 42min 59.973s wall clock time, 55.4M memory peak

So it survives reboots, because the offending rows are persistent.


CONFIRMATION

199 rows contained a literal backslash: 54 in ResourceScoreCache, 84 in
ResourceEvent, 61 in ResourceInfo, all recorded on 2026-05-12. After deleting
them and restarting both services:

                                            before        after
    loop messages per 10s                     2689            0
    total session DBus messages per 10s      13277          301
    kactivitymanagerd read syscalls/sec     202,702            0
    plasmashell read syscalls/sec            72,457           28
    kactivitymanagerd CPU                     60.6%         0.1%
    WAL size                                  12 MB            0

kactivitymanagerd used 0 seconds of CPU in its first 95 seconds after restart.
PRAGMA integrity_check returned ok.

This explains why "clear the resources directory" works as a workaround for
everyone in this bug: it removes the undeletable rows.


HOW THE ROWS GOT THERE

Cypress and Playwright runs on Windows wrote output to a Linux filesystem over
an RDP share, so the Windows path separator ended up inside the Linux filename,
for example ~/SharedRDP/temp/cypress\snapshots/foo.snap.png. Any path
containing
a literal backslash should reproduce it. Paths containing % or _ are likely
affected the same way, since those are also LIKE metacharacters.

Minimal reproducer, without needing Windows:

1. touch 'foo\bar.txt' somewhere in $HOME and open it in a Plasma application
so
   it is recorded as a resource.
2. rm 'foo\bar.txt'
3. Log out and back in, then leave the session idle.


SUGGESTED FIX

Any one of these breaks the livelock. The first is the actual defect.

1. Do not use LIKE ... ESCAPE for what is an exact-identity delete. Match with
=
   on the resource path. If wildcard deletion is a required feature, keep it on
   a separate explicit code path rather than applying pattern semantics to
every
   caller-supplied path.
2. If LIKE must stay, escape the bound value before binding, so a literal \, %
   or _ in a stored path is passed through as itself.
3. Defensively, emit ResourceScoreDeleted only when the statement actually
   affected rows. That alone would stop the feedback loop even where a delete
   legitimately matches nothing.
4. Defensively, have the consumer in plasma-activities-stats stop re-requesting
   deletion of a resource whose deletion it has already requested without
   effect.


POSSIBLY RELATED

Bug 508766 (database not cleared by "Clear History" in Security and Privacy)
may share this root cause, since a backslash row would resist that path too.

Bug 470026 and its duplicate bug 484837 blame an oversized recently-used.xbel.
That is a different trigger and does not apply here: this system's xbel is 41
KB
with 305 entries. However comment 14 of bug 470026 suspects a regression
affecting DeleteStatsForResource calls, which may be this same defect surfacing
by another route.
------------------------------- CUT HERE --------------------------------------



================================================================================
FALLBACK
Only if bug 521757 is judged to be a different issue and a fresh report is
wanted. Uses the standard KDE bug entry template.
================================================================================

Field values for the New Bug form:

    Product:      plasmashell
    Component:    The Activities subsystem in general
    Version:      6.7.4
    Severity:     major        (permanent 100% CPU on one core, never recovers)
    Platform:     Archlinux Packages
    OS:           Linux

------------------------------- CUT HERE --------------------------------------
SUMMARY
DeleteStatsForResource can never delete a resource whose path contains a
backslash, because the delete matches with LIKE :targettedResource ESCAPE '\'
and the backslash in the stored path is consumed as an escape character. The
statement affects zero rows, ResourceScoreDeleted is emitted anyway, the
consumer in plasmashell re-reads, finds the resource still present and requests
deletion again. plasmashell and kactivitymanagerd then livelock at roughly 134
iterations per second indefinitely.


STEPS TO REPRODUCE
1. Create a file whose name contains a literal backslash, for example
   touch 'foo\bar.txt' in $HOME, and open it in a Plasma application so it is
   recorded as a resource in the activity database.
2. Delete the file so the recorded resource no longer resolves.
3. Log out, log back in, and leave the session idle.


OBSERVED RESULT
kactivitymanagerd and plasmashell each begin consuming CPU continuously and
never stop. dbus-monitor --session shows the pair exchanging
DeleteStatsForResource and ResourceScoreDeleted about 134 times per second, all
referring to the same single resource. The row is never removed. The condition
persists across reboots because the row is persistent.

Measured on the affected system: 202,702 read syscalls/sec in kactivitymanagerd
and 72,457/sec in plasmashell, 790 MB/s and 280 MB/s of page-cache re-reads
against a 9 MB database, 18.3 TB lifetime rchar, and 3h44m plus 2h13m of CPU
consumed in a single 37 day boot.


EXPECTED RESULT
DeleteStatsForResource removes the named resource, whatever characters its path
contains, and the loop terminates.


SOFTWARE/OS VERSIONS
Operating System: Arch Linux
KDE Plasma Version: 6.7.4
KDE Frameworks Version: 6.7.4
Qt Version: 6.11.2
Kernel Version: 7.0.3-arch1-2
Graphics Platform: Wayland


ADDITIONAL INFORMATION
The offending SQL is in
/usr/lib/qt6/plugins/kactivitymanagerd1/org.kde.ActivityManager.ResourceScoring.so:

    DELETE FROM ResourceInfo WHERE targettedResource LIKE :targettedResource
ESCAPE '\'

Demonstrated against a copy of the live database, the row is reachable by
equality and unreachable by the predicate the service uses:

    select count(*) from ResourceScoreCache
      where targettedResource =
'/home/too/SharedRDP/temp/cypress\snapshots/3879-swa-question-text.snap.png';
    1

    select count(*) from ResourceScoreCache
      where targettedResource LIKE
'/home/too/SharedRDP/temp/cypress\snapshots/3879-swa-question-text.snap.png'
ESCAPE '\';
    0

Workaround, which also confirms the diagnosis:

    cp -r ~/.local/share/kactivitymanagerd/resources \
          ~/.local/share/kactivitymanagerd/resources.bak-$(date +%F)
    systemctl --user stop plasma-kactivitymanagerd
    sqlite3 ~/.local/share/kactivitymanagerd/resources/database \
      "DELETE FROM ResourceScoreCache WHERE
instr(targettedResource,char(92))>0;
       DELETE FROM ResourceEvent      WHERE
instr(targettedResource,char(92))>0;
       DELETE FROM ResourceInfo       WHERE
instr(targettedResource,char(92))>0;
       PRAGMA wal_checkpoint(TRUNCATE);
       VACUUM;
       PRAGMA wal_checkpoint(TRUNCATE);"
    systemctl --user start plasma-kactivitymanagerd
    systemctl --user restart plasma-plasmashell

plasmashell must be restarted too, otherwise it keeps its in-memory copy of the
stale entry and restarts the loop against the now-clean database.

After this the loop messages drop from 2689 per 10s to 0, kactivitymanagerd
read
syscalls drop from 202,702/sec to 0, and it uses 0 seconds of CPU in its first
95 seconds. PRAGMA integrity_check returns ok.

See bug 521757, which reports the same symptom and workaround on 6.7.0 through
6.7.4 and identifies the resultRemoved() allocation storm that this explains.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to