I've made an effort to reduce the size of the static assets of my theme to speed up the page loading on mobile platforms; my target was a normal G2 connection, so 300 ms of latency and 250 Kb/s of downstream bandwidth.
The elephant in the room was the JQuery library required by Bootstrap, in my case was used only for the collapsible navigation bar, so it was the first thing I tried to tackle. I looked to compile only the required module, but the gain was minimal. Next I tried automatic tools for dead code elimination and "three shaking" but they didn't produced a gain either. Fortunately before I started to write a solution by my own, I've found an actively developed library created to replaced JQuery as a drop in replacement, so with the bootstrap.native library it passed from 120 KByte uncompressed to less than 3.5 KByte uncompressed.1
I slightly modified the Javascript to not be completely broken on IE8, still some glitches remain, and arranged the theme to not be completely unusable if Javascript is disabled.
I used the occasion to fix an ugly bit that was present in the previous iteration. The current wisdom is to put all the Javascript functions at the end of a page to speed up the rendering, but with my theme, when an images gallery was added to an article, a Javascript function necessary to initialize the gallery unfortunately ended in the middle of the index page, as I couldn't find a way with Jinja to pass a value from a child template to a parent template if the value was defined inside an include.
I was stuck forever with this and I suspect a bug as it's supposed to work according to Jinja's documentation. I ended up using a workaround abusing the global context:
{% set _ = gallery_required.append(gallery_name) %}
It's not a clean approach and it risks to not work in a future version, but I couldn't find a better solution. When I have some time I need to isolate a test case and report it upstream.
Unfortunately the current stable version (2.9) fixed a bug that enforce the scoping of variables modified inside code blocks and it broke the rendering of my archives.html
page. Considering the design's decisions, I'm starting to doubt that Jinja is flexible enough for a static site generator.
Anyway I rearranged the archives code without values passed using variables and now should be fixed for the foreseeable future:
{% for year, articles in dates| groupby('date.year')| reverse %}
{{ year }}
{% for article in articles %}
{{ article.date.strftime("%d %B") }} - {{ article.title }}
{% endfor %}
{% endfor %}
I've experimented with WebFonts in the theme, i.e. if the primary font choice wasn't installed in the system, a webfont was used as a secondary choice2. I've verified that Firefox doesn't show glitches but testing it with Chrome there was an ugly FOIT when loading the index page, so for the platforms that don't have Georgia installed I'm sorry, you have to use your system version of a serif font.
Another thing that I've played with is what is called "critical CSS".
There is a Google developer page where it's suggested to inline in the page's header tag only the portion of CSS necessary to style the elements displayed on the view-port3, defer the loading of the full CSS file as one of the last operations and finally inject it in the page when the file is downloaded.
I've implemented the thing, but considering that the full CSS when minified it's only about 20 KByte, I've toyed with the idea to put all my CSS inline.
There's still a thing that's broken: Bootstrap doesn't do anything special with the abbr
HTML tag4, so on devices with touchscreen, abbreviations don't work. It's in the to do list.
The final tuning was to enable HTTP/2, gzip compression and cache expiration for the static assets and moving the page encoding from the HTML to the web server headers.
I haven't extensively bench-marked the page loading, but throttling the network parameters to simulate a regular 2G connection, the process of loading and displaying an index page with a cold cache is completed in less than two seconds, the painting of the view-port above the fold happens after less than 700 ms. On a desktop it's near a half second, not bad considering that the server I'm using is now in the East of North America.
It seems that I'm frequently involved in a recurring scenario: I tweak something, a couple of days later I discover that an unrelated thing was broken by the tweak, I fall down the rabbit hole to fix the issue.
Today is the notebook's keyboard backlight, I don't even use the thing as I always keep it off, I find the light distracting.
So, a few months ago I started using a recent kernel otherwise a third party application causes the Intel Xorg driver to segfault. I think last week I spotted that a new kernel package was available, so I installed the new version without too much attention, but it wasn't a minor release and only today I realized that the backlight of the keyboard now turns on every time the notebook is resumed after a suspend or simply unlocking the screen lock.
A quick Google search pointed to a mix of a new kernel feature1 and UPower behavior, digging some more I found the freedesktop.org bug #95457 with a patch that should have fixed the issue in the upstream version.
- "It will took me half an hour tops to port it to the version in Debian stable and creating a package" - or that's what I thought.
It's a straight forward patch, it changes the behavior of the UPower daemon to check the brightness level instead of using the last saved value. The problem was that my modified daemon didn't fully worked, the backlight seemed to behave correctly or at least it didn't turn on after resume, but I couldn't query the brightness level using D-Bus and the "failed to convert brightness" error was present on syslog, so something was wrong with my version of the patch.
Bummer.
I spent a considerable amount of time staring at my code, searching for the error, but all looked consistent with what was applied upstream. So I rebuilt the package with the debug symbols2,
$ DEB_BUILD_OPTIONS='nostrip noopt debug' dpkg-buildpackage -b -uc -us
installed the new package, killed the daemon, launched it attached with gdb and set a break-point on the up_kbd_backlight_brightness_read
function, in other words, standard boring C development.
A couple of minutes later, it turned out that I didn't introduced new bugs, the issue was with the upstream patch itself in the *end != '\0'
test case, a off by 1 example. I found the confirmation in the UPower's git log and it was also tracked by the bug #96215.
So now I can query and set the brightness using D-Bus3 and the state of the backlight is preserved like I was used to:
$ gdbus call --system \n
--dest org.freedesktop.UPower \n
--object-path /org/freedesktop/UPower/KbdBacklight \n
--method org.freedesktop.UPower.KbdBacklight.GetBrightness
If someone is interested in the patch for Debian Jessie the diff below apply to the upower_0.99.1-3.2_amd64.deb package:
--- upower-0.99.1/src/up-kbd-backlight.c 2013-10-29 11:37:08.000000000 +0100
+++ upower-0.99.1.mod/src/up-kbd-backlight.c 2017-01-23 15:37:54.022599766 +0100
@@ -47,7 +47,6 @@
struct UpKbdBacklightPrivate
{
gint fd;
- gint brightness;
gint max_brightness;
DBusGConnection *connection;
};
@@ -62,10 +61,41 @@
G_DEFINE_TYPE (UpKbdBacklight, up_kbd_backlight, G_TYPE_OBJECT)
/**
+ * up_kbd_backlight_brightness_read:
+ **/
+static gint
+up_kbd_backlight_brightness_read (UpKbdBacklight *kbd_backlight)
+{
+ gchar buf[16];
+ gchar *end = NULL;
+ ssize_t len;
+ gint64 brightness = -1;
+
+ g_return_val_if_fail (kbd_backlight->priv->fd >= 0, brightness);
+
+ lseek (kbd_backlight->priv->fd, 0, SEEK_SET);
+ len = read (kbd_backlight->priv->fd, buf, G_N_ELEMENTS (buf) - 1);
+
+ if (len > 0) {
+ buf[len] = '\0';
+ brightness = g_ascii_strtoll (buf, &end, 10);
+
+ if (brightness < 0 ||
+ brightness > kbd_backlight->priv->max_brightness ||
+ end == buf) {
+ brightness = -1;
+ g_warning ("failed to convert brightness: %s", buf);
+ }
+ }
+
+ return brightness;
+}
+
+/**
* up_kbd_backlight_brightness_write:
**/
static gboolean
-up_kbd_backlight_brightness_write (UpKbdBacklight *kbd_backlight, gint value)
+ up_kbd_backlight_brightness_write (UpKbdBacklight *kbd_backlight, gint value)
{
gchar *text = NULL;
gint retval;
@@ -96,9 +126,8 @@
}
/* emit signal */
- kbd_backlight->priv->brightness = value;
g_signal_emit (kbd_backlight, signals [BRIGHTNESS_CHANGED], 0,
- kbd_backlight->priv->brightness);
+ value);
out:
g_free (text);
@@ -113,8 +142,20 @@
gboolean
up_kbd_backlight_get_brightness (UpKbdBacklight *kbd_backlight, gint *value, GError **error)
{
+ gint brightness;
+
g_return_val_if_fail (value != NULL, FALSE);
- *value = kbd_backlight->priv->brightness;
+
+ brightness = up_kbd_backlight_brightness_read (kbd_backlight);
+
+ if (brightness >= 0) {
+ *value = brightness;
+ } else {
+ g_set_error_literal (error, UP_DAEMON_ERROR,
+ UP_DAEMON_ERROR_GENERAL,
+ "error reading brightness");
+ }
+
return TRUE;
}
@@ -226,23 +267,13 @@
goto out;
}
- /* read brightness */
+ /* open the brightness file for read and write operations */
path_now = g_build_filename (dir_path, "brightness", NULL);
- ret = g_file_get_contents (path_now, &buf_now, NULL, &error);
- if (!ret) {
- g_warning ("failed to get brightness: %s", error->message);
- g_error_free (error);
- goto out;
- }
- kbd_backlight->priv->brightness = g_ascii_strtoull (buf_now, &end, 10);
- if (kbd_backlight->priv->brightness == 0 && end == buf_now) {
- g_warning ("failed to convert brightness: %s", buf_now);
- goto out;
- }
- /* open the file for writing */
kbd_backlight->priv->fd = open (path_now, O_RDWR);
- if (kbd_backlight->priv->fd < 0)
+
+ /* read brightness and check if it has an acceptable value */
+ if (up_kbd_backlight_brightness_read (kbd_backlight) < 0)
goto out;
/* success */
@@ -315,4 +346,3 @@
{
return g_object_new (UP_TYPE_KBD_BACKLIGHT, NULL);
}
Last week I've found out that the battery in my phone didn't last a day and according to the phone power estimations, a fully charged battery provided a little less than 9 hours of idle time.
Bummer.
My phone is an old Android terminal, it has already passed his manufacturer support life at least two times, but I'm really appalled by the current offer in the market. There's exactly one manufacturer that still produces a high-end Android model with a display under 5 inches1, I really hate the current trend of phones with ginourmous displays that can't fit in a trouser pocket. And nope, I'm not so keen to adopt Apple's ecosystem.
A reboot didn't solve the issue and at that moment I wasn't yet sure if it was a hardware or a software problem. Considering the age of the phone, I was leaning for a dying battery.
Turned out instead, the OS had stopped entering deep sleep state at all, a.k.a. the lowest power mode. So diagnosis was easy, at least one process was not releasing a "wakelock", a function in the Android power management stack where a process can keep the OS "awake", as the name suggests. Thorough analysis2 pointed specifically to the suspend_backoff
wakelock, but trying to find the guilty program was fruitless, as disabling first and then removing a bunch of applications installed with the last update was fruitless.
Before proceeding with a factory reset, a process that I dread, I tried to clean the Dalvik cache as a last attempt. How do you clean a cache? Oh my, you delete its content! I always have trouble recalling the button sequence for boot in recovery mode: is it power and volume up or is it power and volume down? Anyway, the recovery image I installed didn't have that option, or at least I couldn't find it.
Well, if one has access to the root user on the phone, cleaning the Dalvik cache it's a matter of seconds:
$ adb shell
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
$ su
# cd /data/dalvik-cache/
# ls -l
drwx--x--x root root 2016-12-23 19:08 arm
drwx--x--x system system 2016-12-23 19:08 profiles
# rm -r arm profiles
# reboot
Faster than saying suspend_backoff
wakelock!
After the reboot I've verified that the OS could finally transition to deep sleep state again. Brilliant, it looks like I don't have an excuse to buy a new one.
Bummer.