---
title: "Le bogue que je voulais corriger était déjà réparé — et sa réparation était fausse"
subtitle: "Anatomie d'une première contribution : trois découvertes que je ne cherchais pas, une prédiction ratée, et ce que le matériel dit quand le code se tait"
description: "Comment choisir sur quoi contribuer quand on débute ? J'ai suivi le conseil habituel — trouver une tâche bien délimitée dans un projet actif — et je suis tombé sur trois choses que personne n'avait signalées : une entrée de table fausse dans du code fusionné depuis deux mois et relu par quatre personnes, un capteur absent, et un défaut de performance que le mainteneur du paquet avait lui-même instrumenté sans que personne ne le remonte. Récit d'une soirée sur un Pixel 3a, avec les impasses, la prédiction que j'ai ratée, et ce qu'il faut écrire quand on n'a pas réussi à prouver."
date: 2026-09-08
image: images/pixel3a/og-pixel3a.jpg
tags: [contribution, logiciel libre, libcamera, postmarketos, pixel 3a, méthode, revue de code, débogage, premier patch, upstream]
slug: premiere-contribution-libcamera
lang: fr
---

# Le bogue que je voulais corriger était déjà réparé — et sa réparation était fausse

## Anatomie d'une première contribution : trois découvertes que je ne cherchais pas, une prédiction ratée, et ce que le matériel dit quand le code se tait

<div class="tldr" markdown="1">
**En deux minutes**

- **Le point de départ** : je voulais contribuer et je ne savais pas par où. J'ai pris le conseil standard — une tâche bien délimitée, dans un projet actif, sur du matériel que je possède.
- **Première surprise** : la tâche que je visais avait été faite six semaines plus tôt. L'avertissement que je voyais sur mon téléphone n'était pas un manque, c'était un décalage de version.
- **Deuxième surprise** : la correction récente contenait une erreur. Deux valeurs interverties, dans du code relu par quatre personnes dont le mainteneur, et marqué « testé ».
- **Le vrai travail** : un capteur absent des tables. Données relevées sur l'appareil, sauf une que je n'ai pas pu prouver — et que j'ai signalée comme telle dans le message de commit.
- **La prédiction ratée** : j'annonçais des photos plus claires après correction. Mesuré : 39,2 % avant, 38,1 % après. Rien. L'explication tient à l'arithmétique, et elle est instructive.
- **La vraie trouvaille** : en cherchant pourquoi l'application gelait, un défaut de performance que le mainteneur du paquet avait lui-même instrumenté en écrivant qu'il « signale un problème actionnable côté pilote ». Personne ne l'avait remonté.

*Ce billet parle de méthode plutôt que de caméras. Il devrait se lire même si `libcamera` ne vous dit rien. La partie technique est dans [le billet précédent](pixel-3a-postmarketos-linux-mainline.html).*
</div>

## Le problème de la première contribution

Tout le monde donne le même conseil : « trouve une issue étiquetée *good first issue* ». C'est un bon conseil et il ne marche pas très bien, pour une raison simple — ces tâches sont soit triviales au point de n'apprendre rien, soit déjà prises par quelqu'un de plus rapide.

Ce qui marche mieux, je crois, c'est de partir de ce qu'on a **que les autres n'ont pas**. Dans mon cas : un vieux téléphone que j'avais décidé de faire tourner sous Linux, et donc du matériel physique sur lequel exécuter le code que d'autres écrivent à l'aveugle.

C'est un avantage plus rare qu'il n'y paraît. Beaucoup de développeurs travaillent sur des pilotes de matériel qu'ils ne possèdent pas, en se fiant à des fiches techniques et aux rapports des utilisateurs. Celui qui a l'appareil branché sur son bureau peut répondre à des questions que personne d'autre ne peut trancher.

## Le fil : une tâche bien délimitée

Le projet postmarketOS maintient une liste de tâches par appareil. Pour le mien, une issue parapluie intitulée « Camera TODOs » listait une dizaine d'items, tous cochés sauf un :

> imx355 driver (front camera) is missing features for libcamera, makes the later complain (e.g. when running `cam -l`)

Périmètre net, symptôme reproductible, une commande pour le constater. Exactement ce qu'on cherche.

J'ai lancé la commande sur le téléphone. Elle s'est plainte, comme annoncé. J'ai ouvert le dépôt de `libcamera` pour écrire le correctif.

Et l'entrée était déjà là.

## Première leçon : une issue ouverte n'est pas forcément ouverte

Le support du capteur avait été ajouté le 17 juillet 2026, par un ingénieur de Raspberry Pi. La version installée sur mon téléphone datait du 10 juillet.

**Sept jours d'écart.** L'avertissement que je voyais n'était pas un manque dans le projet, c'était un décalage entre la version publiée et le dépôt.

C'est une confusion facile à faire, et elle mérite un réflexe : avant de coder quoi que ce soit, vérifier que le problème existe encore **en amont**, pas seulement sur sa propre machine. Ça se fait en deux requêtes.

<details markdown="1">
<summary>Pour aller au fond : comparer une version publiée au dépôt</summary>

La plupart des forges permettent de récupérer un fichier à une référence donnée. Il suffit de comparer la version qu'on exécute et la branche principale :

```bash
F="src/libcamera/sensor/camera_sensor_properties.cpp"
for ref in v0.7.2 master; do
  echo -n "$ref : "
  curl -s ".../repository/files/$(urlencode $F)/raw?ref=$ref" | grep -c '"imx355"'
done
# v0.7.2 : 0
# master : 1
```

Zéro occurrence dans la version publiée, une dans le dépôt : le travail est fait mais pas encore sorti. Il n'y a rien à écrire.

Le même réflexe vaut pour le noyau. Le second item de la liste concernait un pilote qui ne répondait pas à une requête ; là aussi, la correction existait déjà en amont — absente des versions 6.18, 7.0 et 7.1, présente dans la branche principale. Elle n'avait simplement pas encore été publiée.
</details>

Deux tâches sur trois s'étaient donc évaporées. J'aurais pu m'arrêter là avec le sentiment d'avoir perdu ma soirée. Sauf qu'en lisant l'entrée fraîchement ajoutée, quelque chose n'allait pas.

## Deuxième leçon : la relecture ne voit pas les tables

L'entrée associait deux motifs de test à des valeurs numériques. Or le pilote du capteur, dans le noyau, définit ces valeurs dans l'ordre inverse. « Couleur unie » et « barres de couleur » étaient **interverties**.

J'ai vérifié trois fois, par des chemins indépendants :

| Source | Ce qu'elle dit |
|---|---|
| Le pilote sur mon téléphone, interrogé directement | `1 = couleur unie`, `2 = barres de couleur` |
| Le code source du pilote dans le noyau officiel | même ordre |
| Deux autres capteurs au menu identique, décrits juste à côté | correspondance correcte |

Et j'ai cherché activement le contexte où l'auteur aurait eu raison : son entreprise maintient son propre noyau, avec parfois des pilotes différents. J'ai vérifié — même ordre dans les deux arbres. L'erreur était réelle, y compris sur son propre matériel.

Ce qui rend cette histoire intéressante, ce n'est pas l'erreur. C'est que le commit avait été **relu par quatre personnes**, dont le mainteneur du projet, et portait la mention « testé » par une cinquième.

Comment cinq personnes compétentes laissent-elles passer deux valeurs interverties ?

Parce qu'il n'y a **aucune logique à relire**. C'est une correspondance entre deux documents qui ne vivent pas dans le même dépôt : une table d'un côté, un tableau de chaînes de caractères dans le noyau de l'autre. Pour la vérifier, il faut ouvrir les deux fichiers côte à côte, ou avoir l'appareil sous la main. Et « testé » signifiait ici *la caméra fonctionne, je pointe l'objectif et j'obtiens une image* — ce qui est vrai, et n'exerce jamais les motifs de test, qui sont un outil de diagnostic.

Le reste de l'entrée était juste. La taille du photosite, les délais du capteur : tout ce qui est exercé au quotidien était correct. Seule la partie que personne ne fait tourner était fausse.

