Kompeilerfehler des tages

Der gcc „optimiert“ bitoperazjonen so „gut“, dass das ergebnis falsch ist. Und nein, dazu muss man ihm nicht eigens sagen, dass er optimieren soll (obwohl es dann natürlich immer noch ein fehler im gcc wäre). Andere kompeiler übersetzen den beispielkohd richtig. (Gut, ich habs nur mit LLVM getestet.)

Wer den gcc nutzt oder nutzen muss und gerade mit seltsamen, ziemlich unverständlichen problemen zu kämpfen hat, wenn er ein bisschen shiftet, das komplement bildet oder ein paar bits setzt, maskiert, invertiert — ein ganz schneller tipp ohne jede garantie von mir: nehmt durchgehend unsigned und hängt an jede konstante ein U dran, dann scheint dieser fehler nicht aufzutreten.

Ändräut des tages

Willste professjoneller häcker werden? Da gibts ne äpp für! 😀

Achtung: der link führt in den guhgell-pläjhstohr und damit mittenmang in die guhgell-skripthölle; eine versjon als bildschirmfoto gibts hier. Die äpp wurde auch schon zwischen 50.000 und 100.000 mal installiert, und der zugriff aufs netzwerk wird sicherlich für nichts übles benutzt werden…

Für alle freunde von „doom“

Ein kleines leckerchen für alle freunde des altmodischen metzelns und alle softwäjhr-archäologen, das jetzt ein paar monate lang völlig an mir vorbeigegangen ist, obwohl ich es sehr interessant und faszinierend finde: John Romero hat da wohl ein paar alte bäckups auf einer verstaubten platte seiner ollen NeXT-stäschen gefunden und eine interne prebeta von doom 2 inklusive kwelltexten gefunden und zum daunlohd gestellt. Andere haben aus den unvollständigen daten (es fehlen jede menge texturen) dann ein funkzjonsfähiges WAD gebaut. Der damalige, teilweise interessante stand der texturen gehört ebenfalls dazu… 😎

Die links aus dem doomworld-forum, die jetzt nicht mehr funkzjonieren, sind kein problem, denn es gibt einen mirror bei archive.org.

Zum anspielen der prebeta-level benötigt man das WAD von doom 2.

Bildschirmfoto von level 7, 'dead simple' in der prebeta-versjon

Die meisten level sind den entsprechenden leveln in doom 2 sehr ähnlich, und alles, was darin anders ist, ist in der veröffentlichten versjon natürlich besser. Einige wenige level sehen sehr anders aus, und wenn man sie mal anspielt, sieht man auch schnell, warum diese ideen schließlich verworfen worden. Die texturen sind oft noch roh, und die mühe eines korrekten alignments wurde mehrfach eingespart. Das bildschirmfoto¹ oben ist map07 aus der prebeta, „dead simple“, das in dieser versjon nicht nur viel zu „simple“ ist, sondern anders als die offene arena mit den mancubi und arachnotrons auch kein gutes spiel abgibt, geschweige denn ein gutes deathmatch für das fröhliche gegenseitige abmetzeln. Es ist übrigens so wenig fertiggestellt, dass es nicht einmal eine möglichkeit gibt, den level zu verlassen. Sehr gut, dass dieser level für die veröffentlichung völlig neu gemacht wurde!

Generell „fühlen“ sich viele dieser beta-level mehr nach doom 1 an, insbesondere erinnert der startraum von map01 von seinen texturen her ein bisschen an das ende von E3M8; die neuen gegner kommen einem allerdings schon in ungewohnt frühen leveln entgegen.

Wer noch ein olles doom rumliegen hat und sich mal ein wenig anschauen möchte, wie dieses in meinen augen großartige frühe ballerspiel entstanden ist, sollte diese großartige gelegenheit nutzen. Ich empfehle allerdings die benutzung eines modernen sourceports, denn inzwischen hat ja jeder mehr als einen alten 486er rumstehen. Sehr hübsch (aber im rendering deutlich anders als das alte origjnal) ist gzdoom, und wer es möglichst genau so haben will, wie es damals war (einschließlich aller fehler und einschränkungen), der hole sich chocolate doom. Ein großer, fetter dank an id software, dass der kwelltext freigegeben wurde, so dass es auch gute softwäjhr für moderne kompjuter gibt. Auf gar keinen fall und niemals, niemals, niemals dieses beschiss… ähm… bescheidene doom 95 nehmen, das damals für windohs verkauft wurde²! Da war die DOS-versjon besser und fehlerfreier!

Die originalen kwelltexte der hilfsprogramme von id software (einschließlich des origjnalen level-editors) sind allerdings nicht ohne weiteres nutzbar. Entwickelt wurde doom ja unter next OS, und deshalb natürlich in objective C und für die beste entwicklungsumgebung aller zeiten. (Darauf läuft der olle editor auch gut, und ja, das bildschirmfoto zeigt eine grafische oberfläche aus den frühen neunziger jahren.) Eigentlich sollte es möglich sein, die kwelltexte für mac OS X anzupassen. Einen versuch mit GNUstep habe ich nicht gemacht, aber ich halte es für möglich, dass man damit nur wenig bastelei hat. Vermutlich gehts am einfachsten (ich bin noch nicht dazu gekommen), wenn man sich openstep in einer virtuellen maschine installiert und damit kompiliert. Beim ersten überfliegen sah die kohdkwalität ziemlich gut aus, es handelt sich also nicht um heftig tricksende häckware, die vermutlich nur beim passenden winkel der mondeinstrahlung durch den kompeiler läuft. Wer es hinbekommt, erhält jedenfalls einen sehr genauen eindruck davon, in welcher umgebung damals mitte der neunziger jahre der vater aller „richtigen egoshooter“ entstanden ist… 😉

Also: viel spaß!

(Hach, und jetzt noch mal ähnlich tiefe einblicke in die entwicklung von quake, bitte!)

¹Angefertigt mit gzdoom, deshalb sieht es deutlich besser als in den neunziger jahren aus. Natürlich verbessert sich die auflösung der texturen damit nicht…

²Man kann doom 95 ziemlich zuverlässig zum absturz bringen, indem man auf die karte schaltet und eine markierung für seine posizjon setzt. Oder, indem man einfach doom damit spielt. Die maus funkzjoniert übrigens nur, wenn man sich irgendwoher eine olle VxD runterlädt. Das ding ist so mies, es könnte von meikrosoft sein…

„Emacs“ des tages

Ich warte ja immer noch darauf, dass sich ein paar leute hinsetzen und das „emacs OS for phones“ in dieser schrecklichen programmiersprache mit den vielen vielen klammern kohden. (Psst, nicht so laut beim lesen! Es könnte jemand hören und für eine gute idee halten!) Auf diesem weg hinein in den totalen programmierdadaismus ist es immerhin ein kleines stück vorangegangen: „emacs“ kann jetzt auch windowmanager für X

Natürlich ist dieser kohd noch ziemlich alfa. Und: ein schöner gruß aus dem sommerloch.

C-X C-C 😉

Dem programmierer empfehle ich wärmstens M-X doctor.

„Stackoverflow“ des tages

Das heitere pohsting des tages (in englisch, und eher für progger und häcker) ist die zurzeit oben stehende antwort auf diese kleine frage bei „stackoverflow“… 😉

Die frage, warum jemand der HTML parst, nicht einfach einen fertigen, bewährten und gut getesteten HTML-parser (oder, wenns XHTML ist, einen fertigen XML-parser) benutzt, wird darin allerdings nicht beantwortet. Genau das sollte man nämlich tun, wenn man vor diesem problem steht und keine lust auf die ganzen kopfschmerzen mit kommentaren, skriptbereichen und self contained tags hat. Geeigneten kohd sollte es für jede ernstzunehmende programmiersprache (also nahezu alles außer FORTRAN und COBOL) geben.

Objektorientierte programmierung…

Erstmal ein kleiner aufwärmer, der achtzig prozent der leser zum schließen des brausertabs bringt — die restlichen zwanzig prozent können sich ja fragen, was daran falsch ist:

class ID
{
  private:
  static unsigned int nextId = 1;
  unsigned int myId;

  public:
  ID () : myId(nextId++) { }
  unsigned int getID () { return myId; }
};

Ich bin leider auf dem sprung und kann deshalb nicht mal eben schnell einen anreißer dieses „kleinen“ englischen textchens mit dem titel „objektorientierte programmierung ist eine teure katastrofe, die aufhören muss“ ins deutsche übertragen.

Anmerkung: das eingangs zitierte kwelltext-schnippselchen habe ich vor ein paar jahren mal wirklich in einer sehr ähnlichen form (in einem namespace und mit etwas mehr kram) gesehen. Seitdem bin ich der meinung, dass „multithreading“ eine erfindung des teufels sein muss. Die durch das handliche postinkrement so harmlos aussehende anweisung nextId++ ist nicht zwangsläufig eine einzelne CPU-instrukzjon, sondern kann (und in diesem kontext: wird fast überall) bedeuten, dass für den folgenden schreibvorgang ein wert aus dem RAM in ein register eingelesen, dann im register inkrementiert, dann als instanzvariable ins RAM geschrieben und wieder ins RAM zurückgeschrieben wird, und zwischendurch kann ein anderer thread seine scheibe rechenzeit bekommen und nochmal die gleiche ID generieren. Und wenn man dann ahnand dieser IDs objekte identifiziert: viel spaß mit dem debugger, um so einen (natürlich sehr tief im ganzen kram versteckten) scheißfehler zu finden! Und ja, ich weiß natürlich, dass mitmensch java-progger da einfach synchronized in seine funkzjonsdefinizjon reinschreibt und sich dabei für so überlegen hält, dass er gar nicht mehr merkt, wie sein gehäcksel schleicht und resorßen wegmümmelt, aber das war halt postinkrementiertes C…

pydoc re

Das hier nennt man dann wohl kwalitätssicherung:

Screenshot pydoc re in einem Terminal -- The following documentation is automatically generated from the Python source files.  It may be incomplete, incorrect or include features that are considered implementation detail and may vary between Python implementations.  When in doubt, consult the module reference at the location listed above.

Kurze übersetzung für nicht-techniker: Wir dokumentieren unsere standardbibliotek zwar im kwelltext und wir haben ein tolles progrämmchen, mit dem man sich diese dokumentazjon bekwem anschauen kann, aber was da drinsteht, kann völlig falsch, irreführend, veraltet und unbrauchbar sein. Großes kino!

Übrigens: wenn ihr jemals ein CGI-skript in python schreibt und euch wundert, dass das bei der ausgabe mit print() komische exceptions für unicode-fehler schmeißt — das liegt daran, dass dieses depperte python in den umgebungsvariablen für die eingestellte locale nachschaut, was für ein zeichensatz verwendet wird und seine interne unicode-darstellung dorthin konvertiert. In einer shell funkzjoniert das wunderbar. In der umgebung, die so ein websörver beim aufruf eines CGI-programmes setzt, steht so etwas aber natürlich nicht drin. Und wenn man dann nur ein einziges zeichen ausgibt, das über 7-bit-ASCII hinausgeht, weiß dieses python nicht, was es damit machen soll und behandelt das als fehler.

Es gibt mehrere möglichkeiten, diesen fehler zu umschiffen. Die möglichkeit, die ich empfehle, ist, einfach bytestrings auszugeben und nicht-ascii-zeichen in XML-entitäten umzuwandeln. Wenn in der variablen output drinsteht, was ausgegeben werden soll, könnte das ungefähr so aussehen:

sys.stdout.flush()
sys.stdout.buffer.write(output.encode('ascii', 'xmlcharrefreplace'))

Ja, das ist ein bisschen stupide. Der flush() steht da übrigens für den fall, dass schon etwas „ganz normal“ mit print() ausgegeben wurde, etwa ein paar HTTP-kopfzeilen — print() ist aus den üblichen fiesheitsgründen gebuffert. Die meldung „malformed headers“ im log des websörvers ist dann auch nicht wirklich hilfreich.

Wie wir früher mit arrays proggten…

Früher, als man seine softwäjhr noch so geschrieben hat, dass sie den rechner nicht in die knie zwang und so, dass sie nach möglichkeit einfach gut lief, hatten wir natürlich auch schon das problem, dass die größe eines array manchmal erst zur laufzeit feststand. Tja, dann haben wir halt mit malloc() eine ausreichend erscheinende menge speicher alloziert, uns gemerkt, wieviel das ist und wenn wir beim einfügen an die grenzen kamen, haben wir den speicherbereich mit realloc() vergrößert — in der regel durch schlichte verdopplung¹ der allokazjon. Und wenn man sortiert einfügen wollte, einfach mit binärer suche den ort zum einfügen des neuen elementes gefunden und mit memmove() platz für das neue element geschaffen, dass wir dann an die richtige stelle brachten. Nicht, dass so etwas spaß macht, aber es funkzjoniert und ist halbwegs performant.

Tja, das waren die alten zeiten, in denen man altmodische sprachen wie C verwendet hat, die einem nicht alles am kompjuter abstrahierten. Heute hingegen, heute ist alles viel viel besser. Heute fordert man einfach für jedes eingefügte element einen neuen speicherbereich an, der für ein element mehr platz bietet, kopiert den alten speicherbereich in den neuen und fügt das neue element an, um zum einfügen an die richtige stelle eine allgemeine biblioteks-sortierfunktion (meistens als mergesort oder quicksort implementiert) aufzurufen. Am besten in einer inneren for(;;)-schleife, also für eine vorab bekannte anzahl von fällen nacheinander. Und damit man nicht sofort merkt, wie hirnrissig das ist, notiert sich das ganze in schönster C++-syntax. So wird aus ganz gewöhnlicher datenverarbeitung eine implementazjon, die auch auf zeitgemäßer hardwäjhr ganz schön lahmarschig läuft. Und es gibt keine elektronen im kompjuter, die sich dabei langweilen.

Ich führe ja einen großteil der unbefriedigenden performanz „moderner“ anwendungen auf solche idjotie zurück…

¹Technischer hinweis: ursprüngliche allokazjon ist gern eine zweierpotenz minus eins, und zum vergrößern wird dieser wert verdoppelt und eins addiert, so dass wieder eine zweierpotenz minus eins rauskommt, also 255, 511, 1023, 2047, etc. Natürlich kann man das oft — je nach konkreter anwendung — ein bisschen intelligenter machen, aber frühzeitige optimierung ist eine der häufigsten und garstigsten fehlerkwellen, und diese schlichte implementazjon ist ganz brauchbar. Ziel des vorgehens ist es, die relativ aufwändige und langsame anforderung von speicher selten zu machen.

Ubuntus gwibber ist schrott!

Ich benutze häufig (vor allem, wenn ich an ubuntu-kisten sitze) gwibber, um meist eher bedeutungslose textstummel auf twitter und identi.ca zu posten. Wenn man einmal davon absieht, dass dieses programm für einen recht kleinen anwendungsfall einen recht großen speicherabdruck hinterlässt, ist es an sich ganz brauchbar.

Es ist vermutlich leicht vorstellbar, dass ich dieses stück softwäjhr oft sehr lange im hintergrund mitlaufen lasse. Genau dafür scheint mir so eine softwäjhr auch gemacht zu sein, dass sie ähnlich wie ein IRC-klient oder ein IM-programm gut im hintergrund laufen kann. Wenn mein nick erwähnt wird, bekomme ich eine kleine einblendung auf dem desktop, die mich zur aufmerksamkeit ruft.

So weit das gute.

Und nun das miese, also die art, wie dieses gwibber seine daten speichert. Und natürlich, wie dieses gwibber programmiert wurde.

Fange ich mal damit an, wie ich das problem entdeckt habe. Mir ist aufgefallen, dass einer der rechner, an denen ich häufig sitze, in regelmäßigen abständen in die knie ging. Es ist ein für meine verhältnisse ausgesprochen gut ausgestatteter rechner mit einer zweikern-CPU und 4 GiB RAM. Dennoch stand dieser rechner regelmäßig so sehr unter last, dass ich bei der audiobearbeitung probleme mit aussetzern bekam und es sogar zu rucklern beim abspielen von 720p-videos gekommen ist.

Natürlich empfand ich das als untragbar. Die video-probleme sind für mich eher untergeordnet, aber wenn ich keine klänge bearbeiten kann, ist ein kompjuter für mich kastriert. Ich benutze kompjuter verdammt noch mal zum musikmachen, und ich konnte das schon in den neunziger jahren mit meinem amiga machen, ohne mich mit derartigen aussetzern herumschlagen zu müssen.

Und deshalb habe ich mir dieses problem näher angeschaut.

Dabei ist mir schnell aufgefallen, dass der rechner in regelmäßigen abständen für mehrere sekunden eine CPU-auslastung von 100 prozent hat. Genauer gesagt: er wurde alle fünf minuten für ungefähr fünfzehn bis zwanzig sekunden unter so große last gesetzt, dass andere prozesse nicht mehr rund liefen.

Alle fünf minuten… schnell fiel mir ein, dass das genau die frekwenz war, mit der sich gwibber automatisch aktualisiert. Genauer gesagt: mit der sich ein hintergrund-prozess, der übrigens auch noch läuft, wenn man gwibber beendet hat, die fiepser und die statusmeldungen von identi.ca abholt, um sie lokal zu speichern. Ein aufruf von top an der kommandozeile brachte gewissheit, es war der prozess gwibber-service, der mir hier den spaß am rechner verdarb.

„Aber das ging doch früher“, habe ich mir gedacht.

Und dann bekam ich einen verdacht, dass gwibber wohl unmengen alter daten irgendwo gespeichert hatte und inzwischen probleme haben könnte, neue daten einzufügen.

Zeit, sich das einmal anzuschauen.

Zum glück speichert gwibber seine daten wie erwartet unter ~/.config/gwibber. In diesem verzeichnis liegt eine sqlite3-datenbank mit dem namen gwibber.sqlite herum. Ein schnelles ls -l führte dann dazu, dass mir einen kurzen moment lang die gesichtszüge entgleisten:

$ ls -l
-rw-r--r-- 1 elias elias 684362752 Apr 26 15:39 gwibber.sqlite
$ _

Dieses… ähm… tolle programm hat also im laufe der zeit 652,7 MiB an daten gespeichert. Kein wunder, dass das einfügen von daten in diese übergroße datei, das aktualisieren der indizes, und das anschließende benachrichtigen des GUI-prozesses gwibber, der aus dieser datei dann die neue ansicht ermittelt, ein bisschen last verursacht.

Ich dachte einen moment lang, dass gwibber niemals alte nachrichten löschen würde und dass die programmierer hirnlose vollpfosten seien, die ihre eigene softwäjhr gar nicht nutzen. Dieser gedanke erwies sich allerdings als halber trugschluss, denn in gwibber befindet sich sehr wohl etwas kohd, der die datenbank von alten einträgen befreit. In einer etwas obskuren SQL-anweisung mit zwei unter-SELECTs werden für jeden dienst alle nachrichten bis auf die letzten 2000 entfernt. Dies gilt allerdings nur für normale nachrichten. Gwibber hält… moment…

*tipptipptipp* SELECT stream, count(*) from messages group by 1;

…aber neben den „messages“ auch noch „images“, „links“, „lists“, „private“, „profile“, „replies“, „search“, „send_private“, „send_thread“, „user“ und „videos“ — und moment…

*tipptipptipp* SELECT operation, count(*) from messages group by 1;

…neben den empfangenen nachrichten („receive“) auch ein paar weitere nachrichtentypen, die als „lists“, „private“, „receive“, „responses“, „search“, „send“, „send_private“, „send_thread“ und „user_messages“ ausgewiesen sind. Was das alles bedeutet? Ich kann es in einigen fällen sicher erraten, und in anderen ist es mir eigentlich egal.

Entscheidend ist nur eines: alles, was gwibber davon selbst löscht, sind einträge, bei denen operation = "receive" und stream = "message" ist.

Und alles andere sammelt sich im laufe der zeit in einer datenbank an, über deren relationales desein ich an dieser stelle lieber den gnädigen schleier des schweigens senke, um nicht an einem akuten mangel unflätiger wörter zu verenden. Nur so viel sei hierzu noch angemerkt: Damit die datenbank auch schön moppelig werde und nicht gar zu viel platz auf der festplatte unbelegt bleibe, steht zu jedem eintrag einer nachricht auch noch das vollständige json-format, in dem er vom gwibber-service empfangen wurde, in der datenbank. Was die programmierer sich dabei gedacht haben? Ich weiß es nicht. Vielleicht wollten sie einfach eine möglichkeit der wiederherstellung haben, wenn einmal eine spalte mit daten verdampft. :mrgreen:

