Repository: qtile/qtile

Issue:
CPU usage leak
Running qtile master. Over a timespan of about 24 hours, qtile's CPU usage rises from trivially low to nearly 100%. In the worst stages, qtile is non-responsive to keyboard shortcuts, forcing me to kill it externally.



Discussion:
I think this should be fixed in the latest development branch. Can you try
that and see if it fixes your problem?
On Mar 5, 2014 9:33 AM, "David Simon" notifications@github.com wrote:

> Running qtile master. Over a timespan of about 24 hours, qtile's CPU usage
> rises from trivially low to nearly 100%. In the worst stages, qtile is
> non-responsive to keyboard shortcuts, forcing me to kill it externally.
> 
> ## 
> 
> Reply to this email directly or view it on GitHubhttps://github.com/qtile/qtile/issues/379
> .

Sorry, meant to say in the beginning that I was running on develop branch. :-)

Ok, what widgets are you using?
On Mar 5, 2014 10:14 AM, "David Simon" notifications@github.com wrote:

> Sorry, meant to say in the beginning that I was running on develop branch.
> :-)
> 
> ## 
> 
> Reply to this email directly or view it on GitHubhttps://github.com/qtile/qtile/issues/379#issuecomment-36774249
> .

``` python
##-> Screens
screens = [
    Screen(
        top=bar.Bar(widgets=[
            widget.GroupBox(**Theme.groupbox),
            widget.Prompt(),
            widget.WindowName(**Theme.widget),

            widget.CPUGraph(graph_color='18BAEB', fill_color='1667EB.3', **Theme.graph),
            widget.MemoryGraph(graph_color='00FE81', fill_color='00B25B.3', **Theme.graph),

            #widget.CurrentLayout(**Theme.widget),
            widget.Systray(**Theme.systray),
            #widget.Sep(**Theme.sep),
            widget.BatteryIcon(**Theme.battery),
            widget.Battery(**Theme.battery_text),
            #widget.Sep(**Theme.sep),
            widget.Volume(theme_path='/usr/share/icons/Humanity-Dark/status/24/', **Theme.widget),
            widget.Clock(fmt='%a %d %b %I:%M %p', **Theme.widget),
            ], **Theme.bar),
    ),
    Screen(
        top=bar.Bar(widgets=[
            widget.GroupBox(**Theme.groupbox),
            widget.WindowName(**Theme.widget),
            #widget.CurrentLayout(**Theme.widget),
        ], **Theme.bar),
    )
]
```

@tych0 Any thoughts on which widget might be causing the leak? I can just start removing them in a binary-search pattern to narrow it down, but since it takes several hours for the leak to be noticeable, that could take a while without a hint.

I've disabled the graph and battery indicator widgets, and the leak seems to be gone. I'll try re-enabling the battery indicator and seeing what happens.

@tych0 Still leaks with only the battery indicator (i.e. not the graph) enabled. I'll leave them both off again and see if that confirms the problem.

Hi David,

Which Battery indicator are you using, text, icon or both?

I use Text and have not noticed any memory issues with it.

On 18 March 2014 12:18, David Simon notifications@github.com wrote:

> @tych0 https://github.com/tych0 Still leaks with only the battery
> indicator (i.e. not the graph) enabled. I'll leave them both off again and
> see if that confirms the problem.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/qtile/qtile/issues/379#issuecomment-37926022
> .

## 

Craig

I was using both. I've verified that there is no leak when they're both disabled. I'll try just enabling the icon one.

Hi David,

On Wed, Mar 19, 2014 at 06:26:32AM -0700, David Simon wrote:

> I was using both. I've verified that there is no leak when they're both disabled. I'll try just enabling the icon one.

Thanks, that would be great. Both icons are using code that is fairly
fundamental to qtile, so if there is a leak there, we're leaking
everywhere and it would be good to fix :-).

\t

@tych0 I have verified that the leak occurs even if only the icon indicator is enabled, though it is a significantly slower leak.

There's got to be a better way to instrument this. :-| What profiler tool for Python is useful for this sort of thing?

I'm running on the v0.7.0 tag and both `widget.Volume` and `widget.Battery` use lots of CPU.

@DavidMikeSimon you can run qtile with 

```
python -m cProfile -o /some/file.profile qtile
```

And after you stop qtile, you can inspect /some/file.profile with RunSnakeRun (or KCachegrind, but I have a KDE aversion).

I  also had this problem with the text Battery widget. Qtile was trying to open a non-existent file to get the battery status (only a snippet of strace output, it will try to open each file more than 80 times): 

```
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
13:39:58 open("/sys/class/power_supply/BAT0/charge_now", O_RDONLY) = -1 ENOENT (No such file or directory)
```

Setting battery_name to BAT1 at the widget creation fixed it for me.
I'm not familiar enough with qtile widget system to pinpoint the problem, but it seems that some widget are called way too often, regardless of the update_interval value, and depending on what the widget does, it might uses large amount of CPU.


Resolving patch:
diff --git a/libqtile/widget/battery.py b/libqtile/widget/battery.py
--- a/libqtile/widget/battery.py
+++ b/libqtile/widget/battery.py
@@ -74,11 +74,14 @@ def _load_file(self, name):
             self.log.exception("Failed to get %s" % name)
 
     def _get_param(self, name):
-        if name in self.filenames:
+        if name in self.filenames and self.filenames[name]:
             return self._load_file(self.filenames[name])
-        else:
+        elif name not in self.filenames:
             # Don't have the file name cached, figure it out
-            file_list = BATTERY_INFO_FILES.get(name, [])
+
+            # Don't modify the global list! Copy with [:]
+            file_list = BATTERY_INFO_FILES.get(name, [])[:]
+
             if getattr(self, name, None):
                 # If a file is manually specified, check it first
                 file_list.insert(0, getattr(self, name))
@@ -90,7 +93,10 @@ def _get_param(self, name):
                     self.filenames[name] = file
                     return value
 
-        # If we made it this far, we don't have a valid file. Just return None.
+        # If we made it this far, we don't have a valid file.
+        # Set it to None to avoid trying the next time.
+        self.filenames[name] = None
+
         return None
 
     def _get_info(self):


Test patch: