Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
QHyodaGdb.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2022 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7#include "QHyodaToolCell.h"
8#include "QHyodaMachine.h"
9
10QHyodaGdb::QHyodaGdb(QHyodaJob *_job,
11 QHyodaMachine *machine,
12 quint32 adrs,
13 quint32 port,
14 quint32 pyld):job(_job),
15 tab(machine),
16 tcpAdrs(adrs),
17 tcpPort(port),
18 tcpPyld(pyld),
19 process(new QProcess()),
20 state(QHyodaGdb::None),
21 data_read_memory(QString()){
22 qDebug() << "[QHyodaGdb::QHyodaGdb] NEW, tcpAdrs="<<tcpAdrs;
23}
24
25
26/**********************
27 * ~QHyodaGdb
28 **********************/
29QHyodaGdb::~QHyodaGdb(void){
30 qDebug() << "QHyodaGdb::QHyodaGdb DELETE";
31 process->close();
32 delete process;
33}
34
35
36/**********************
37 * launch
38 **********************/
39bool QHyodaGdb::launch(void){
40 QString command("ssh");
41 QStringList args = QStringList() << "-Tx"
42 << tab->localHostName
43 << "/usr/bin/gdb" // "/usr/local/bin/gdb"
44 << "--nw" // Do not use a window interface
45 << "--nx" // Do not read .gdbinit file
46 << "--interpreter=mi2"
47 // << "--readnever" // Do not read symbol files
48 // << "--readnow" // Fully read symbol files on first access
49 << "--se"
50 << cmdline;
51 qDebug() << "[QHyodaGdb::launch] command"<<command;
52 qDebug() << "[QHyodaGdb::launch] args"<<args;
53 process->setProcessChannelMode(QProcess::MergedChannels);
54 connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(gdbmi()));
55 process->start(command, args);
56 if (!process->waitForStarted())
57 qFatal("[QHyodaGdb::launch] NOT started!");
58 // Configuration initiale
59 enqueue("-gdb-set target-async 1");
60 enqueue(QString("-target-select remote %1:3883").arg(job->host));
61 // Permet de se configurer vis-à-vis de Papi
62 enqueue("-interpreter-exec console \"handle SIG36 nostop noprint pass\"");
63 enqueue("-break-insert Arcane::Hyoda::loopbreak"); // break #1
64 enqueue("-break-insert Arcane::Hyoda::hook"); // break #2
65 enqueue("-break-insert -d Arcane::Hyoda::softbreak"); // break #3 disabled by default
66 enqueue("-break-insert -d Arcane::EntryPoint::_getAddressForHyoda"); // break #4 disabled by default
67 //enqueue("-break-insert Arcane::Hyoda::softbreak");
68 //enqueue("-break-insert Arcane::EntryPoint::_getAddressForHyoda");
69 enqueue("-enable-pretty-printing");
70 return true;
71}
72
73
74/**********************
75 * enqueue
76 **********************/
77void QHyodaGdb::enqueue(const QString & t){
78 commands.enqueue(t);
79 process->write(commands.dequeue().append("\n").toLocal8Bit().constData());
80}
81
82
83/**********************
84 * dequeue
85 **********************/
86void QHyodaGdb::dequeue(void){
87 if (!commands.isEmpty())
88 process->write(commands.dequeue().append("\n").toLocal8Bit().constData());
89}
90
91
92/******************************************************************************
93 * Queued Handler de l'output du process GDB
94 *****************************************************************************/
96 const QStringList read_lines=QString(process->readAllStandardOutput()).split(QRegExp("\n"));
97 //qDebug() << "\n\nQHyodaGdb::gdbmi_parser_slot read_lines"<<read_lines;
98
99 for(int i=0; i<read_lines.size(); ++i){
100 const QString line=read_lines.at(i).trimmed();
101 //qDebug() << "QHyodaGdb::gdbmi filtering"<<line;
102 if (line.isEmpty()) continue;
103 if (line.startsWith("~")){
104 //job->gdbTextEdit->append(line.split(QRegExp("\"")).at(1).remove('\n'));
105 continue;
106 }
107 if (line.startsWith("&")){//log-stream-output
108 job->gdbTextEdit->append(line);
109 continue;
110 }
111 if (line.startsWith("=library-loaded")){
112 job->gdbTextEdit->append(QString("Loading %1").arg(line.split(QRegExp("\"")).at(1)));
113 continue;
114 }
115 if (line.startsWith("=")){//notify-async-output
116 //job->gdbTextEdit->append("=");//line);
117 continue;
118 }
119 if (line=="^done") {
120 //job->gdbTextEdit->append(line);
121 continue;
122 }
123 if (line=="^running") continue;
124 if (line.startsWith("*running")) continue;
125 if (line=="(gdb)") continue;
126
127 outputs.enqueue(line);
128 }
129
130 //qDebug() << "QHyodaGdb::gdbmi outputs: "<<outputs;
131
132 while (!outputs.isEmpty()){
133 const QString output=outputs.dequeue();
134
135 //qDebug() << output;
136 //qDebug() << "\tQHyodaGdb::gdbmi pending commands"<<*commands;
137 //qDebug() << "\n\tQHyodaGdb::gdbmi state"<<state;
138
140 if (state==QHyodaGdb::Detach){
141 state=QHyodaGdb::None;
142 qDebug() << "\t[QHyodaGdb::gdbmi] breaks cleanup, detaching and exit";
143 commands.enqueue("-var-assign qhyoda_hooked 0");
144 commands.enqueue("-break-disable 1"); // loopbreak
145 commands.enqueue("-break-disable 2"); // hook
146 commands.enqueue("-break-disable 3"); // softbreak
147 commands.enqueue("-break-disable 4"); // entry points
148 commands.enqueue("-break-delete 1");
149 commands.enqueue("-break-delete 2");
150 commands.enqueue("-break-delete 3");
151 commands.enqueue("-break-delete 4");
152 commands.enqueue("-target-detach");
153 commands.enqueue("-gdb-exit");
154 continue;
155 }
156
158 if (output.startsWith("^exit")){
159 qDebug() << "\t[QHyodaGdb::gdbmi] EXIT";
160 //job->quit();
161 continue;
162 }
163
164 // STOPPED becouse of normal exit
165 if (output.startsWith("*stopped,reason=\"exited-normally\"")){
166 qDebug() << "\t[QHyodaGdb::gdbmi] Exiting normally!";
167//#warning Exiting normally
168 //job->detach();
169 //job->quit();
170 //tab->close();
171 //QCoreApplication::exit(0);
172 //return;
173 continue;
174 }
175
176
177 /*****************************************************************
178 * BREAKPOINT #4 == Arcane::EntryPoint::_getAddressForHyoda *
179 * BaseForm[Hash["executeEntryPoint", "CRC32"], 10] = 3782526747 *
180 *****************************************************************/
181 if (output.startsWith("*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"4\"")){
182 int i;
183 //qDebug() << "\t[QHyodaGdb::gdbmi] breakpoint-hit getAddressForHyoda:"<<output;
184 if (!output.contains("next_entry_point_address")){
185 qDebug("\33[7mNo address for Hyoda!\33[m");
186 continue;
187 }
188 QStringList tokens=output.split(QRegExp("\""));
189 for(i=0;i<tokens.count();i+=1){
190 //qDebug() << "\t[QHyodaGdb::gdbmi] token #"<<i<<", is "<<tokens.at(i);
191 if (tokens.at(i).startsWith("next_entry_point_address")) break;
192 //if (tokens.at(i).startsWith("0x")) break;
193 }
194 if (i==tokens.count()) {
195 qDebug("File not found from address for Hyoda!");
196 while(true);
197 }
198 QString adrs=tokens.at(i+2);//.split(QRegExp("\"")).at(1);
199 qDebug() << "\t[QHyodaGdb::gdbmi] adrs="<<adrs;
200 int idxOfEntryPoint=entryPoints.indexOf(adrs);
201 qDebug() << "\t[QHyodaGdb::gdbmi] idxOfEntryPoint="<<idxOfEntryPoint;
202 // Il y a une phase d'apprentissage où l'on crée la liste des points d'entrées
203 if (idxOfEntryPoint==-1){ // no item matched: pas dans la liste
204 // On a enfin l'adresse du prochain EntryPoint que l'on va utiliser
205 //qDebug() << "\t[QHyodaGdb::gdbmi] " << QString("3782526747-data-disassemble -s %1 -e %1+1 -- 1").arg(adrs);
206 // On demande de désassembler l'adresse récupérée
207 commands.enqueue(QString("3782526747-data-disassemble -s %1 -e %1+1 -- 1").arg(adrs));
208 // Pour rajouter l'adresse à la liste des points d'entrée
209 entryPoints.append(adrs);
210 }else{
211 // Si le point d'entrée est associé à un couple fichier/ligne, on l'affiche
212 if (!entryPointsFile.at(idxOfEntryPoint).isEmpty()){
213 showViaEmacsClient(entryPointsFile.at(idxOfEntryPoint),entryPointsLine.at(idxOfEntryPoint));
214 job->startButton->setEnabled(true);
215 job->stepButton->setEnabled(true);
216 //qDebug() << "\t[QHyodaGdb::gdbmi] getAddressForHyoda & setEnabled";
217 }else
218 commands.enqueue("-exec-continue");
219 }
220 continue;
221 }
222 if (output.startsWith("3782526747")){
223 //qDebug() << "\t3782526748:QHyodaGdb::gdbmi file+line"<<output;
224 QString fileName;
225 QString lineNumber("+");
226 if (output.contains("file=")){
227 QStringList tokens=output.split(QRegExp(","));
228 //qDebug() << "\t3782526748:QHyodaGdb tokens="<<tokens;
229 fileName=tokens.at(2).split(QRegExp("\"")).at(1);
230 lineNumber.append(tokens.at(1).split(QRegExp("\"")).at(1));
231 qDebug()<<"\33[7m"<<fileName<<lineNumber<<"\33[m";
232 //showViaEmacsClient(fileName,lineNumber);
233 }
234 entryPointsFile.append(fileName);
235 entryPointsLine.append(lineNumber);
236 commands.enqueue("-exec-continue");
237 continue;
238 }
239
240
241 /**************
242 * SOFT BREAK *
243 **************/
244 if (output.startsWith("*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"3\"")){
245 //qDebug() << "\t[QHyodaGdb::gdbmi] breakpoint-hit softbreak:"<<output;
246 QString fileName;
247 QString lineNumber("+");
248 // Fetching filename and line number
249 const QStringList tokens=output.split(QRegExp(","));
250 for(int i=0; i<tokens.size(); ++i){
251 const QString token=tokens.at(i).trimmed();
252 //qDebug() << "QHyodaGdb::gdbmi softbreak filtering"<<token;
253 if (token.isEmpty()) continue;
254 if (token.startsWith("{name=\"fileName\"")){
255 const QStringList values=tokens.at(i+1).trimmed().split(QRegExp("\""));
256 //qDebug()<<"fileName="<<values.value(2);
257 fileName=values.value(2);
258 }
259 if (token.startsWith("{name=\"lineNumber\"")){
260 const QStringList values=tokens.at(i+1).trimmed().split(QRegExp("\""));
261 //qDebug()<<"lineNumber="<<values.value(1);
262 lineNumber.append(values.value(1));
263 }
264 }
265 fileName.remove('\\');
266 //qDebug()<<"fileName:"<<fileName<<", lineNumber="<<lineNumber;
267
268 showViaEmacsClient(fileName,lineNumber);
269 job->startButton->setEnabled(true);
270 job->stepButton->setEnabled(true);
271 continue;
272 }
273
274
275 /**************
276 * Hook BREAK *
277 **************/
278 if (output.startsWith("*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"2\"")){
279 //qDebug() << "\tQHyodaGdb::gdbmi breakpoint-hit hook";
280 // Si on veut changer de cell
281 if (state==QHyodaGdb::TargetCell){
282 //qDebug() << "\tQHyodaGdb::gdbmi TARGET CELL"<<job->target_cell_id;//tab->targetCellNumLineEdit->text();
283 commands.enqueue("-break-enable 1");
284 //commands.enqueue("-break-disable 3");
285 //commands.enqueue("-break-disable 4");
286 commands.enqueue("-exec-continue");
287 continue;
288 }
289 // Sinon, on rafraîchit les variables depuis Arcane
290 commands.enqueue(data_read_memory.toLocal8Bit().constData());
291 continue;
292 }
293
295 if (output.startsWith("^done,addr")){
296 //qDebug() << "\tQHyodaGdb::gdbmi ^done,addr: "<<output;
297 const QStringList splitted_output=output.split(QRegExp("\""));
298 job->refresh_common_variables(splitted_output);
299
300 if (job->bottomRightTools->tabText(job->bottomRightTools->currentIndex())=="Cell")
301 job->bottomRightTools->cell->refresh(splitted_output);
302
303 // Si un STOP a été demandé
304 if (state==QHyodaGdb::Interrupt){
305 state=QHyodaGdb::None;
306 commands.enqueue("-exec-interrupt");
307 job->tackButton->setEnabled(false);
308 job->startButton->setEnabled(true);
309 job->stepButton->setEnabled(true);
310 continue;
311 }
312
313 // Si un next a été demandé, on ne relance pas
314 if (state==QHyodaGdb::Step){
315 commands.enqueue("-break-enable 3");
316 //commands.enqueue("-break-enable 4");
317 job->startButton->setEnabled(true);
318 job->stepButton->setEnabled(true);
319 commands.enqueue("-exec-continue");
320 continue;
321 }
322
323 // Si le bouton untack a été poussé
324 if (state==QHyodaGdb::Untack){
325 state=QHyodaGdb::None;
326 //qDebug() << "\tQHyodaGdb::gdbmi UNTACK";
327 commands.enqueue("-break-disable 3");
328 commands.enqueue("-break-disable 4");
329 commands.enqueue("-var-assign qhyoda_hooked 0");
330 }
331
332 commands.enqueue("-exec-continue");
333 continue;
334 }
335
336
338 // Au delà de ce point, c'est de l'opérationnel moins critique //
340
342 if (output.startsWith("^done,value")){// On récupère l'adresse de la value
343 //qDebug() << "\tQHyodaGdb::gdbmi ^done,value";
344 // On ne chope que la première adresse (celle de 'data')
345 if (!data_read_memory.isNull()) continue;
346 data_read_memory.append("-data-read-memory ");
347 data_read_memory.append(output.split(QRegExp("\"")).at(1));
348 data_read_memory.append(" x 8 1 32"); // sizeof(64bits): struct gdb_data_to_output = 8+8*3 =32
349 //qDebug() << "\tQHyodaGdb::gdbmi VALUE, data_read_memory="<<data_read_memory.trimmed();
350 continue;
351 }
352
353
354 /* loopbreak: utile à l'init, lors d'un changement de maille et quand on se réaccroche */
355 if (output.startsWith("*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"1\"")){
356 //qDebug()<<"\33[7m\tQHyodaGdb::gdbmi *stopped breakpoint-hit @ Arcane::Hyoda::breakpoint, state="<<state<<"\33[m";
357
358 if (state==QHyodaGdb::Retack){
359 state=QHyodaGdb::None;
360 //qDebug() << "\tQHyodaGdb::gdbmi RETACK, now assigning hyoda";
361 commands.enqueue("-var-assign qhyoda_hooked 1");
362 commands.enqueue("-break-disable 1");
363 commands.enqueue("-break-disable 3");
364 commands.enqueue("-break-disable 4");
365 commands.enqueue("-exec-continue");
366 continue;
367 }
368
369 // Si on veut changer de cell
370 if (state==QHyodaGdb::TargetCell){
371 state=QHyodaGdb::None;
372 //qDebug() << "\tQHyodaGdb::gdbmi NOW MODIFYING TARGET CELL"<<job->data->target_cell_uid;
373 commands.enqueue(QString("-var-assign target_cell_uid %1").arg(job->data->target_cell_uid));
374 commands.enqueue("-break-disable 1");
375 commands.enqueue("-exec-continue");
376 continue;
377 }
378
379 // data est la première value que l'on fetch, afin de récupérer son adresse de base
380 commands.enqueue("-var-create data * m_data");
381 commands.enqueue("-var-set-format data hexadecimal");
382 commands.enqueue("-var-evaluate-expression data");
383
384 commands.enqueue("-var-create target_cell_uid * m_target_cell_uid");
385 commands.enqueue("-var-set-format target_cell_uid hexadecimal");
386 commands.enqueue("-var-assign target_cell_uid 0");
387
388 commands.enqueue("-var-create qhyoda_hooked * m_qhyoda_hooked");
389 commands.enqueue("-var-set-format qhyoda_hooked hexadecimal");
390
391 // Attention, dès qu'on met 'qhyoda_hooked' à 1, ça risque de breaker!
392 commands.enqueue("-var-assign qhyoda_hooked 1");
393
394 // Et on relache le breakpoint 1
395 commands.enqueue("-break-disable 1");
396 // Et on ne veut pas breaker sur le softbreak et les entryPoints
397 //commands.enqueue("-break-enable 3");
398 //commands.enqueue("-break-enable 4");
399 // Si on est en Local, il faut encore faire les dup2 et close
400 commands.enqueue("-exec-continue");
401 continue;
402 }
403
404 if (output.startsWith("^done,name=\"target_cell_uid\"")) continue;
405 if (output.startsWith("^done,name=\"qhyoda_hooked\"")) continue;
406 if (output.startsWith("^done,name=\"data\"")) continue;
407 if (output.startsWith("^done,format")) continue;
408 if (output.startsWith("^done,bkpt")){
409 //qDebug() << "\tQHyodaGdb::gdbmi "<<output;
410 job->gdbTextEdit->append(output);
411 continue;
412 }
413
414 /* Async STOP */
415 if (output.startsWith("*stopped")){
416 //qDebug() << "\tQHyodaGdb::gdbmi *stopped: "<<output;
417 if (state==QHyodaGdb::Retack){
418 qDebug() << "\tQHyodaGdb::gdbmi RETACK";
419 commands.enqueue("-break-enable 1");
420 //commands.enqueue("-break-enable 3");
421 //commands.enqueue("-break-enable 4");
422 commands.enqueue("-exec-continue");
423 continue;
424 }
425 job->gdbTextEdit->append(output);
426 // On tente de continuer
427 commands.enqueue("-exec-continue");
428 continue;
429 }
430
431 /* Connection initiale */
432 if (output.startsWith("^connected")){
433 state=QHyodaGdb::None;
434 qDebug() << "\tQHyodaGdb::gdbmi ^connected";
435 // On vient de se connecter, on autorise le clickodrome
436 job->arcaneCommonVariablesGroupBox->setEnabled(true);
437 job->stopButton->setEnabled(true);
438 job->stepButton->setEnabled(true);
439 job->startButton->setEnabled(false);
440 job->tackButton->setEnabled(true);
441 job->cellButton->setEnabled(true);
442 job->meshButton->setEnabled(true);
443 job->papi_slot();
444 job->mesh_slot();
445 qDebug() << "ARCANE_HYODA_MATRIX_RENDER:"<<qgetenv("ARCANE_HYODA_MATRIX_RENDER").count();
446 if (qgetenv("ARCANE_HYODA_MATRIX_RENDER").count()>0) job->matrix_slot();
447 continue;
448 }
449
450
452 // Au delà de ce point, il devrait rester les end-states //
454
455 if (output.startsWith("*running")){
456 qDebug() << "\tQHyodaGdb::gdbmi gdb_async_running";
457 job->gdbTextEdit->append(output);
458 continue;
459 }
460
461 if (output.startsWith("^error")){
462 state=QHyodaGdb::None;
463 qDebug() << "\tQHyodaGdb::gdbmi ^error"<<output;
464 job->gdbTextEdit->append(output);
465 continue;
466 }
467
468 if (output.startsWith("~")){
469 qDebug() << "\tQHyodaGdb::gdbmi console-stream-output:"<<output;
470 job->gdbTextEdit->append(output);
471 continue;
472 }
473
474 if (output.startsWith("=")){
475 qDebug() << "\tQHyodaGdb::gdbmi notify-async-output:"<<output;
476 job->gdbTextEdit->append(output);
477 continue;
478 }
479
480 qDebug() << "\tQHyodaGdb::gdbmi UNFILTERED "<<output;
481 job->gdbTextEdit->append(output);
482 }
483
484 dequeue();
485}
486
487void QHyodaGdb::showViaEmacsClient(QString file, QString line){
488 if (job->srcButton->isEnabled()) return;
489 //qDebug()<<"\33[7mshowViaEmacsClient"<<file<<line<<"\33[m";
490 QProcess EmacsClient(this);
491 EmacsClient.setProcessChannelMode(QProcess::MergedChannels);
492 QString emacsclient("emacsclient");
493 QStringList clientArgs=QStringList() << "-s"<< "src" << "-n" << line << file;
494 EmacsClient.start(emacsclient, clientArgs);
495 if (!EmacsClient.waitForStarted()) qFatal("QProcessEmacs NOT started!");
496 if (!EmacsClient.waitForFinished()) qFatal("QProcessEmacs NOT finished!");
497 EmacsClient.close();
498}
void gdbmi(void)
Definition QHyodaGdb.cc:95