PulseView  unreleased development snapshot
A Qt-based sigrok GUI
main.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef ENABLE_DECODE
21 #include <libsigrokdecode/libsigrokdecode.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */
22 #endif
23 
24 #include <cstdint>
25 #include <fstream>
26 #include <getopt.h>
27 #include <vector>
28 
29 #ifdef ENABLE_FLOW
30 #include <gstreamermm.h>
31 #include <libsigrokflow/libsigrokflow.hpp>
32 #endif
33 
34 #include <libsigrokcxx/libsigrokcxx.hpp>
35 
36 #include <QCheckBox>
37 #include <QDebug>
38 #include <QFile>
39 #include <QFileInfo>
40 #include <QMessageBox>
41 #include <QSettings>
42 #include <QTextStream>
43 
44 #include "config.h"
45 
46 #ifdef ENABLE_SIGNALS
47 #include "signalhandler.hpp"
48 #endif
49 
50 #ifdef ENABLE_STACKTRACE
51 #include <signal.h>
52 #include <boost/stacktrace.hpp>
53 #include <QStandardPaths>
54 #endif
55 
56 #include "pv/application.hpp"
57 #include "pv/devicemanager.hpp"
58 #include "pv/globalsettings.hpp"
59 #include "pv/logging.hpp"
60 #include "pv/mainwindow.hpp"
61 #include "pv/session.hpp"
62 #include "pv/util.hpp"
63 #include "pv/data/segment.hpp"
64 
65 #ifdef ANDROID
66 #include <libsigrokandroidutils/libsigrokandroidutils.h>
67 #include "android/assetreader.hpp"
68 #include "android/loghandler.hpp"
69 #endif
70 
71 #ifdef _WIN32
72 #include <QtPlugin>
73 #ifdef QT_STATIC
74 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
75 Q_IMPORT_PLUGIN(QSvgPlugin)
76 #endif
77 #endif
78 
79 using std::exception;
80 using std::ifstream;
81 using std::ofstream;
82 using std::shared_ptr;
83 using std::string;
84 
85 #if ENABLE_STACKTRACE
86 QString stacktrace_filename;
87 
88 void signal_handler(int signum)
89 {
90  ::signal(signum, SIG_DFL);
91  boost::stacktrace::safe_dump_to(stacktrace_filename.toLocal8Bit().data());
92  ::raise(SIGABRT);
93 }
94 
95 void process_stacktrace(QString temp_path)
96 {
97  const QString stacktrace_outfile = temp_path + "/pv_stacktrace.txt";
98 
99  ifstream ifs(stacktrace_filename.toLocal8Bit().data());
100  ofstream ofs(stacktrace_outfile.toLocal8Bit().data(),
101  ofstream::out | ofstream::trunc);
102 
103  boost::stacktrace::stacktrace st =
104  boost::stacktrace::stacktrace::from_dump(ifs);
105  ofs << st;
106 
107  ofs.close();
108  ifs.close();
109 
110  QFile f(stacktrace_outfile);
111  f.open(QFile::ReadOnly | QFile::Text);
112  QTextStream fs(&f);
113  QString stacktrace = fs.readAll();
114  stacktrace = stacktrace.trimmed().replace('\n', "<br />");
115 
116  qDebug() << QObject::tr("Stack trace of previous crash:");
117  qDebug() << "---------------------------------------------------------";
118  // Note: qDebug() prints quotation marks for QString output, so we feed it char*
119  qDebug() << stacktrace.toLocal8Bit().data();
120  qDebug() << "---------------------------------------------------------";
121 
122  f.close();
123 
124  // Remove stack trace so we don't process it again the next time we run
125  QFile::remove(stacktrace_filename.toLocal8Bit().data());
126 
127  // Show notification dialog if permitted
128  pv::GlobalSettings settings;
129  if (settings.value(pv::GlobalSettings::Key_Log_NotifyOfStacktrace).toBool()) {
130  QCheckBox *cb = new QCheckBox(QObject::tr("Don't show this message again"));
131 
132  QMessageBox msgbox;
133  msgbox.setText(QObject::tr("When %1 last crashed, it created a stack trace.\n" \
134  "A human-readable form has been saved to disk and was written to " \
135  "the log. You may access it from the settings dialog.").arg(PV_TITLE));
136  msgbox.setIcon(QMessageBox::Icon::Information);
137  msgbox.addButton(QMessageBox::Ok);
138  msgbox.setCheckBox(cb);
139 
140  QObject::connect(cb, &QCheckBox::stateChanged, [](int state){
141  pv::GlobalSettings settings;
143  !state); });
144 
145  msgbox.exec();
146  }
147 }
148 #endif
149 
150 void usage()
151 {
152  fprintf(stdout,
153  "Usage:\n"
154  " %s [OPTIONS] [FILE]\n"
155  "\n"
156  "Help Options:\n"
157  " -h, -?, --help Show help option\n"
158  "\n"
159  "Application Options:\n"
160  " -V, --version Show release version\n"
161  " -l, --loglevel Set libsigrok/libsigrokdecode loglevel\n"
162  " -d, --driver Specify the device driver to use\n"
163  " -D, --dont-scan Don't auto-scan for devices, use -d spec only\n"
164  " -i, --input-file Load input from file\n"
165  " -s, --settings Load PulseView session setup from file\n"
166  " -I, --input-format Input format\n"
167  " -c, --clean Don't restore previous sessions on startup\n"
168  "\n", PV_BIN_NAME);
169 }
170 
171 int main(int argc, char *argv[])
172 {
173  int ret = 0;
174  shared_ptr<sigrok::Context> context;
175  string open_file_format, open_setup_file, driver;
176  vector<string> open_files;
177  bool restore_sessions = true;
178  bool do_scan = true;
179  bool show_version = false;
180 
181 #ifdef ENABLE_FLOW
182  // Initialise gstreamermm. Must be called before any other GLib stuff.
183  Gst::init();
184 
185  // Initialize libsigrokflow. Must be called after Gst::init().
186  Srf::init();
187 #endif
188 
189  Application a(argc, argv);
190 
191 #ifdef ANDROID
192  srau_init_environment();
194  pv::AndroidAssetReader asset_reader;
195 #endif
196 
197  // Parse arguments
198  while (true) {
199  static const struct option long_options[] = {
200  {"help", no_argument, nullptr, 'h'},
201  {"version", no_argument, nullptr, 'V'},
202  {"loglevel", required_argument, nullptr, 'l'},
203  {"driver", required_argument, nullptr, 'd'},
204  {"dont-scan", no_argument, nullptr, 'D'},
205  {"input-file", required_argument, nullptr, 'i'},
206  {"settings", required_argument, nullptr, 's'},
207  {"input-format", required_argument, nullptr, 'I'},
208  {"clean", no_argument, nullptr, 'c'},
209  {"log-to-stdout", no_argument, nullptr, 's'},
210  {nullptr, 0, nullptr, 0}
211  };
212 
213  const int c = getopt_long(argc, argv,
214  "h?VDcl:d:i:s:I:", long_options, nullptr);
215  if (c == -1)
216  break;
217 
218  switch (c) {
219  case 'h':
220  case '?':
221  usage();
222  return 0;
223 
224  case 'V':
225  show_version = true;
226  break;
227 
228  case 'l':
229  {
230  const int loglevel = atoi(optarg);
231  if (loglevel < 0 || loglevel > 5) {
232  qDebug() << "ERROR: invalid log level spec.";
233  break;
234  }
235  context->set_log_level(sigrok::LogLevel::get(loglevel));
236 
237 #ifdef ENABLE_DECODE
238  srd_log_loglevel_set(loglevel);
239 #endif
240 
241  if (loglevel >= 5) {
242  const QSettings settings;
243  qDebug() << "Settings:" << settings.fileName()
244  << "format" << settings.format();
245  }
246  break;
247  }
248 
249  case 'd':
250  driver = optarg;
251  break;
252 
253  case 'D':
254  do_scan = false;
255  break;
256 
257  case 'i':
258  open_files.emplace_back(optarg);
259  break;
260 
261  case 's':
262  open_setup_file = optarg;
263  break;
264 
265  case 'I':
266  open_file_format = optarg;
267  break;
268 
269  case 'c':
270  restore_sessions = false;
271  break;
272  }
273  }
274  argc -= optind;
275  argv += optind;
276 
277  for (int i = 0; i < argc; i++)
278  open_files.emplace_back(argv[i]);
279 
280  qRegisterMetaType<uint64_t>("uint64_t");
281  qRegisterMetaType<pv::util::Timestamp>("util::Timestamp");
282  qRegisterMetaType<SharedPtrToSegment>("SharedPtrToSegment");
283  qRegisterMetaType<shared_ptr<pv::data::SignalBase>>("shared_ptr<SignalBase>");
284 
285  // Prepare the global settings since logging needs them early on
286  pv::GlobalSettings settings;
287  settings.add_change_handler(&a); // Only the application object can't register itself
288  settings.save_internal_defaults();
289  settings.set_defaults_where_needed();
290  settings.apply_language();
291  settings.apply_theme();
292 
293  pv::logging.init();
294 
295  // Initialise libsigrok
296  context = sigrok::Context::create();
297  pv::Session::sr_context = context;
298 
299 #if ENABLE_STACKTRACE
300  QString temp_path = QStandardPaths::standardLocations(
301  QStandardPaths::TempLocation).at(0);
302  stacktrace_filename = temp_path + "/pv_stacktrace.dmp";
303  qDebug() << "Stack trace file is" << stacktrace_filename;
304 
305  ::signal(SIGSEGV, &signal_handler);
306  ::signal(SIGABRT, &signal_handler);
307 
308  if (QFileInfo::exists(stacktrace_filename))
309  process_stacktrace(temp_path);
310 #endif
311 
312 #ifdef ANDROID
313  context->set_resource_reader(&asset_reader);
314 #endif
315  do {
316 
317 #ifdef ENABLE_DECODE
318  // Initialise libsigrokdecode
319  if (srd_init(nullptr) != SRD_OK) {
320  qDebug() << "ERROR: libsigrokdecode init failed.";
321  break;
322  }
323 
324  // Load the protocol decoders
325  srd_decoder_load_all();
326 #endif
327 
328 #ifndef ENABLE_STACKTRACE
329  try {
330 #endif
331 
332  // Create the device manager, initialise the drivers
333  pv::DeviceManager device_manager(context, driver, do_scan);
334 
335  a.collect_version_info(device_manager);
336  if (show_version) {
337  a.print_version_info();
338  } else {
339  // Initialise the main window
340  pv::MainWindow w(device_manager);
341  w.show();
342 
343  if (restore_sessions)
344  w.restore_sessions();
345 
346  if (open_files.empty())
348  else
349  for (string& open_file : open_files)
350  w.add_session_with_file(open_file, open_file_format, open_setup_file);
351 
352 #ifdef ENABLE_SIGNALS
354  SignalHandler *const handler = new SignalHandler(&w);
355  QObject::connect(handler, SIGNAL(int_received()), &w, SLOT(close()));
356  QObject::connect(handler, SIGNAL(term_received()), &w, SLOT(close()));
357  QObject::connect(handler, SIGNAL(usr1_received()), &w, SLOT(on_run_stop_clicked()));
358  } else
359  qWarning() << "Could not prepare signal handler.";
360 #endif
361 
362  // Run the application
363  ret = a.exec();
364  }
365 
366 #ifndef ENABLE_STACKTRACE
367  } catch (exception& e) {
368  qDebug() << "Exception:" << e.what();
369  }
370 #endif
371 
372 #ifdef ENABLE_DECODE
373  // Destroy libsigrokdecode
374  srd_exit();
375 #endif
376 
377  } while (false);
378 
379  return ret;
380 }
void print_version_info()
Logging logging
Definition: logging.cpp:39
void add_session_with_file(string open_file_name, string open_file_format, string open_setup_file_name)
Definition: mainwindow.cpp:413
static void install_callbacks()
Definition: loghandler.cpp:117
CMake option(DISABLE_WERROR"Build without -Werror"TRUE) option(ENABLE_SIGNALS"Build with UNIX signals"TRUE) option(ENABLE_STACKTRACE"Enable stack trace when crashing"FALSE) option(ENABLE_DECODE"Build with libsigrokdecode"TRUE) option(ENABLE_FLOW"Build with libsigrokflow"FALSE) option(ENABLE_TESTS"Enable unit tests"FALSE) option(STATIC_PKGDEPS_LIBS"Statically link to (pkg-config) libraries"FALSE) option(ENABLE_TS_UPDATE"Update .ts source files (Qt l10n)"FALSE) if(WIN32) set(STATIC_PKGDEPS_LIBS TRUE) set(ENABLE_SIGNALS FALSE) endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING"Choose the type of build (None
void usage()
Definition: main.cpp:150
static const QString Key_Log_NotifyOfStacktrace
void restore_sessions()
Definition: mainwindow.cpp:476
static shared_ptr< sigrok::Context > sr_context
Definition: session.hpp:128
void collect_version_info(pv::DeviceManager &device_manager)
#define PV_BIN_NAME
Definition: config.h:25
void add_default_session()
Definition: mainwindow.cpp:420
void setValue(const QString &key, const QVariant &value)
static bool prepare_signals()
void init()
Definition: logging.cpp:69
#define PV_TITLE
Definition: config.h:24
int main(int argc, char *argv[])
Definition: main.cpp:171
static void add_change_handler(GlobalSettingsInterface *cb)