Nun gut, wenn gwibber das selbst nicht besser kann, muss ich das problem halt ein bisschen „spanabhebend“ lösen. Zum Beispiel, indem ich mit einer SQL-anweisung alle einträge lösche, die älter als dreißig tage sind. Und weil zu erwarten ist, dass ich diese wartungsaufgabe noch etwas häufiger vor mir haben werde, und weil andererseits nicht zu erwarten ist, dass die im auftrage canonicals meinen kompjuter unbrauchbar machenden gwibber-programmierer in der kommenden monaten etwas gegen dieses problem tun werden, ist es wohl am günstigsten, hierfür ein ganz schnelles skript zu hacken, dass man bei bedarf ausführt. Zum beispiel dieses hier:

#!/bin/bash

db_dir=~/.config/gwibber
db_name=gwibber.sqlite
db_keepdays=30

db_path=$db_dir/$db_name
db_keepsecs=$(($db_keepdays * 24 * 60 * 60))
db_deletebefore=$((`date +%s` - $db_keepsecs))

sqlite3 "$db_path" <<EOF
delete from messages where time < $db_deletebefore;
vacuum;
EOF

Eine kommentierte versjon des skriptes kann bei pastebin angeschaut und heruntergeladen werden — es ist also nicht nötig, hier bei bedarf die zwischenablage zu bemühen…

Nach aufruf dieses skriptes (und etlichen sekunden intensivens kratzens auf der festplatte) befanden sich bei mir nur noch die einträge der letzten dreißig tage in der datenbank. Wer ein anderes zeitfenster wünscht, kann für $db_keepdays einen anderen wert setzen.

Die größe der datenbank hat sich in meinem fall auf 83,5 MiB reduziert. Das ist zwar eine menge, aber es sind nur noch 13 prozent der ursprünglichen größe — die speicherverschwendung liegt an der stark redundanten datenhaltung. Und ich kann gwibber immer noch so benutzen, wie ich das will: ich kann meine timeline lesen und meine textstummel auf twitter und identi.ca veröffentlichen. Welchen vorzug der gespeicherte datenmüll aus anwendersicht haben sollte, ist mir auch nach kurzem lesen der gwibber-kwelltexte nicht aufgegangen.

Dabei fühlt sich gwibber jetzt deutlich schneller an. Und die systemlast bei aktualisieren ist so sehr reduziert, dass es nicht mehr zu derart üblen aussetzern kommt, wie ich sie weiter oben beschrieben habe.

Warum canonical seinem ubuntu eine derartige müllanwendung ausliefert, die den rechner in einer weise unbrauchbar macht, die „normale anwender“ einfach nur vor rätsel stellt (das ging doch vor ein paar wochen auch noch), gehört zu den fragen, mit denen man sich bitte an canonical wendet. Mein verdacht ist ja, dass die „zielgruppe“ canonicals leute sind, die niemals mit ihrem kompjuter arbeiten.

PHP-entwickler!

PHP-entwickler! Was zum hackenden Henker habt ihr euch dabei gedacht?! Ein…

$a = NULL;
--$a;

…führt dazu, dass $a den Wert NULL behält und nicht etwa zu 0 oder -1 wird; aber ein sehr ähnliches…

$a = NULL;
++$a;

…führt dazu, dass $a den Wert 1 bekommt.

Wer hat euch ins gehirn geschissen, damit ihr auf solche ideen kommt?!

Ungefähr dreiundsiebzig

Ich dachte ja immer, dass so ein kompjuter doch wenigstens zählen könnte, aber wenn man für ein projekt in seiner MySQL-datenbank eine ansicht benutzen will und flugs sein CREATE VIEW tippt…

Schätze mal schön...

…dann kann nur noch geschätzt werden, dass sich insgesamt ungefähr 73 zeilen in der datenbank befinden.

Übrigens: die ungefähr null einträge in der ansicht active_user sind in wirklichkeit genau zwei. Das kommt der wahrheit also schon recht nahe, wenn man nur genug abstand nimmt. 😉

Alles muss man selbst machen. Sogar zählen.