source: rtems-tools/trace/record/record-main-lttng.cc @ 5d80d0b

5
Last change on this file since 5d80d0b was 5d80d0b, checked in by Sebastian Huber <sebastian.huber@…>, on 09/06/19 at 07:29:12

record: Optionally use LLVM to resolve addresses

Update #3665.

  • Property mode set to 100644
File size: 23.2 KB
Line 
1/*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Ravindra Kumar Meena <rmeena840@gmail.com>
5 * Copyright (C) 2018, 2019 embedded brains GmbH
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30
31#include "client.h"
32
33#include <getopt.h>
34
35#include <cassert>
36#include <cinttypes>
37#include <cstdio>
38#include <cstring>
39#include <iostream>
40#include <map>
41#include <string>
42#include <vector>
43
44#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
45#include <llvm/DebugInfo/Symbolize/Symbolize.h>
46#include <llvm/Support/Path.h>
47#endif
48
49#define CTF_MAGIC 0xC1FC1FC1
50#define TASK_RUNNING 0x0000
51#define TASK_IDLE 0x0402
52#define UUID_SIZE 16
53#define THREAD_NAME_SIZE 16
54#define THREAD_API_COUNT 3
55#define THREAD_ID_COUNT 0x10000
56#define BITS_PER_CHAR 8
57#define COMPACT_HEADER_ID 31
58
59static const uint8_t kEmptyThreadName[THREAD_API_COUNT] = "";
60
61static const uint8_t kUUID[] = {0x6a, 0x77, 0x15, 0xd0, 0xb5, 0x02, 0x4c, 0x65,
62                                0x86, 0x78, 0x67, 0x77, 0xac, 0x7f, 0x75, 0x5a};
63
64struct ClientItem {
65  uint64_t ns;
66  uint32_t cpu;
67  rtems_record_event event;
68  uint64_t data;
69};
70
71struct PacketHeader {
72  uint32_t ctf_magic;
73  uint8_t uuid[UUID_SIZE];
74  uint32_t stream_id;
75  uint64_t stream_instance_id;
76} __attribute__((__packed__));
77
78struct PacketContext {
79  PacketHeader header;
80  uint64_t timestamp_begin;
81  uint64_t timestamp_end;
82  uint64_t content_size;
83  uint64_t packet_size;
84  uint64_t packet_seq_num;
85  uint64_t events_discarded;
86  uint32_t cpu_id;
87} __attribute__((__packed__));
88
89static const size_t kPacketContextBits = sizeof(PacketContext) * BITS_PER_CHAR;
90
91struct EventHeaderCompact {
92  uint8_t id;
93  uint32_t event_id;
94  uint64_t ns;
95} __attribute__((__packed__));
96
97static const size_t kEventHeaderBits =
98    sizeof(EventHeaderCompact) * BITS_PER_CHAR;
99
100struct EventRecordItem {
101  EventHeaderCompact header;
102  uint64_t data;
103} __attribute__((__packed__));
104
105static const size_t kEventRecordItemBits =
106    sizeof(EventRecordItem) * BITS_PER_CHAR;
107
108struct EventSchedSwitch {
109  EventHeaderCompact header;
110  uint8_t prev_comm[THREAD_NAME_SIZE];
111  int32_t prev_tid;
112  int32_t prev_prio;
113  int64_t prev_state;
114  uint8_t next_comm[THREAD_NAME_SIZE];
115  int32_t next_tid;
116  int32_t next_prio;
117} __attribute__((__packed__));
118
119static const size_t kEventSchedSwitchBits =
120    sizeof(EventSchedSwitch) * BITS_PER_CHAR;
121
122struct EventIRQHandlerEntry {
123  EventHeaderCompact header;
124  int32_t irq;
125  uint8_t name[1];
126} __attribute__((__packed__));
127
128static const size_t kEventIRQHandlerEntryBits =
129    sizeof(EventIRQHandlerEntry) * BITS_PER_CHAR;
130
131struct EventIRQHandlerExit {
132  EventHeaderCompact header;
133  int32_t irq;
134  int32_t ret;
135} __attribute__((__packed__));
136
137static const size_t kEventIRQHandlerExitBits =
138    sizeof(EventIRQHandlerExit) * BITS_PER_CHAR;
139
140struct PerCPUContext {
141  FILE* event_stream;
142  uint64_t timestamp_begin;
143  uint64_t timestamp_end;
144  uint64_t size_in_bits;
145  uint32_t thread_id;
146  uint64_t thread_ns;
147  size_t thread_name_index;
148  EventRecordItem record_item;
149  EventSchedSwitch sched_switch;
150  EventIRQHandlerEntry irq_handler_entry;
151  EventIRQHandlerExit irq_handler_exit;
152};
153
154class LTTNGClient : public Client {
155 public:
156  LTTNGClient() {
157    Initialize(LTTNGClient::HandlerCaller);
158
159    std::memset(&pkt_ctx_, 0, sizeof(pkt_ctx_));
160    std::memcpy(pkt_ctx_.header.uuid, kUUID, sizeof(pkt_ctx_.header.uuid));
161    pkt_ctx_.header.ctf_magic = CTF_MAGIC;
162
163    for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) {
164      PerCPUContext& pcpu = per_cpu_[i];
165      pcpu.sched_switch.header.id = COMPACT_HEADER_ID;
166      pcpu.sched_switch.header.event_id = 1024;
167      pcpu.irq_handler_entry.header.id = COMPACT_HEADER_ID;
168      pcpu.irq_handler_entry.header.event_id = 1025;
169      pcpu.irq_handler_entry.name[0] = '\0';
170      pcpu.irq_handler_exit.header.id = COMPACT_HEADER_ID;
171      pcpu.irq_handler_exit.header.event_id = 1026;
172      pcpu.irq_handler_exit.ret = 1;
173      pcpu.record_item.header.id = COMPACT_HEADER_ID;
174    }
175  }
176
177  void OpenExecutable(const char* elf_file);
178
179  void Destroy() {
180    Client::Destroy();
181    CloseStreamFiles();
182  }
183
184 private:
185  PerCPUContext per_cpu_[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT];
186
187  /*
188   * @brief Thread names indexed by API and object index.
189   *
190   * The API indices are 0 for Internal API, 1 for Classic API and 2 for
191   * POSIX API.
192   */
193  uint8_t thread_names_[THREAD_API_COUNT][THREAD_ID_COUNT][THREAD_NAME_SIZE];
194
195  PacketContext pkt_ctx_;
196
197  size_t cpu_count_ = 0;
198
199#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
200  llvm::symbolize::LLVMSymbolizer symbolizer_;
201#endif
202
203  std::string elf_file_;
204
205  bool resolve_address_ = false;
206
207  typedef std::map<uint64_t, std::vector<char>> AddressToLineMap;
208
209  AddressToLineMap address_to_line_;
210
211  static rtems_record_client_status HandlerCaller(uint64_t bt,
212                                                  uint32_t cpu,
213                                                  rtems_record_event event,
214                                                  uint64_t data,
215                                                  void* arg) {
216    LTTNGClient& self = *static_cast<LTTNGClient*>(arg);
217    return self.Handler(bt, cpu, event, data);
218  }
219
220  rtems_record_client_status Handler(uint64_t bt,
221                                     uint32_t cpu,
222                                     rtems_record_event event,
223                                     uint64_t data);
224
225  void CopyThreadName(const ClientItem& item,
226                      size_t api_index,
227                      uint8_t* dst) const;
228
229  void WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item);
230
231  void WriteSchedSwitch(PerCPUContext* pcpu, const ClientItem& item);
232
233  void WriteIRQHandlerEntry(PerCPUContext* pcpu, const ClientItem& item);
234
235  void WriteIRQHandlerExit(PerCPUContext* pcpu, const ClientItem& item);
236
237  void AddThreadName(PerCPUContext* pcpu, const ClientItem& item);
238
239  void PrintItem(const ClientItem& item);
240
241  void OpenStreamFiles(uint64_t data);
242
243  void CloseStreamFiles();
244
245  AddressToLineMap::iterator AddAddressAsHexNumber(const ClientItem& item);
246
247  AddressToLineMap::iterator ResolveAddress(const ClientItem& item);
248};
249
250static uint32_t GetAPIIndexOfID(uint32_t id) {
251  return ((id >> 24) & 0x7) - 1;
252}
253
254static uint32_t GetObjIndexOfID(uint32_t id) {
255  return id & (THREAD_ID_COUNT - 1);
256}
257
258static bool IsIdleTaskByAPIIndex(uint32_t api_index) {
259  return api_index == 0;
260}
261
262static const uint8_t kCPUPostfix[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT][2] = {
263    {'0', '\0'}, {'1', '\0'}, {'2', '\0'}, {'3', '\0'}, {'4', '\0'},
264    {'5', '\0'}, {'6', '\0'}, {'7', '\0'}, {'8', '\0'}, {'9', '\0'},
265    {'1', '0'},  {'1', '1'},  {'1', '2'},  {'1', '3'},  {'1', '4'},
266    {'1', '5'},  {'1', '6'},  {'1', '7'},  {'1', '8'},  {'1', '9'},
267    {'2', '0'},  {'2', '1'},  {'2', '2'},  {'2', '3'},  {'2', '4'},
268    {'2', '5'},  {'2', '6'},  {'2', '7'},  {'2', '8'},  {'2', '9'},
269    {'3', '0'},  {'3', '1'}};
270
271void LTTNGClient::OpenExecutable(const char* elf_file) {
272  elf_file_ = elf_file;
273  resolve_address_ = true;
274}
275
276void LTTNGClient::CopyThreadName(const ClientItem& item,
277                                 size_t api_index,
278                                 uint8_t* dst) const {
279  const uint8_t* name;
280
281  if (api_index < THREAD_API_COUNT) {
282    name = thread_names_[api_index][GetObjIndexOfID(item.data)];
283  } else {
284    name = kEmptyThreadName;
285  }
286
287  std::memcpy(dst, name, THREAD_NAME_SIZE);
288
289  if (IsIdleTaskByAPIIndex(api_index)) {
290    /*
291     * In Linux, the idle threads are bound to a specific CPU (swapper/n).  In
292     * RTEMS, the idle threads can move around, so mimic this Linux behaviour.
293     */
294    dst[4] = '/';
295    dst[5] = kCPUPostfix[item.cpu][0];
296    dst[6] = kCPUPostfix[item.cpu][1];
297  }
298}
299
300static bool IsCodeEvent(rtems_record_event event) {
301  switch (event) {
302    default:
303      return false;
304    case RTEMS_RECORD_CALLER:
305    case RTEMS_RECORD_FUNCTION_ENTRY:
306    case RTEMS_RECORD_FUNCTION_EXIT:
307    case RTEMS_RECORD_ISR_DISABLE:
308    case RTEMS_RECORD_ISR_ENABLE:
309    case RTEMS_RECORD_LINE:
310    case RTEMS_RECORD_THREAD_DISPATCH_DISABLE:
311    case RTEMS_RECORD_THREAD_DISPATCH_ENABLE:
312      return true;
313  }
314}
315
316LTTNGClient::AddressToLineMap::iterator LTTNGClient::AddAddressAsHexNumber(
317    const ClientItem& item) {
318  char hex[19];
319  int n = std::snprintf(hex, sizeof(hex), "0x%" PRIx64, item.data);
320  assert(static_cast<size_t>(n) < sizeof(hex));
321  std::vector<char> code(hex, hex + n + 1);
322  return address_to_line_.emplace(item.data, std::move(code)).first;
323}
324
325LTTNGClient::AddressToLineMap::iterator LTTNGClient::ResolveAddress(
326    const ClientItem& item) {
327#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
328  if (resolve_address_) {
329    auto res_or_err = symbolizer_.symbolizeCode(elf_file_, item.data);
330
331    if (res_or_err) {
332      auto info = res_or_err.get();
333      std::string fn = info.FunctionName;
334      std::string str;
335
336      if (fn != "<invalid>") {
337        str += fn;
338        str += " at ";
339      }
340
341      str += llvm::sys::path::filename(info.FileName);
342      str += ":";
343      str += std::to_string(info.Line);
344      std::vector<char> code(str.begin(), str.end());
345      code.push_back('\0');
346      return address_to_line_.emplace(item.data, std::move(code)).first;
347    }
348  }
349#endif
350
351  return AddAddressAsHexNumber(item);
352}
353
354void LTTNGClient::WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item) {
355  if (IsCodeEvent(item.event)) {
356    EventHeaderCompact header;
357    header.id = COMPACT_HEADER_ID;
358    header.event_id = item.event;
359    header.ns = item.ns;
360
361    auto it = address_to_line_.find(item.data);
362    if (it == address_to_line_.end()) {
363      it = ResolveAddress(item);
364    }
365
366    pcpu->size_in_bits += (sizeof(header) + it->second.size()) * BITS_PER_CHAR;
367
368    std::fwrite(&header, sizeof(header), 1, pcpu->event_stream);
369    std::fwrite(&(*it->second.begin()), it->second.size(), 1,
370                pcpu->event_stream);
371  } else {
372    pcpu->size_in_bits += kEventRecordItemBits;
373
374    EventRecordItem& ri = pcpu->record_item;
375    ri.header.ns = item.ns;
376    ri.header.event_id = item.event;
377    ri.data = item.data;
378
379    std::fwrite(&ri, sizeof(ri), 1, pcpu->event_stream);
380  }
381}
382
383void LTTNGClient::WriteSchedSwitch(PerCPUContext* pcpu,
384                                   const ClientItem& item) {
385  pcpu->size_in_bits += kEventSchedSwitchBits;
386
387  EventSchedSwitch& ss = pcpu->sched_switch;
388  ss.header.ns = item.ns;
389
390  uint32_t api_index = GetAPIIndexOfID(item.data);
391  ss.next_tid = IsIdleTaskByAPIIndex(api_index) ? 0 : item.data;
392
393  CopyThreadName(item, api_index, ss.next_comm);
394  std::fwrite(&ss, sizeof(ss), 1, pcpu->event_stream);
395}
396
397void LTTNGClient::WriteIRQHandlerEntry(PerCPUContext* pcpu,
398                                       const ClientItem& item) {
399  pcpu->size_in_bits += kEventIRQHandlerEntryBits;
400
401  EventIRQHandlerEntry& ih = pcpu->irq_handler_entry;
402  ih.header.ns = item.ns;
403  ih.irq = static_cast<int32_t>(item.data);
404  std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
405}
406
407void LTTNGClient::WriteIRQHandlerExit(PerCPUContext* pcpu,
408                                      const ClientItem& item) {
409  pcpu->size_in_bits += kEventIRQHandlerExitBits;
410
411  EventIRQHandlerExit& ih = pcpu->irq_handler_exit;
412  ih.header.ns = item.ns;
413  ih.irq = static_cast<int32_t>(item.data);
414  std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
415}
416
417void LTTNGClient::AddThreadName(PerCPUContext* pcpu, const ClientItem& item) {
418  if (pcpu->thread_name_index >= THREAD_NAME_SIZE) {
419    return;
420  }
421
422  uint32_t api_index = GetAPIIndexOfID(pcpu->thread_id);
423  if (api_index >= THREAD_API_COUNT) {
424    return;
425  }
426
427  uint32_t obj_index = GetObjIndexOfID(pcpu->thread_id);
428  uint64_t name = item.data;
429  size_t i;
430  for (i = pcpu->thread_name_index; i < pcpu->thread_name_index + data_size();
431       ++i) {
432    thread_names_[api_index][obj_index][i] = static_cast<uint8_t>(name);
433    name >>= BITS_PER_CHAR;
434  }
435
436  pcpu->thread_name_index = i;
437}
438
439void LTTNGClient::PrintItem(const ClientItem& item) {
440  PerCPUContext& pcpu = per_cpu_[item.cpu];
441  if (pcpu.timestamp_begin == 0) {
442    pcpu.timestamp_begin = item.ns;
443  }
444
445  pcpu.timestamp_end = item.ns;
446
447  EventSchedSwitch& ss = pcpu.sched_switch;
448  switch (item.event) {
449    case RTEMS_RECORD_THREAD_SWITCH_OUT: {
450      uint32_t api_index = GetAPIIndexOfID(item.data);
451      ss.header.ns = item.ns;
452
453      if (IsIdleTaskByAPIIndex(api_index)) {
454        ss.prev_tid = 0;
455        ss.prev_state = TASK_IDLE;
456      } else {
457        ss.prev_tid = item.data;
458        ss.prev_state = TASK_RUNNING;
459      }
460
461      CopyThreadName(item, api_index, ss.prev_comm);
462      break;
463    }
464    case RTEMS_RECORD_THREAD_SWITCH_IN:
465      if (item.ns == ss.header.ns) {
466        WriteSchedSwitch(&pcpu, item);
467      }
468      break;
469    case RTEMS_RECORD_THREAD_ID:
470      pcpu.thread_id = item.data;
471      pcpu.thread_ns = item.ns;
472      pcpu.thread_name_index = 0;
473      break;
474    case RTEMS_RECORD_INTERRUPT_ENTRY:
475      WriteIRQHandlerEntry(&pcpu, item);
476      break;
477    case RTEMS_RECORD_INTERRUPT_EXIT:
478      WriteIRQHandlerExit(&pcpu, item);
479      break;
480    case RTEMS_RECORD_THREAD_NAME:
481      AddThreadName(&pcpu, item);
482      break;
483    case RTEMS_RECORD_PROCESSOR_MAXIMUM:
484      OpenStreamFiles(item.data);
485      break;
486    default:
487      if (item.ns != 0) {
488        WriteRecordItem(&pcpu, item);
489      }
490      break;
491  }
492}
493
494rtems_record_client_status LTTNGClient::Handler(uint64_t bt,
495                                                uint32_t cpu,
496                                                rtems_record_event event,
497                                                uint64_t data) {
498  ClientItem item;
499
500  item.ns = rtems_record_client_bintime_to_nanoseconds(bt);
501  item.cpu = cpu;
502  item.event = event;
503  item.data = data;
504
505  PrintItem(item);
506
507  return RTEMS_RECORD_CLIENT_SUCCESS;
508}
509
510void LTTNGClient::OpenStreamFiles(uint64_t data) {
511  // Assertions are ensured by C record client
512  assert(cpu_count_ == 0 && data < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT);
513  cpu_count_ = static_cast<size_t>(data) + 1;
514
515  for (size_t i = 0; i < cpu_count_; ++i) {
516    std::string filename("stream_");
517    filename += std::to_string(i);
518    FILE* f = std::fopen(filename.c_str(), "wb");
519    if (f == NULL) {
520      throw ErrnoException("cannot create file '" + filename + "'");
521    }
522    per_cpu_[i].event_stream = f;
523    std::fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, f);
524  }
525}
526
527void LTTNGClient::CloseStreamFiles() {
528  for (size_t i = 0; i < cpu_count_; ++i) {
529    PerCPUContext* pcpu = &per_cpu_[i];
530    std::fseek(pcpu->event_stream, 0, SEEK_SET);
531
532    pkt_ctx_.header.stream_instance_id = i;
533    pkt_ctx_.timestamp_begin = pcpu->timestamp_begin;
534    pkt_ctx_.timestamp_end = pcpu->timestamp_end;
535    pkt_ctx_.content_size = pcpu->size_in_bits + kPacketContextBits;
536    pkt_ctx_.packet_size = pkt_ctx_.content_size;
537    pkt_ctx_.cpu_id = i;
538
539    std::fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, pcpu->event_stream);
540    std::fclose(pcpu->event_stream);
541  }
542}
543
544static const char kMetadata[] =
545    "/* CTF 1.8 */\n"
546    "\n"
547    "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
548    "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n"
549    "typealias integer { size = 32; align = 8; signed = true; } := int32_t;\n"
550    "typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n"
551    "typealias integer { size = 64; align = 8; signed = true; } := int64_t;\n"
552    "typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n"
553    "\n"
554    "typealias integer {\n"
555    "\tsize = 64; align = 8; signed = false; base = 16;\n"
556    "} := xint64_t;\n"
557    "\n"
558    "typealias integer {\n"
559    "\tsize = 8; align = 8; signed = false; encoding = UTF8; base = 10;\n"
560    "} := utf8_t;\n"
561    "\n"
562    "typealias integer {\n"
563    "\tsize = 27; align = 1; signed = false;\n"
564    "\tmap = clock.monotonic.value;\n"
565    "} := uint27_clock_monotonic_t;\n"
566    "\n"
567    "typealias integer {\n"
568    "\tsize = 64; align = 8; signed = false;\n"
569    "\tmap = clock.monotonic.value;\n"
570    "} := uint64_clock_monotonic_t;\n"
571    "\n"
572    "trace {\n"
573    "\tmajor = 1;\n"
574    "\tminor = 8;\n"
575    "\tuuid = \"6a7715d0-b502-4c65-8678-6777ac7f755a\";\n"
576    "\tbyte_order = le;\n"
577    "\tpacket.header := struct {\n"
578    "\t\tuint32_t magic;\n"
579    "\t\tuint8_t  uuid[16];\n"
580    "\t\tuint32_t stream_id;\n"
581    "\t\tuint64_t stream_instance_id;\n"
582    "\t};\n"
583    "};\n"
584    "\n"
585    "env {\n"
586    "\thostname = \"RTEMS\";\n"
587    "\tdomain = \"kernel\";\n"
588    "\tsysname = \"Linux\";\n"
589    "\tkernel_release = \"5\";\n"
590    "\tkernel_version = \"0\";\n"
591    "\ttracer_name = \"lttng-modules\";\n"
592    "\ttracer_major = 2;\n"
593    "\ttracer_minor = 11;\n"
594    "\ttracer_patchlevel = 0;\n"
595    "};\n"
596    "\n"
597    "clock {\n"
598    "\tname = \"monotonic\";\n"
599    "\tuuid = \"234d669d-7651-4bc1-a7fd-af581ecc6232\";\n"
600    "\tdescription = \"Monotonic Clock\";\n"
601    "\tfreq = 1000000000;\n"
602    "\toffset = 0;\n"
603    "};\n"
604    "\n"
605    "struct packet_context {\n"
606    "\tuint64_clock_monotonic_t timestamp_begin;\n"
607    "\tuint64_clock_monotonic_t timestamp_end;\n"
608    "\tuint64_t content_size;\n"
609    "\tuint64_t packet_size;\n"
610    "\tuint64_t packet_seq_num;\n"
611    "\tuint64_t events_discarded;\n"
612    "\tuint32_t cpu_id;\n"
613    "};\n"
614    "\n"
615    "struct event_header_compact {\n"
616    "\tenum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
617    "\tvariant <id> {\n"
618    "\t\tstruct {\n"
619    "\t\t\tuint27_clock_monotonic_t timestamp;\n"
620    "\t\t} compact;\n"
621    "\t\tstruct {\n"
622    "\t\t\tuint32_t id;\n"
623    "\t\t\tuint64_clock_monotonic_t timestamp;\n"
624    "\t\t} extended;\n"
625    "\t} v;\n"
626    "} align(8);\n"
627    "\n"
628    "stream {\n"
629    "\tid = 0;\n"
630    "\tevent.header := struct event_header_compact;\n"
631    "\tpacket.context := struct packet_context;\n"
632    "};\n"
633    "\n"
634    "event {\n"
635    "\tname = sched_switch;\n"
636    "\tid = 1024;\n"
637    "\tstream_id = 0;\n"
638    "\tfields := struct {\n"
639    "\t\tutf8_t _prev_comm[16];\n"
640    "\t\tint32_t _prev_tid;\n"
641    "\t\tint32_t _prev_prio;\n"
642    "\t\tint64_t _prev_state;\n"
643    "\t\tutf8_t _next_comm[16];\n"
644    "\t\tint32_t _next_tid;\n"
645    "\t\tint32_t _next_prio;\n"
646    "\t};\n"
647    "};\n"
648    "\n"
649    "event {\n"
650    "\tname = irq_handler_entry;\n"
651    "\tid = 1025;\n"
652    "\tstream_id = 0;\n"
653    "\tfields := struct {\n"
654    "\t\tint32_t _irq;\n"
655    "\t\tstring _name;\n"
656    "\t};\n"
657    "};\n"
658    "\n"
659    "event {\n"
660    "\tname = irq_handler_exit;\n"
661    "\tid = 1026;\n"
662    "\tstream_id = 0;\n"
663    "\tfields := struct {\n"
664    "\t\tint32_t _irq;\n"
665    "\t\tint32_t _ret;\n"
666    "\t};\n"
667    "};\n";
668
669static void GenerateMetadata() {
670  FILE* f = std::fopen("metadata", "w");
671  if (f == NULL) {
672    throw ErrnoException("cannot create file 'metadata'");
673  }
674
675  std::fwrite(kMetadata, sizeof(kMetadata) - 1, 1, f);
676
677  for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
678    if (IsCodeEvent(static_cast<rtems_record_event>(i))) {
679      std::fprintf(f,
680                   "\n"
681                   "event {\n"
682                   "\tname = %s;\n"
683                   "\tid = %i;\n"
684                   "\tstream_id = 0;\n"
685                   "\tfields := struct {\n"
686                   "\t\tstring _code;\n"
687                   "\t};\n"
688                   "};\n",
689                   rtems_record_event_text(static_cast<rtems_record_event>(i)),
690                   i);
691    } else {
692      std::fprintf(f,
693                   "\n"
694                   "event {\n"
695                   "\tname = %s;\n"
696                   "\tid = %i;\n"
697                   "\tstream_id = 0;\n"
698                   "\tfields := struct {\n"
699                   "\t\txint64_t _data;\n"
700                   "\t};\n"
701                   "};\n",
702                   rtems_record_event_text(static_cast<rtems_record_event>(i)),
703                   i);
704    }
705  }
706
707  std::fclose(f);
708}
709
710static LTTNGClient client;
711
712static void SignalHandler(int s) {
713  client.RequestStop();
714  std::signal(s, SIG_DFL);
715}
716
717static const struct option kLongOpts[] = {{"help", 0, NULL, 'h'},
718                                          {"host", 1, NULL, 'H'},
719                                          {"port", 1, NULL, 'p'},
720                                          {NULL, 0, NULL, 0}};
721
722static void Usage(char** argv) {
723  std::cout
724      << argv[0]
725      << " [--host=HOST] [--port=PORT] [--limit=LIMIT] [--elf=ELF] [INPUT-FILE]"
726      << std::endl
727      << std::endl
728      << "Mandatory arguments to long options are mandatory for short "
729         "options too."
730      << std::endl
731      << "  -h, --help                 print this help text" << std::endl
732      << "  -H, --host=HOST            the host IPv4 address of the "
733         "record server"
734      << std::endl
735      << "  -p, --port=PORT            the TCP port of the record server"
736      << std::endl
737      << "  -l, --limit=LIMIT          limit in bytes to process" << std::endl
738      << "  -e, --elf=ELF              the ELF executable file" << std::endl
739      << "  INPUT-FILE                 the input file" << std::endl;
740}
741
742int main(int argc, char** argv) {
743  const char* host = "127.0.0.1";
744  uint16_t port = 1234;
745  const char* elf_file = nullptr;
746  const char* input_file = nullptr;
747  int opt;
748  int longindex;
749
750  while ((opt = getopt_long(argc, argv, "e:hH:l:p:", &kLongOpts[0],
751                            &longindex)) != -1) {
752    switch (opt) {
753      case 'e':
754        elf_file = optarg;
755        break;
756      case 'h':
757        Usage(argv);
758        return 0;
759      case 'H':
760        host = optarg;
761        break;
762      case 'l':
763        client.set_limit(strtoull(optarg, NULL, 0));
764        break;
765      case 'p':
766        port = (uint16_t)strtoul(optarg, NULL, 0);
767        break;
768      default:
769        return 1;
770    }
771  }
772
773  if (optind == argc - 1) {
774    input_file = argv[optind];
775    ++optind;
776  }
777
778  if (optind != argc) {
779    std::cerr << argv[0] << ": unrecognized options:";
780    for (int i = optind; i < argc; ++i) {
781      std::cerr << " '" << argv[i] << "'";
782    }
783    std::cerr << std::endl;
784    return 1;
785  }
786
787  try {
788    GenerateMetadata();
789
790    if (elf_file != nullptr) {
791      client.OpenExecutable(elf_file);
792    }
793
794    if (input_file != nullptr) {
795      client.Open(input_file);
796    } else {
797      client.Connect(host, port);
798    }
799
800    std::signal(SIGINT, SignalHandler);
801    client.Run();
802    client.Destroy();
803  } catch (std::exception& e) {
804    std::cerr << argv[0] << ": " << e.what() << std::endl;
805    return 1;
806  }
807
808  return 0;
809}
Note: See TracBrowser for help on using the repository browser.