**C'est là que possède l'appareil devient un avantage décisif.** Pas parce que je suis meilleur, mais parce que j'étais le seul à pouvoir poser la question au matériel.

## Ce qu'un correctif de deux lignes doit contenir

Le correctif fait deux lignes. Son message de commit en fait trente, et c'est délibéré.

Un relecteur ne doit rien avoir à aller chercher. J'y ai donc mis le tableau du pilote noyau cité tel quel, la sortie de la commande qui interroge mon téléphone, la conséquence concrète en une phrase — *demander une couleur unie produit des barres, et réciproquement* — et l'argument de cohérence avec les deux capteurs correctement décrits.

Et j'ai devancé la question qui allait venir : « et les autres entrées, elles sont fausses aussi ? » Non, et je l'ai vérifié pilote par pilote : deux autres capteurs utilisent bien l'ordre inverse, parce que **leurs** pilotes le définissent ainsi. Une phrase dans le message évite un aller-retour de trois jours.

<details markdown="1">
<summary>Pour aller au fond : le piège du numéro de commit</summary>

Ces projets utilisent une convention pour désigner le commit qu'on corrige :

```
Fixes: a1db25dabaee ("libcamera: camera_sensor: Add Sony IMX355 sensor properties")
```

Le numéro fait douze caractères. J'en avais sept sous les yeux et j'ai **complété les cinq manquants de tête**. Ils étaient faux. Un identifiant inventé qui ressemble à un vrai est pire qu'un identifiant absent : personne ne le vérifie, et il pointe vers rien.

La forme correcte demande à l'outil plutôt qu'à la mémoire :

```bash
git rev-parse --short=12 <référence>
```

C'est une petite chose. C'est aussi exactement le genre de détail sur lequel un premier patch se fait renvoyer.
</details>

## Le vrai travail, et la valeur qu'on ne peut pas prouver

Restait quelque chose de réellement absent : le second capteur du téléphone ne figurait nulle part. Ni dans la bibliothèque, ni dans le noyau officiel — son pilote a été écrit chez un fondeur il y a huit ans et n'a jamais été remonté en amont.

J'ai relevé les valeurs sur l'appareil et dans le code du pilote. Trois se déduisent rigoureusement. La quatrième, non : c'est un paramètre qui se lit dans une fiche technique, et les fiches techniques de ces capteurs ne sont pas publiques.

J'ai cherché à l'étayer. J'ai trouvé un fichier de configuration, sur mon propre téléphone, qui déclarait exactement la valeur que j'avais supposée. Excellente nouvelle pendant environ trois minutes — jusqu'à ce que je vérifie s'il était **indépendant**. Il ne l'était pas : les dix fichiers équivalents du projet déclarent tous la même valeur, y compris pour des capteurs de trois fabricants différents. Ce n'était pas dix mesures concordantes, c'était un défaut recopié dix fois.

**Alors je l'ai écrit tel quel dans le message de commit** : cette valeur suit ce qui est documenté pour les capteurs de la même famille. Pas « d'après la fiche technique », pas de formulation qui laisserait croire à une mesure. Un relecteur qui a la documentation la corrigera en un message, et c'est très bien.

Habiller une incertitude est la façon la plus efficace de perdre la confiance d'un projet dès son premier patch.

## La prédiction que j'ai ratée

En lisant le code, j'avais compris ce que l'absence de ce capteur provoquait : sans lui, la bibliothèque ne convertit plus le gain de la caméra. Elle écrit un numéro de réglage là où elle devrait écrire un facteur d'amplification, et relit le numéro comme s'il était le facteur. La boucle d'exposition automatique est faussée dans les deux sens.

J'en ai déduit une prédiction : après correction, les photos en basse lumière devraient être meilleures. J'ai monté un protocole propre — téléphone calé et jamais déplacé, mesures objectives de luminance et de bruit, série avant et série après.

Résultat : **39,2 % de luminance avant, 38,1 % après.** Rien.

L'explication tient à l'arithmétique, et je l'avais signalée comme un risque avant de lancer le test — sans en tirer les conséquences, ce qui était l'erreur.

