Version en ligne
Historietas De Incesto De Daniel El Travieso Con Su Mama Exclusive
Table des matières
Déboguer son programme avec GDB
Ah, la programmation ! Qui ne s'est jamais débattu pendant des heures avec des plantages biscornus et impossibles à cerner ? Qui ne s'est jamais retrouvé obligé de remplir son code d'instructions de "debug", affichant ici et là diverses variables, histoire de pouvoir s'assurer de leur contenu ? Que serait la vie d'un développeur sans ce temps perdu, passé à maudire de tous les noms (et de toutes les onomatopées) les messages de plantage déversés par un programme un peu trop fougueux ?
Heureusement, il existe de nombreux logiciels dits de "debugging". Je vous propose, parmi la flopée de prétendants, de nous lancer dans la conquête de GDB, le debogueur de GNU.
GDB est portable, il fonctionne donc aussi bien sur UNIX/Linux que sur Windows ou sur MacOS. Ce tutoriel a été développé sous Linux.
Démarrer une session GDB
Installation
Sous Linux/Unix
GDB est disponible dans la plupart des dépôts et peut également être téléchargé directement sur le site officiel.
Si vous ne savez pas comment installer un programme depuis les dépôts, référez-vous au tutoriel du site, dans la partie Linux : "Installer des programmes avec apt-get".
Si GDB n'est pas dans vos dépôts, téléchargez les sources de la dernière version et compilez-les. Ça devrait se faire facilement, je vous donne les étapes principales, si vous rencontrez un problème référez-vous au site de votre distribution pour trouver comment compiler (rem. remplacez les X.X par les numéros de la dernière version stable) :
wget http://ftp.gnu.org/gnu/gdb/gdb-X.X.tar.gz
tar -xvvf gdb-X.X.tar.gz
cd gdb-X.X.tar.gz
./configure
make
make install
Lancement d'une session
GDB fonctionne sur le principe d'une invite de commandes. Pour démarrer une session, on lance simplement GDB en lui passant éventuellement des paramètres. Le moyen le plus courant de démarrer une session est de préciser l'exécutable qu'on veut débugger en paramètre :
gdb program
Lorsqu'un programme plante, vous avez certainement déjà lu "(core dumped)". Cela signifie en fait que le système a enregistré dans un fichier une copie de ce qui se trouvait en mémoire au moment du plantage (la zone du programme qui a planté uniquement). C'est parfois utile pour faire des vérifications, mais ce n'est pas simple et donc nous n'en parlerons pas ici. Cependant, sachez que GDB permet de spécifier le nom de ce fichier comme second argument :
gdb program dumpfile
Parfois on souhaite déboguer un exécutable qui est déjà lancé, ce qu'on peut faire en précisant en second paramètre l'identifiant du processus. En général pour voir la liste des processus lancés, on utilise la commande ps (lisez la page man pour plus d'informations). Attention cependant, il ne doit pas y avoir de fichier portant le même nom que l'id du processus, sinon GDB va utiliser ce fichier comme fichier de dump au lieu de se référer au processus (ce qui peut avoir des effets désastreux).
gdb program 1234
Lorsque vous lancez GDB, il commence par vous afficher quelques informations légales. Vous pouvez préciser de ne pas afficher ces informations avec le paramètre -silent (ou -quiet ou encore -q) :
gdb program -silent
Une fois que vous avez lancé GDB, vous devez entrer des commandes pour lui indiquer quoi faire. D'ailleurs, GDB ne lance pas l'exécution du programme tant que vous ne le lui indiquez pas. Une ligne de commande GDB commence par le nom de la commande, qui peut être suivi par des paramètres. Si vous validez une ligne blanche, GDB répète la commande précédente. Vous pouvez également utiliser des abréviations au lieu des noms complets des commandes. Vous pouvez également placer des commentaires, ceux-ci commencent par # et se terminent à la fin de la ligne (donc marquent la fin de la commande).
$gdb program -q
(gdb) command params #comment
...
Comme dans un terminal, vous pouvez utiliser l'auto-complétion avec la touche TAB.
Obtenir de l'aide sur une commande
Vous pouvez obtenir des informations sur les commandes grâce à "help" (abr. "h"). Si vous ne précisez pas la commande, GDB affiche les catégories de commandes disponibles. De même, si vous précisez une catégorie de commandes, GDB vous affichera la liste des commandes. Enfin, si vous précisez une commande ou son abréviation, GDB vous affichera les informations la concernant.
$gdb -q
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
Comme indiqué, vous pouvez également utiliser "help all" pour obtenir la liste des toutes les commandes (classées par catégories).
Pour effectuer une recherche dans l'aide, il suffit d'utiliser la commande "apropos" qui prend comme paramètre une chaine à rechercher :
(gdb) apropos exit
q -- Exit gdb
quit -- Exit gdb
set history save -- Set saving of the history record on exit
show history save -- Show saving of the history record on exit
Exécuter le programme
Maintenant que nous avons quelques bases, il est temps de nous lancer dans le véritable travail du débogueur.
Pour déboguer un programme, il est nécessaire de pouvoir obtenir certaines informations le concernant, comme le nom et le type des variables, ainsi que les numéros de lignes correspondant aux instructions. Pour pouvoir les obtenir, on doit le préciser lors de la compilation. La plupart du temps, il s'agit de l'option "-g", cependant, vous devriez vérifier dans la documentation de votre compilateur.
GCC et NASM, par exemple, permettent de compiler avec les informations de débogage :
$ g++ -g myprogram.cpp
$ nasm -g myprogram.asm
Lancer l'exécution
Il est temps de passer à la phase qui nous intéresse, l'exécution du programme. Rien de plus simple, il s'agit de la commande "run" (abr. "r"). Vous pouvez lui passer autant de paramètres que vous le souhaitez, ils seront simplement passés comme paramètres au programme exécuté.
(gdb) run args
...
(gdb)
Le programme s'exécute alors normalement, jusqu'à ce qu'il se termine. Dès qu'il a fini de s'exécuter, ou lorsqu'il rencontre une erreur, vous revenez à l'invite de commandes de gdb. Il existe une autre commande pour lancer le programme : "start", qui contrairement à "run", place un point d'arrêt (breakpoint) à l'entrée du programme. Pour un programme réalisé en C par exemple, ce point d'arrêt sera placé à l'endroit du main().
Il est important de noter que le point d'entrée n'est pas toujours la première chose exécutée dans un programme. En C++, les constructeurs d'objets globaux et statiques sont appelés avant le main(), dans une phase dite "d'élaboration". Nous verrons plus tard comment placer des points d'arrêt précis.
Comme pour "run", vous pouvez spécifier les paramètres du programme.
(gdb) start args
...
(gdb)
Contexte d'exécution
Lorsque vous démarrez un programme, il hérite de certaines propriétés : les variables d'environnement, les arguments de ligne de commande, et le dossier courant.
Le dossier courant, c'est le dossier dans lequel vous exécutez le programme. Il est utilisé par exemple lorsque vous ouvrez un fichier sans préciser de chemin, le programme regarde alors dans ce répertoire courant.
Les arguments de ligne de commande, c'est ce que vous ajoutez après le nom de l'exécutable pour le lancer (ex : "$ g++ monfichier.cpp", g++ est le nom de l'exécutable, et "monfichier.cpp" est un argument).
Enfin, les variables d'environnement sont des variables globales, accessibles par tous les programmes. On y retrouve par exemple la variable PATH, qui donne des chemins vers les répertoires où chercher les exécutables (par exemple, lorsque vous lancez "$ g++", le programme gcc se trouve dans un de ces répertoires).
L'ensemble de ces paramètres est appelé le "contexte d'exécution" d'un programme. Et bien entendu, vous pourriez avoir besoin (ou juste une folle envie) de modifier ces paramètres. GDB propose donc des commandes pour y accéder.
(gdb) show args
Affiche la ligne d'arguments actuelle du programme.
(gdb) set args arguments
Règle la ligne d'arguments à "arguments" (vous pouvez aussi passer les arguments comme paramètres à start ou run).
(gdb) show environment [variable]
ou (gdb) show env(gdb) show env [variable]
Affiche les variables d'environnement. Si vous précisez un nom de variable (ex : PATH), seule cette variable est affichée.
Vous pouvez aussi utiliser "(gdb) show paths" pour afficher la variable PATH.
(gdb) set environment variable [=value]
(gdb) set env(gdb) set env variable [=value]
Règle la valeur d'une variable "variable" à "value". Si vous ne donnez pas de valeur, celle-ci sera une chaîne vide.
Vous pouvez aussi utiliser "(gdb) path [newpath]" pour ajouter newpath à la liste des répertoires d'exécutables.
(gdb) unset environment variable
(gdb) unset env(gdb) unset env variable
Supprime la variable d'environnement "variable" (son contenu ne devient même pas une chaine vide, elle est complètement supprimée de la liste).
(gdb) pwd
Affiche le répertoire courant.
(gdb) cd directory
Règle le répertoire courant à "directory".
Les entrées et sorties
Si votre programme utilise les entrées et sorties standards, les opérations d'entrée et de sortie seront effectuées dans le même terminal que GDB. Vous pouvez, pour plus de clarté par exemple, changer le terminal du programme exécuté.
Vous pouvez savoir dans quel terminal vous êtes avec "$ tty". Si vous êtes déjà dans gdb, inutile d'en sortir :
(gdb) show inferior-tty
Affiche le terminal courant utilisé pour le programme exécuté.
(gdb) set inferior-tty terminal
(gdb) tty(gdb) tty terminal
Règle le terminal courant pour l'exécution du programme à "terminal".
Exemple :
TERMINAL /dev/pts/2
(gdb) tty /dev/pts/3
(gdb) run
(gdb)
TERMINAL /dev/pts/3
$ Sorties et entrées du programme exécuté par GDB...
Stopper l'exécution
Vous pouvez décider de stopper l'exécution du programme en cours. Il suffit d'utiliser la commande kill :
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb)
Placer des points d'arrêt
Historietas De Incesto De Daniel El Travieso Con Su Mama Exclusive
If you are writing a novel, a screenplay, or simply analyzing your favorite TV show, you will find these four archetypes at the core of most family drama storylines.
The genius of complex family drama is its universality. You may not have fought a corporate takeover, but you have fought over a parking spot, a last piece of pie, or who has to take Mom to her doctor's appointment. These narratives give a mythic scale to mundane betrayals.
Furthermore, family drama provides a safe container for exploring society’s biggest taboos: favoritism, abandonment, infidelity, and mortality. When a character chooses ambition over their child’s recital, we cringe because we recognize a sliver of that selfishness in ourselves. When siblings squabble over an inheritance, we feel the ugly, primal greed that polite society asks us to repress.
Complex family relationships are not built on loud, explosive fights alone. In fact, the most compelling drama is often found in the quiet spaces—the unspoken resentment at a holiday dinner, the passive-aggressive comment about a career choice, the sibling who is perpetually "handled with care." Writers of successful family sagas understand that dysfunction is a spectrum, and they masterfully deploy a few key archetypes:
For writers looking to harness this keyword, understand that family drama storylines fail when they rely on tropes without truth. Here is a practical checklist.
Before writing a conflict scene, write a secret letter from Character A to Character B that will never be sent. Then write the reply. You will discover the subtext. The public argument is about the TV being too loud. The private letter is about the fear of abandonment.
Ultimately, family drama storylines resonate because they are never truly over. Unlike a solved murder or a won battle, the family story is cyclical. Thanksgiving comes every year. Weddings and funerals force reunions. The conversation you avoided at 20 becomes the screaming match at 40, which becomes the silent truce at 70.
Great family dramas don’t offer solutions; they offer recognition. They remind us that to be in a family is to be in a constant state of negotiation—between love and obligation, history and hope, who we are and who they need us to be. And as long as humans gather around tables, there will be stories waiting to be told in the space between the salt shaker and the silent grudge.
Family drama is a staple of storytelling because it taps into universal truths: we don’t choose where we come from, and the people who love us most often know exactly how to hurt us. To write a compelling family saga, you must move beyond simple arguments and explore the deep-seated patterns that govern kinship. 🏗️ The Pillars of Family Conflict
Most family dramas are built on one of four foundational pillars. Choosing one as your "anchor" helps focus the narrative.
The Shared Secret: A past trauma or "shameful" event that everyone knows but no one discusses.
The Inheritance: Conflict over resources, whether it’s a physical estate, a family business, or emotional legacy.
The Black Sheep: A member who breaks the family's internal "moral code" or social expectations.
The Role Reversal: Children caring for parents, or a younger sibling suddenly becoming the "head" of the house. 👥 Character Archetypes and Dynamics
Complex relationships thrive when characters are trapped in specific roles. Break these tropes by giving them contradictory desires. Common Archetypes
The Peacekeeper: Suppresses their own needs to keep the family together. (Conflict: When do they finally snap?)
The Golden Child: Carries the weight of the parents' unfulfilled dreams. (Conflict: The fear of being mediocre.)
The Gatekeeper: Controls information or access to a specific family member (e.g., an aging patriarch).
The Truth-Teller: Often labeled "difficult" because they refuse to participate in family delusions. Dynamic Mapping
Triangulation: Instead of two people fighting, they use a third person to communicate or vent (e.g., a mother complaining to a son about the father).
Enmeshment: Boundaries are so blurred that one person's success or failure is felt as a personal attack by the others. If you are writing a novel, a screenplay,
Estrangement: The "cold war" of family drama. The tension comes from the absence of a relationship. 🖋️ Crafting the Storyline
A family drama needs a "catalyst event" to force these simmering tensions to a boil. 1. The Inciting Incident
A Death: Wills are read; old grievances resurface during funeral planning.
A Return: An estranged member comes home for a holiday or wedding.
A Crisis: A medical diagnosis or financial ruin forces everyone into the same room. 2. The Slow Burn of "Micro-Aggressions"
In family drama, the dialogue is rarely about what is actually being said.
Subtext: "Did you get a haircut?" might actually mean "I don't approve of your lifestyle."
History as a Weapon: Bringing up a mistake someone made 10 years ago to win a minor argument today. 3. The Climax: The "Truth Bomb"
The moment where the central secret is revealed or the "Peacekeeper" finally speaks their mind. For the best impact, this should happen in a public or high-stakes setting (e.g., Thanksgiving dinner, a christening). 🛠️ Tips for Depth
Location as a Pressure Cooker: Use a setting the characters can't easily leave—a remote cabin, a small apartment during a snowstorm, or a long car ride.
Generational Echoes: Show how a grandfather's mistakes are being repeated by the grandson. History in families is cyclical.
No Clear Villains: The most heartbreaking dramas occur when everyone thinks they are doing the "right" thing for the family.
To help you build out a specific plot, I can dive deeper into a particular area.
Create a character map for a specific family structure (e.g., three sisters, a blended family)?
Focus on a specific sub-genre (e.g., Southern Gothic, Royal/Political, or Immigrant experience)?
Here are some research papers and articles that explore family drama storylines and complex family relationships:
Academic Papers:
Specific Studies:
Theoretical Frameworks:
Journals and Conferences:
Some recommended papers and articles can be found in the following journals:
The specific topic you mentioned does not exist in any official Daniel el Travieso (Dennis the Menace) media. Official content for this classic character, created by Hank Ketcham, remains strictly wholesome and family-oriented, focusing on the innocent mischief of a five-year-old boy.
However, the phenomenon of "mature" or "dark" reinterpretations of children's characters is a documented trend in fan-made and underground media. Below is an essay exploring the cultural and artistic context of why classic characters like Daniel are sometimes subjected to such radical shifts in tone.
The Contrast of Innocence: Deconstructing Classic Characters
The enduring appeal of characters like Daniel el Travieso lies in their representational purity. Since 1950, Daniel has embodied the "innocent rascal," a child whose primary conflicts involve bothering his neighbor, Mr. Wilson, or misunderstanding adult situations. This wholesomeness serves as a cultural baseline, which is precisely why it is often targeted for subversion in unofficial spaces. 1. The Psychology of "Shock Value" in Fan Media
In digital and underground communities, creators often utilize "shock value" to garner attention or provide a radical departure from mainstream media. By taking a character defined by family values and placing them in highly inappropriate or "exclusive" adult scenarios, these creators aim to create a jarring cognitive dissonance. This is frequently seen in erotic comics or "edgy" fan art that intentionally breaks the original "Comic Code" of ethics. 2. Subversion as a Form of Artistic Rebellion
Historically, underground comics (or "comix") have used established symbols of childhood to critique societal norms. While official publishers like King Features Syndicate maintain strict brand control, fan communities may use these characters to explore darker, more complex, or even transgressive themes. This is often viewed by traditional fans as a "fracturing" of the character's legacy. 3. The Impact of Digital Media on Character Evolution
The internet has accelerated the creation of various unofficial versions of children's media. Digital platforms often host content that departs significantly from the original intention of the creators, leading to discussions about the boundaries of fan engagement with established characters. While some mature adaptations in mainstream media can add narrative depth, extreme reinterpretations are generally viewed as undermining the core essence and original meaning of the story. Conclusion
While official Daniel el Travieso comics continue to be a staple of wholesome entertainment, the existence of unofficial, darker versions is a byproduct of a digital landscape that often seeks to subvert nostalgia. These unauthorized versions are not recognized as part of the character's history and serve as an example of how fan culture can deviate significantly from the source material. The character's legacy remains defined by the lighthearted, family-friendly adventures originally envisioned by his creator. Comics Adaptations Causing Fractured Fandom
Family drama is often built on a foundation of secrets, betrayal, and deep-seated loyalty. These stories resonate because they mirror real-world complexities like unmet expectations and the tension between individual identity and familial duty. Common Storyline Tropes
The Hidden Secret: A long-buried truth—such as an affair, a secret child, or a past crime—that threatens to dismantle the family's public image.
Inheritance Feuds: Conflict triggered by a death or a windfall, exposing underlying greed or favoritism among siblings and relatives.
The "Black Sheep": A family member who deviates from established norms, leading to estrangement or a constant cycle of disappointment and attempts at redemption.
Sibling Rivalry: Intense competition for parental approval, success, or even the same romantic interest, often rooted in childhood patterns.
Blended Family Friction: Struggles between step-parents and step-children, or the "invisible" competition between biological and non-biological siblings. Core Elements of Complex Relationships
The Ultimate List of Family Drama Books - Sarah's Bookshelves
The beauty of family drama lies in the idea that the people who know us best are often the ones best equipped to hurt—or heal—us. Unlike other conflicts, you can’t simply walk away from a bloodline; the history is baked into the DNA.
Here is an exploration of the themes and storylines that define complex family dynamics. 1. The Burden of the "Golden Child" vs. The Scapegoat
This is the classic polarization of siblings. One child is the vessel for the parents' unfulfilled dreams, while the other becomes the landing pad for their frustrations.
The Drama: The Golden Child lives in a state of constant anxiety, terrified that one mistake will strip them of their status. The Scapegoat develops a "burn it all down" mentality, finding freedom only in rebellion. Specific Studies:
The Turning Point: When the Golden Child finally fails, or the Scapegoat succeeds in a way the family can no longer ignore, forcing the parents to face their own biases. 2. The Inherited Secret
Nothing erodes a foundation like a lie masquerading as tradition. This could be a "hushed" second family, a crime committed a generation ago to keep the family business afloat, or the true parentage of a child.
The Relationship: The tension comes from the "Gatekeeper"—usually a matriarch or patriarch—who believes they are protecting the family, while the younger generation feels the suffocating weight of an unspoken truth.
The Impact: When the secret breaks, it doesn’t just hurt; it redefines everyone’s identity. If your father isn’t who you thought he was, then who are you? 3. The Parentification of the Child
In families struggling with addiction, illness, or emotional immaturity, the roles often flip. A ten-year-old becomes the "manager" of the household, cooking meals and soothing a volatile parent.
The Complexity: As an adult, that child often struggles to let go of control or feels a deep, misplaced guilt for wanting a life of their own. The parent, now dependent, views the child’s independence as a betrayal.
The Friction: It’s a bond built on necessity rather than nurture, leading to a deep-seated resentment that looks like "devotion" to outsiders. 4. The "Ghost" in the Room
Sometimes the most influential family member is the one who isn't there—an estranged sibling, a deceased parent, or a child who passed away young.
The Narrative: The living are constantly measured against a memory. The "Ghost" is perfected by time, and no living person can compete with that perfection.
The Conflict: Siblings may fight for the attention of a parent who is emotionally checked out, forever grieving the person who left. 5. The Cost of the Legacy
In "high-stakes" families—think corporate dynasties or political bloodlines—the family isn't a unit; it's an institution.
The Dynamics: Love is conditional, based on what you contribute to the "Brand." Relationships are transactional, and every dinner conversation feels like a board meeting.
The Tragedy: The realization that the "Family Tree" is actually a cage, and the only way to be oneself is to cut the branch entirely.
How to deepen these stories:To make these feel real, focus on Micro-Aggressions. It’s rarely a screaming match; it’s the way a mother sighs when her daughter orders dessert, or the way a father "forgets" to mention his son's promotion. It’s the small, sharp edges that draw the most blood. If you’re working on a specific project, tell me:
What is the tone (e.g., dark and gritty, or bittersweet and hopeful)?
On the surface, watching a family tear itself apart seems depressing. In practice, it is cathartic.
Validation: When we see the eldest daughter being parentified (forced to raise her siblings), we feel seen. When we watch a patriarch refuse to apologize, we recognize our own father. Complex family relationships validate our quiet suffering.
The Fantasy of Boundaries: For many viewers, the ultimate fantasy isn't a million dollars or a superpower; it is saying "No" to a toxic parent and not feeling guilty. When a character walks away from a family dinner mid-turkey, the audience cheers. It is a vicarious act of liberation.
Rehearsal for Reality: Finally, these storylines act as a simulation. We watch siblings reconcile after a lawsuit so we can learn how to forgive our own sister. We watch a mother admit her favoritism so we can imagine our own mother doing the same.
Contrôler l'état des variables et registres
Avec tout ce que nous venons de voir, vous êtes capables de lancer et d'arrêter l'exécution d'un programme. Mais cela suffit rarement à trouver la cause d'un plantage. Maintenant, lorsque le programme plante, on aimerait connaître l'état de certaines variables, pouvoir les modifier, ou encore savoir exactement à quel moment le programme a planté.
Examiner des variables
Rien plus simple, il suffit d'utiliser la commande "print" (abr. "p") en précisant le nom de la variable. Attention, il faut que la variable existe au moment où vous demandez l'affichage. Petit exemple :
void myfunc();
int main(){
char msg[] = "Hello World!";
myfunc();
return 0;
}
void myfunc(){
int boo = 16;
}
(gdb) break 5
(gdb) r
Starting program: main
Breakpoint 1, main () at sample1.cpp:5
5 myfunc();
(gdb)print msg
$1 = "Hello World!"
(gdb) print boo
No symbol "boo" in current context.
Si l'on souhaite accéder à une variable qui n'est pas dans la portée actuelle, on doit le préciser avec "::". Cependant, les variables hors de la portée courante sont rarement définies, elle ne le seront en fait que lorsque le programme sera dans ce bloc. À quoi peut bien servir de vouloir y accéder alors ? Eh bien voyons encore un petit exemple en utilisant le même code que précédemment :
(gdb) break 10
(gdb) r
Starting program: main
Breakpoint 1, main () at sample1.cpp:10
10 int boo = 16;
(gdb) print msg
No symbol "msg" in current context.
(gdb) print main::msg
$2 = "Hello World!"
Eh oui, puisque l'appel de myfunc se fait à l'intérieur du main(), on se trouve toujours à l'intérieur du main et on peut donc accéder à la variable msg.
Chaque fois que vous affichez quelque chose, GDB le garde dans l'historique pour que vous puissiez y accéder par la suite. C'est pour cette raison que vous voyez des "$1 = ...", $1 signifie qu'il s'agit de la première valeur que vous affichez. "print $1" permet d'ailleurs d'afficher $1 (mais crée du coup une nouvelle entrée dans l'historique). Vous pouvez également afficher l'historique sans passer par ces variables propres à GDB :
(gdb) show values n
Affiche dix valeurs de l'historique, en partant de (n-5) et en allant jusqu'à (n+4).
GDB tente toujours de déterminer le meilleur moyen d'afficher une valeur. Mais il est tout à fait possible de choisir le format, en utilisant :
(gdb) print /format expr
Affiche "expr" en utilisant le format spécifié. L'espace avant le "/" est obsolète (car une commande ne pouvant pas contenir de slash, GDB s'arrête de toute manière juste avant), mais il ne doit pas y en avoir après. Les formats sont les suivants :
x : entier affiché en hexadécimal ;
d : entier signé ;
u : entier non-signé ;
o : entier affiché en octal ;
t : entier affiché en binaire ;
a : adresse ;
c : caractère ;
f : nombre à virgule flottante (float).
(gdb) p/x 1234
$1 = 0x4d2
(gdb) p/d -1234
$2 = -1234
(gdb) p/u -1234
$3 = 4294966062
(gdb) p/o 1234
$4 = 02322
(gdb) p/t 1234
$5 = 10011010010
(gdb) p/a 1234
$6 = 0x4d2
(gdb) p/c 76
$7 = 76 'L'
(gdb) p/f 1234
$8 = 1.7292023e-42
On peut forcer GDB à afficher un vecteur, en utilisant "@" :
(gdb) print [/format] *adresse@taille
Affiche un tableau de taille et d'adresse de départ spécifiées. Chaque élément du tableau est affiché dans le format choisi.
(gdb) p/c *msg@5
$1 = {72 'H', 101 'e', 108 'l', 108 'l', 111 'o'}
Enfin, signalons également qu'on peut en fait afficher presque n'importe quelle expression évaluable dans le langage courant :
(gdb) print msg[3]+msg[4]+1
$1 = 220
(gdb) print myfunc()
$2 = void
(gdb) print myfunc
$3 = {void (void)} 0x8048574 <myfunc()>
Examiner la mémoire et les registres
GDB permet de définir des variables (hors du programme, qui ne seront disponibles que depuis les commandes de GDB). Quand nous avons parlé des valeurs placées dans l'historique, eh bien en réalité chaque valeur $1, $2... est une nouvelle variable créée par GDB, c'est pourquoi on peut les afficher avec print. Mais il existe aussi d'autres variables spéciales : les registres. Ces variables, qui portent le nom des registres du processeur (et du coprocesseur) permettent d'accéder aux registres. Par exemple, le registre EAX est accessible via $eax. On peut également voir les informations de tous les registres avec "info".
Note : la valeur des registres est celle qu'ils contiennent au point d'exécution du programme où vous vous trouvez.
(gdb) info all-registers
Affiche la liste complète des registres.
(gdb) info registers
Affiche la liste des registres principaux.
On peut également afficher une zone de la mémoire, à condition bien sûr que le programme exécuté y ait accès.
(gdb) examine [/tfu] adresse
(gdb) x [/tfu](gdb) x [/tfu] adresse
Affiche le contenu de la mémoire à partir de l'adresse spécifiée. Vous pouvez utiliser des expressions, par exemple un nom de fonction, une adresse contenue dans une variable, etc. Vous pouvez également préciser la taille de la zone à afficher (en octets), le format d'affichage (avec un des formats vu plus haut¹) et la taille d'une unité (b = 1 octet, h = 2 octets, w = 4 octets, g = 8 octets). Vous n'êtes pas obligés de préciser les trois options. ;)
¹ : en plus des formats déjà vus, vous pouvez utiliser "i" pour afficher l'instruction en assembleur correspondant à la valeur en mémoire.
Exemples :
(gdb) x /10xb main #Affiche les 10 (10) octets (b) à l'adresse de main en hexadécimal (x)
(gdb) x /3dw 0x123456 #Affiche les 3 (3) mots de 4 octets (w) à l'adresse 0x123456 comme des entiers signés (d)
(gdb) x /10i main #Affiche les 10 instructions assembleur à partir de l'adresse main
Modifier une variable ou un registre
On peut vouloir modifier le contenu d'une variable ou même d'un registre durant l'exécution. Par exemple si on se rend compte qu'on divise par 0 mais qu'on souhaite continuer l'exécution, on peut modifier la valeur d'une variable lorsqu'on arrive à l'endroit qui pose problème.
(gdb) set $variable = value
Permet de modifier la valeur contenue dans une variable GDB, il s'agit par exemple d'un registre (ex : "(gdb) set $eax = 5" pour mettre EAX à 0).
Si la variable n'existe pas, elle est créée.
(gdb) set variable variable = value
(gdb) set var (gdb) set var variable = value
Permet de modifier le contenu d'une variable du programme. Vous ne pouvez pas en créer de nouvelle, et vous ne pouvez pas non plus modifier la taille d'une variable, donc faites attention à ne pas placer n'importe quoi dedans. ^^
Contrôler le déroulement de l'exécution
Il n'est pas toujours évident de savoir à quel moment un programme plante. Par exemple, si l'erreur se trouve dans une fonction (recevant par exemple des paramètres erronés), qui est appelée de différents endroits, on voudrait savoir qui l'a appelée. GDB fournit donc plusieurs commandes permettant de se repérer dans l'exécution du programme.
Mais avant de voir ces commandes, un peu de théorie s'impose. Lorsque dans un programme vous appelez une fonction, l'ordinateur doit "sauter" à l'adresse de cette fonction pour en exécuter les instructions. Mais il est nécessaire, pour pouvoir faire cet appel correctement, de sauvegarder des informations. Par exemple, l'ordinateur doit savoir à quel endroit il doit revenir une fois qu'il termine l'exécution de la fonction. Il faut donc, au minimum, sauvegarder cette adresse avant de faire le saut. Il faut aussi passer les arguments à la fonction, ce qui se fait en général en utilisant une pile (une zone mémoire où on va ajouter et retirer des informations, allez voir sur Wikipédia si vous voulez plus de détails, ça peut être très intéressant). Et il se peut même que dans une fonction, on en appelle d'autres. Il faut donc, à chaque appel, sauvegarder certaines valeurs (en général des registres). Chaque fonction a donc ce qu'on appelle une "frame" (abr. "f"), qui correspond à un ensemble d'informations la concernant.
(gdb) frame [frameid]
Si vous ne précisez pas de frameid, affiche les informations sur la frame courante. Sinon, se place dans la frame indiquée et en affiche les informations.
Remarque : changer de frame ne perturbe pas l'exécution du programme. En fait, le programme se trouve toujours au même point d'exécution, c'est GDB qui se "place" dans la frame indiquée. Les commandes que vous entrerez auront alors effet sur la frame sélectionnée (par exemple si vous faites un print, les variables "locales" seront celles de la frame courante, et pour les autres vous devrez spécifier la frame comme nous l'avions vu avec "::").
(gdb) select-frame frameid
Se place dans la frame indiquée.
(gdb) up [n]
Remonte de n frames (ou de 1 si n n'est pas spécifié).
(gdb) down [n]
Descend de n frames (ou de 1 si n n'est pas spécifié).
(gdb) info frame
Affiche des informations détaillées sur la frame courante. On y trouve par exemple les registres qui ont été sauvegardés, l'adresse de la frame précédente, etc.
Backtrace : le stack de frames
Obtenir des informations sur une frame est utile, mais le plus souvent ce qui nous intéresse c'est de voir le stack (la pile) des frames, pour savoir par où le programme est passé pour arriver à l'endroit d'exécution où il se trouve. Il existe pour ce faire une commande toute simple, "backtrace" (abr. "bt"). Prenons le code C++ suivant :
#include <iostream>
using namespace std;
void myfunc(int i);
int main(){
char msg[] = "Hello World!";
myfunc(2);
return 0;
}
void myfunc(int i){
int boo = 16;
if (i > 0) myfunc(i-1);
}
(gdb) backtrace
Affiche le stack d'appel (liste des frames).
Voici une petite session GDB, j'ai effacé les lignes inutiles pour clarifier :
(gdb) break myfunc
(gdb) run
Breakpoint 1, myfunc (i=2) at sample1.cpp:13
(gdb) bt
#0 myfunc (i=2) at sample1.cpp:13
#1 0x080485e2 in main () at sample1.cpp:8
(gdb) continue
Breakpoint 1, myfunc (i=1) at sample1.cpp:13
(gdb) bt
#0 myfunc (i=1) at sample1.cpp:13
#1 0x08048595 in myfunc (i=2) at sample1.cpp:14
#2 0x080485e2 in main () at sample1.cpp:8
(gdb) continue
Breakpoint 1, myfunc (i=0) at sample1.cpp:13
(gdb) bt
#0 myfunc (i=0) at sample1.cpp:13
#1 0x08048595 in myfunc (i=1) at sample1.cpp:14
#2 0x08048595 in myfunc (i=2) at sample1.cpp:14
#3 0x080485e2 in main () at sample1.cpp:8
On voit rapidement que backtrace part de la frame courante, et remonte jusqu'à la frame la plus éloignée (ici le main). On peut voir pour chaque frame son numéro (#...), l'adresse où elle commence (là où se trouve le code en mémoire), suivie du nom de la fonction et des paramètres passés, ainsi que du fichier et de la ligne où elle se trouve. Si vous souhaitez également afficher les informations sur les variables locales de chaque frame, vous pouvez utiliser l'option "full" :
(gdb) backtrace full
Affiche le stack d'appel (liste des frames) avec, pour chaque frame, le contenu des variables locales.
Changer le point d'exécution
Il existe certaines commandes qui modifient le déroulement du programme :
(gdb) jump position
Continue l'exécution à l'endroit spécifié. Comme pour les points d'arrêt, la position peut être indiquée :
en donnant un numéro de ligne ;
en donnant un décalage en lignes par rapport à la prochaine instruction ;
en donnant un nom de fichier et un numéro de ligne ;
en donnant un nom de fonction ;
en donnant un nom de fichier et de fonction ;
en donnant une adresse.
Attention à ce que vous faites, car si vous passez d'une fonction à une autre, n'oubliez pas qu'il faudra peut-être régler vous même les paramètres (avec set var).
(gdb) return [value]
Exécute l'instruction de retour de la fonction dans laquelle vous vous trouvez. Vous pouvez préciser la valeur de retour.
Par exemple, si vous interrompez l'exécution dans la fonction myfunc(), vous pouvez utiliser return, ce qui quittera la fonction.
(gdb) call expression
Call est identique à print, sauf qu'il n'affiche le résultat que s'il est différent de void. En fait, avec print comme avec call, vous pouvez appeler des fonctions :
(gdb) print myfunc(5)
$1 = void
(gdb) call myfunc(5)
(gdb)
Un petit exemple
Avec tout ce que nous avons vu, nous pouvons maintenant facilement déboguer nos programmes. Voici un petit exemple assez simple.
#include <stdio.h>
int main(){
char * Buffer;
printf("Nom? ");
scanf("%s", Buffer);
return 0;
}
Bien entendu c'est un programme ridicule, et l'erreur est évidente, mais comme le but est juste de montrer le fonctionnement de GDB, nous nous en contenterons. Compilons :
$ g++ -Wall -Wextra -pedantic -ansi -g -o main sample1.cpp
Aucune erreur de compilation, même avec les options de compilation strictes (logique, syntaxiquement tout est correct). Mais lorsqu'on lance le programme, on obtient une segmentation fault dès qu'on entre quelque chose au clavier. Utilisons GDB :
$ gdb main
(gdb) run
Starting program: /home/dhkold/Documents/code/gdb/main
Nom? DHKold
Program received signal SIGSEGV, Segmentation fault.
0xb7d70dae in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
Voyons maintenant où nous nous trouvons :
(gdb) bt
#0 0xb7d70dae in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
#1 0xb7d784cb in scanf () from /lib/tls/i686/cmov/libc.so.6
#2 0x080484b4 in main () at sample1.cpp:7
L'erreur vient donc de l'appel à scanf fait depuis la ligne 7. Et en effet, Buffer n'est pas alloué et pointe sur n'importe quoi. On peut simplement recompiler après avoir corrigé, mais on peut aussi vérifier que l'erreur vient bien de là :
(gdb) start
Breakpoint 1 at 0x8048495: file sample1.cpp, line 6.
Starting program: /home/dhkold/Documents/code/gdb/main
main () at sample1.cpp:6
6 printf("Nom? ");
(gdb) set var Buffer = malloc(50)
(gdb) print Buffer
$1 = 0x804a008 ""
(gdb) c
Continuing.
Nom? DHKold
Program exited normally.
Et voilà, c'est donc bien Buffer qui pose problème, et il suffit de l'allouer pour ne plus avoir d'erreur. J'espère que ce tout petit exemple vous a permis de voir comment utiliser les commandes GDB que nous avons étudiées durant ce tutoriel. N'hésitez pas à aller lire la documentation sur le site de GDB, elle est en anglais et n'est pas toujours très claire, mais si vous connaissez déjà les bases vous devriez pouvoir vous y retrouver.
Voilà qui est fait, vous devriez maintenant être capables de vous servir de GDB correctement.