| Gain demandé | Formule correcte | Formule fausse |
|---|---|---|
| 0 | 1,0× | ≈ 1,0× |
| 50 | 1,1× | 50× |
| 300 | 2,4× | 300× |

L'erreur n'est énorme qu'à fort gain. Ma scène de test avait une zone blanche saturée : l'exposition automatique avait de la lumière à revendre, restait près de zéro de gain, et à cet endroit-là **les deux formules donnent le même résultat**.

Le patch reste correct, et il fonctionne — je l'ai vérifié autrement. Après installation, la bibliothèque n'émet plus aucun avertissement pour ce capteur, tandis que l'autre capteur du téléphone, non corrigé, les émet toujours tous. Un témoin négatif propre.

**Mais je n'ai pas démontré d'amélioration visible, et je ne l'écrirai donc pas.** La mention « testé » que je joindrai dira que le capteur est désormais reconnu. Pas que les images sont meilleures.

C'est une distinction qui paraît tatillonne et qui ne l'est pas. Un raisonnement correct sur un mécanisme ne dit rien de son amplitude dans les conditions du test. J'avais le mécanisme ; je n'avais pas l'amplitude.

## La trouvaille qui ne figurait dans aucune liste

Pendant tout ce travail, l'application photo gelait régulièrement. Une nuisance, que j'ai d'abord contournée en relançant.

Puis j'ai instrumenté, faute de mieux. Et il s'est trouvé qu'une **seule ligne**, émise une fois à l'initialisation, expliquait tout :

```
Importing input DMABuf failed, falling back to upload
```

Le traitement d'image se fait sur le processeur graphique. Normalement, l'image brute lui est transmise sans copie, par partage de mémoire. Ici l'import échoue — et la bibliothèque bascule en mode recopie : **douze mégapixels transférés vers le GPU à chaque image**. Le bus mémoire sature, l'affichage n'obtient plus la bande passante pour ses propres opérations, et le pipeline finit étranglé.

Ce n'est pas un plantage. C'est un étouffement, ce qui explique pourquoi il ne laissait aucune trace exploitable.

Et voici ce qui donne à cette ligne toute sa valeur. Elle vient d'un correctif ajouté par le mainteneur du paquet, qui l'avait **délibérément remise** après que le projet amont l'eut retirée. Son argumentaire :

> Les imports en échec dégradent massivement les performances et signalent des problèmes actionnables dans les pilotes V4L2 ou GPU.

Le mainteneur avait donc posé le détecteur, en écrivant noir sur blanc que son déclenchement mérite une investigation. Il s'est déclenché sur mon téléphone. Personne ne l'avait remonté.

Je n'ai pas encore prouvé la causalité — la corrélation est nette, le mécanisme cohérent, et un test simple trancherait. Mais c'est de loin la chose la plus utile que j'aie trouvée ce matin-là, et elle ne figurait dans aucune liste de tâches.

## Ce que je retiens, pour une prochaine fois

**Ce qu'on apporte, ce n'est pas du talent, c'est une position.** Je n'ai été meilleur que personne. J'avais l'appareil branché, et cinq relecteurs compétents ne l'avaient pas. C'est tout, et ça suffit.

**Vérifier que le problème existe encore avant d'écrire.** Deux tâches sur trois s'étaient évaporées, corrigées en amont mais pas encore publiées. Deux requêtes l'auraient dit d'emblée.

**Une source qui confirme n'est utile que si elle est indépendante.** Dix documents qui recopient le même défaut ne valent pas mieux qu'un seul.

**Séparer ce qui est démontré de ce qui est déduit** — dans un message de commit comme dans une conversation. J'avais un mécanisme juste et une prédiction fausse ; dire l'un sans l'autre aurait été un mensonge poli.

**Et instrumenter ce qui agace.** Le gel de l'application était une nuisance que je contournais depuis des heures. C'est en cessant de la contourner que j'ai trouvé la seule chose que personne d'autre ne cherchait.

C'est bien une pelote de ficelle : on tire sur un fil de deux lignes, et il vient trois choses qu'on n'avait pas demandées.
