summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/rtems-bsps-arm.ini5
-rw-r--r--config/rtems-bsps-epiphany.ini25
-rw-r--r--config/rtems-bsps-powerpc.ini18
-rw-r--r--config/rtems-bsps-tiers.ini25
-rw-r--r--config/rtems-bsps.ini3
-rw-r--r--config/rtems-version.ini2
-rw-r--r--linkers/rtems-exeinfo.cpp167
-rw-r--r--linkers/rtems-score-object.ini19
-rw-r--r--linkers/rtems-score-thread.ini15
-rw-r--r--linkers/rtems-syms.cpp8
-rw-r--r--linkers/rtld-print.ini2
-rw-r--r--linkers/wscript1
-rw-r--r--misc/__init__.py (renamed from tester/rtems/testing/bsps/arm1136js-run.ini)13
-rw-r--r--misc/bin2c/rtems-bin2c.c24
-rwxr-xr-xmisc/rtems-boot-image31
-rwxr-xr-xmisc/rtems-tftp-proxy (renamed from tester/rt/cmd-test.py)10
-rw-r--r--misc/tools/__init__.py30
-rw-r--r--misc/tools/boot.py2
-rwxr-xr-xmisc/tools/cmd-boot-image.py44
-rw-r--r--misc/tools/getmac/LICENSE (renamed from tester/rt/tftpy/COPYING)12
-rw-r--r--misc/tools/getmac/__init__.py2
-rw-r--r--misc/tools/getmac/__main__.py67
-rw-r--r--misc/tools/getmac/getmac.py603
-rwxr-xr-xmisc/tools/mkimage.py156
-rw-r--r--misc/tools/tftpproxy.py423
-rw-r--r--misc/wscript14
-rw-r--r--rtemstoolkit/configuration.py2
-rwxr-xr-xrtemstoolkit/elftoolchain/common/native-elf-format2
-rwxr-xr-xrtemstoolkit/execute.py19
-rw-r--r--rtemstoolkit/linux.py19
-rw-r--r--rtemstoolkit/path.py8
-rw-r--r--rtemstoolkit/rld-dwarf.cpp275
-rw-r--r--rtemstoolkit/rld-dwarf.h69
-rw-r--r--rtemstoolkit/rld-elf.cpp5
-rw-r--r--rtemstoolkit/rld-rap.cpp16
-rw-r--r--rtemstoolkit/rld-symbols.cpp3
-rwxr-xr-xrtemstoolkit/rtems.py11
-rw-r--r--rtemstoolkit/wscript6
-rw-r--r--tester/__init__.py30
-rw-r--r--tester/covoar/wscript1
-rwxr-xr-xtester/rt/check.py4
-rwxr-xr-xtester/rt/cmd-run.py44
-rw-r--r--tester/rt/config.py72
-rw-r--r--tester/rt/console.py8
-rw-r--r--tester/rt/coverage.py2
-rw-r--r--tester/rt/exe.py172
-rw-r--r--tester/rt/gdb.py138
-rw-r--r--tester/rt/report.py49
-rw-r--r--tester/rt/run.py30
-rw-r--r--tester/rt/test.py345
-rw-r--r--tester/rt/tftp.py78
-rw-r--r--tester/rt/tftpserver.py723
-rw-r--r--tester/rt/tftpy/README115
-rw-r--r--tester/rt/tftpy/TftpClient.py107
-rw-r--r--tester/rt/tftpy/TftpContexts.py429
-rw-r--r--tester/rt/tftpy/TftpPacketFactory.py47
-rw-r--r--tester/rt/tftpy/TftpPacketTypes.py494
-rw-r--r--tester/rt/tftpy/TftpServer.py266
-rw-r--r--tester/rt/tftpy/TftpShared.py52
-rw-r--r--tester/rt/tftpy/TftpStates.py611
-rw-r--r--tester/rt/tftpy/__init__.py27
-rwxr-xr-xtester/rtems-bsp-builder31
-rwxr-xr-xtester/rtems-run31
-rwxr-xr-xtester/rtems-test31
-rwxr-xr-xtester/rtems-tftp-server (renamed from tester/rt/cmd-bsp-builder.py)44
-rw-r--r--tester/rtems/testing/bsps/a53_ilp32_qemu.ini (renamed from tester/rtems/testing/bsps/arm1136jfs.ini)17
-rw-r--r--tester/rtems/testing/bsps/a53_lp64_qemu.ini (renamed from tester/rtems/testing/bsps/arm1136jfs-run.ini)15
-rw-r--r--tester/rtems/testing/bsps/arm7tdmi.ini41
-rw-r--r--tester/rtems/testing/bsps/arm920.ini41
-rw-r--r--tester/rtems/testing/bsps/armcortexa9-run.ini39
-rw-r--r--tester/rtems/testing/bsps/armcortexa9.ini41
-rw-r--r--tester/rtems/testing/bsps/generic_or1k.ini2
-rw-r--r--tester/rtems/testing/bsps/gr740-sis.ini (renamed from tester/rtems/testing/bsps/erc32-run.ini)12
-rw-r--r--tester/rtems/testing/bsps/leon3-qemu-cov.ini2
-rw-r--r--tester/rtems/testing/bsps/leon3-qemu.ini2
-rw-r--r--tester/rtems/testing/bsps/pc-qemu.ini (renamed from tester/rtems/testing/bsps/arm7tdmi-run.ini)16
-rw-r--r--tester/rtems/testing/bsps/psim-device-tree36
-rw-r--r--tester/rtems/testing/bsps/raspberrypi2.ini43
-rw-r--r--tester/rtems/testing/bsps/rv64imafdc_medany.ini (renamed from tester/rtems/testing/bsps/arm920-run.ini)17
-rw-r--r--tester/rtems/testing/bsps/xilinx_zynq_a9_qemu.ini2
-rw-r--r--tester/rtems/testing/bsps/xilinx_zynq_a9_qemu_smp.ini2
-rw-r--r--tester/rtems/testing/bsps/xilinx_zynq_zc706_qemu.ini2
-rw-r--r--tester/rtems/testing/bsps/xilinx_zynqmp_ilp32.ini38
-rw-r--r--tester/rtems/testing/bsps/xilinx_zynqmp_lp64.ini (renamed from tester/rtems/testing/bsps/arm1136js.ini)17
-rw-r--r--tester/rtems/testing/qemu.cfg5
-rw-r--r--tester/rtems/testing/testing.mc3
-rw-r--r--tester/rtems/version.cfg2
-rw-r--r--tester/wscript26
-rw-r--r--trace/record/client.h198
-rw-r--r--trace/record/inih/LICENSE.txt27
-rw-r--r--trace/record/inih/ini.c284
-rw-r--r--trace/record/inih/ini.h148
-rw-r--r--trace/record/record-client-base.cc199
-rw-r--r--trace/record/record-client.c729
-rw-r--r--trace/record/record-filter-base64.cc103
-rw-r--r--trace/record/record-filter-zlib.cc77
-rw-r--r--trace/record/record-main-lttng.cc899
-rw-r--r--trace/record/record-text.c1071
-rw-r--r--trace/record/rtems/recordclient.h245
-rw-r--r--trace/record/rtems/recorddata.h1204
-rw-r--r--trace/wscript103
-rwxr-xr-xwaf19
-rw-r--r--wscript7
103 files changed, 8898 insertions, 2973 deletions
diff --git a/config/rtems-bsps-arm.ini b/config/rtems-bsps-arm.ini
index ae4990d..02b54e7 100644
--- a/config/rtems-bsps-arm.ini
+++ b/config/rtems-bsps-arm.ini
@@ -22,7 +22,7 @@
#
[arm]
bsps = altcycv_devkit,
- arm1136jfs, arm1136js, arm7tdmi, arm920, armcortexa9, atsamv,
+ atsamv,
beagleboardorig, beagleboardxm, beagleboneblack, beaglebonewhite,
csb336, csb337, csb637,
edb7312,
@@ -47,8 +47,7 @@ bsps = altcycv_devkit,
tms570ls3137_hdk_with_loader,
xilinx_zynq_zc702, xilinx_zynq_zc706, xilinx_zynq_zedboard,
xilinx_zynq_a9_qemu
-exclude-smp = arm1136jfs,
- arm1136js, arm7tdmi, arm920, armcortexa9, atsamv,
+exclude-smp = atsamv,
beagleboardorig, beagleboardxm, beagleboneblack, beaglebonewhite,
csb336, csb337, csb637,
edb7312,
diff --git a/config/rtems-bsps-epiphany.ini b/config/rtems-bsps-epiphany.ini
deleted file mode 100644
index c3ad7a0..0000000
--- a/config/rtems-bsps-epiphany.ini
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2017 Chris Johns (chrisj@rtems.org)
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-bsp-builder'.
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-#
-# Epiphany Architecture
-#
-[epiphany]
-bsps = epiphany_sim
-exclude = smp, network
diff --git a/config/rtems-bsps-powerpc.ini b/config/rtems-bsps-powerpc.ini
index 21dc6b9..b4dc02c 100644
--- a/config/rtems-bsps-powerpc.ini
+++ b/config/rtems-bsps-powerpc.ini
@@ -22,16 +22,15 @@
#
[powerpc]
bsps = beatnik,
- br_uid, brs5l, brs6l,
- dp2,
+ br_uid,
gwlcfm,
haleakala,
hsc_cm01,
icecube,
mcp750,
- mpc5566evb, mpc5566evb_spe, phycore_mpc5554,
- mpc5643l_dpu, mpc5643l_evb, mpc5668g,
- mpc5674f_ecu508_app, mpc5674f_ecu508_boot, mpc5674f_rsm6, mpc5674fevb, mpc5674fevb_spe,
+ mpc5566evb, phycore_mpc5554,
+ mpc5643l_evb, mpc5668g,
+ mpc5674fevb,
mpc8260ads,
mpc8309som,
mpc8313erdb,
@@ -48,16 +47,15 @@ bsps = beatnik,
tqm8xx_stk8xx,
virtex, virtex4, virtex5
exclude-smp = beatnik,
- br_uid, brs5l, brs6l,
- dp2,
+ br_uid,
gwlcfm,
haleakala,
hsc_cm01,
icecube,
mcp750,
- mpc5566evb, mpc5566evb_spe, phycore_mpc5554,
- mpc5643l_dpu, mpc5643l_evb, mpc5668g,
- mpc5674f_ecu508_app, mpc5674f_ecu508_boot, mpc5674f_rsm6, mpc5674fevb, mpc5674fevb_spe,
+ mpc5566evb, phycore_mpc5554,
+ mpc5643l_evb, mpc5668g,
+ mpc5674fevb,
mpc8260ads,
mpc8309som,
mpc8313erdb,
diff --git a/config/rtems-bsps-tiers.ini b/config/rtems-bsps-tiers.ini
index 64ad105..875c5df 100644
--- a/config/rtems-bsps-tiers.ini
+++ b/config/rtems-bsps-tiers.ini
@@ -31,13 +31,17 @@ bsps_powerpc = qoriq_e500
# simulators.
#
[tier-2]
+archs = mips, powerpc, sparc
+bsps_mips = jmr3904
+bsps_powerpc = psim
+bsps_sparc = erc32, leon2, leon3
#
# Tier 3: no build errors, no tests run.
#
[tier-3]
-archs = arm, bfin, i386, lm32, m68k, mips, moxie,
- nios2, or1k, powerpc, sh, sparc, sparc64, v850, x86_64
+archs = arm, bfin, i386, lm32, m68k, moxie,
+ nios2, or1k, sh, sparc64, v850, x86_64
bsps_arm = altcycv_devkit,
arm1136jfs, arm1136js, arm7tdmi, arm920, armcortexa9, atsamv,
beagleboardorig, beagleboardxm, beaglebonewhite,
@@ -78,21 +82,20 @@ bsps_m68k = av5282,
mrm332,
mvme147, mvme147s, mvme162, mvme162lx, mvme167,
uC5282
-bsps_mips = csb350, hurricane, jmr3904, malta, rbtx4925, rbtx4938
+bsps_mips = csb350, hurricane, malta, rbtx4925, rbtx4938
bsps_moxie = moxiesim
bsps_nios2 = nios2_iss
bsps_or1k = generic_or1k
bsps_powerpc = beatnik,
- br_uid, brs5l, brs6l,
- dp2,
+ br_uid,
gwlcfm,
haleakala,
hsc_cm01,
icecube,
mcp750,
- mpc5566evb, mpc5566evb_spe, phycore_mpc5554,
- mpc5643l_dpu, mpc5643l_evb, mpc5668g,
- mpc5674f_ecu508_app, mpc5674f_ecu508_boot, mpc5674f_rsm6, mpc5674fevb, mpc5674fevb_spe,
+ mpc5566evb, phycore_mpc5554,
+ mpc5643l_evb, mpc5668g,
+ mpc5674fevb,
mpc8260ads,
mpc8309som,
mpc8313erdb,
@@ -101,7 +104,6 @@ bsps_powerpc = beatnik,
mvme2100, mvme2307, mvme3100, mvme5500,
pghplus,
pm520_cr825, pm520_ze30,
- psim,
qemuppc, qemuprep, qemuprep-altivec,
qoriq_core_0, qoriq_core_1, qoriq_e6500_32, qoriq_e6500_64
ss555,
@@ -109,7 +111,7 @@ bsps_powerpc = beatnik,
tqm8xx_stk8xx,
virtex, virtex4, virtex5
bsps_sh = gensh1, gensh2, gensh4, simsh1, simsh2, simsh2e, simsh4
-bsps_sparc = erc32, leon2, at697f, gr712rc, ut699, ut700, leon3, gr740
+bsps_sparc = at697f, gr712rc, ut699, ut700, gr740
bsps_sparc64 = niagara, usiii
bsps_v850 = v850e1sim, v850e2sim, v850e2v3sim, v850esim, v850essim, v850sim
bsps_x86_64 = amd64
@@ -118,5 +120,4 @@ bsps_x86_64 = amd64
# Tier 4: nothing expected.
#
[tier-4]
-archs = epiphany
-bsps_epiphany = epiphany_sim
+archs =
diff --git a/config/rtems-bsps.ini b/config/rtems-bsps.ini
index bdbc7e2..93156fb 100644
--- a/config/rtems-bsps.ini
+++ b/config/rtems-bsps.ini
@@ -62,7 +62,6 @@ include = rtems-bsps-tiers.ini
[everything]
archs = arm,
bfin,
- epiphany,
i386,
lm32,
m68k,
@@ -77,7 +76,6 @@ archs = arm,
v850
bsps_arm = ${arm:bsps}
bsps_bfin = ${bfin:bsps}
-bsps_epiphany = ${epiphany:bsps}
bsps_i386 = ${i386:bsps}
bsps_lm32 = ${lm32:bsps}
bsps_m68k = ${m68k:bsps}
@@ -97,7 +95,6 @@ bsps_v850 = ${v850:bsps}
[architectures]
include = rtems-bsps-arm.ini,
rtems-bsps-bfin.ini,
- rtems-bsps-epiphany.ini,
rtems-bsps-i386.ini,
rtems-bsps-lm32.ini,
rtems-bsps-m68k.ini,
diff --git a/config/rtems-version.ini b/config/rtems-version.ini
index 98d0f0f..da74a45 100644
--- a/config/rtems-version.ini
+++ b/config/rtems-version.ini
@@ -5,4 +5,4 @@
#
[version]
-revision = 5.0.not_released
+revision = 6.0.not_released
diff --git a/linkers/rtems-exeinfo.cpp b/linkers/rtems-exeinfo.cpp
index eead6db..1e6d4b4 100644
--- a/linkers/rtems-exeinfo.cpp
+++ b/linkers/rtems-exeinfo.cpp
@@ -153,7 +153,7 @@ namespace rld
/*
* Check the compiler and flags match.
*/
- void output_compilation_unit (bool objects);
+ void output_compilation_unit (bool objects, bool full_flags);
/*
* Output the sections.
@@ -176,6 +176,16 @@ namespace rld
void output_init_fini (const char* label, const char** names);
/*
+ * Output the configuration.
+ */
+ void output_config ();
+
+ /*
+ * Output the TLS data.
+ */
+ void output_tls ();
+
+ /*
* Output the inlined functions.
*/
void output_inlined ();
@@ -184,6 +194,10 @@ namespace rld
* Output the DWARF data.
*/
void output_dwarf ();
+
+ private:
+
+ void config (const std::string name);
};
section::section (const files::section& sec, files::byteorder byteorder)
@@ -304,6 +318,7 @@ namespace rld
exe.load_symbols (symbols, true);
debug.load_debug ();
debug.load_types ();
+ debug.load_variables ();
debug.load_functions ();
symbols.globals (addresses);
symbols.weaks (addresses);
@@ -316,7 +331,7 @@ namespace rld
}
void
- image::output_compilation_unit (bool objects)
+ image::output_compilation_unit (bool objects, bool full_flags)
{
dwarf::compilation_units& cus = debug.get_cus ();
@@ -469,12 +484,15 @@ namespace rld
for (auto& f : s.flags)
{
bool present = false;
- for (auto& ff : filter_flags)
+ if (!full_flags)
{
- if (rld::starts_with(f, ff))
+ for (auto& ff : filter_flags)
{
- present = true;
- break;
+ if (rld::starts_with(f, ff))
+ {
+ present = true;
+ break;
+ }
}
}
if (!present)
@@ -612,6 +630,112 @@ namespace rld
std::cout << std::endl;
}
+ void image::output_tls ()
+ {
+ symbols::symbol* tls_data_begin = symbols.find_global("_TLS_Data_begin");
+ symbols::symbol* tls_data_end = symbols.find_global("_TLS_Data_end");
+ symbols::symbol* tls_data_size = symbols.find_global("_TLS_Data_size");
+ symbols::symbol* tls_bss_begin = symbols.find_global("_TLS_BSS_begin");
+ symbols::symbol* tls_bss_end = symbols.find_global("_TLS_BSS_end");
+ symbols::symbol* tls_bss_size = symbols.find_global("_TLS_BSS_size");
+ symbols::symbol* tls_size = symbols.find_global("_TLS_Size");
+ symbols::symbol* tls_alignment = symbols.find_global("_TLS_Alignment");
+ symbols::symbol* tls_max_size = symbols.find_global("_Thread_Maximum_TLS_size");
+
+ if (tls_data_begin == nullptr ||
+ tls_data_end == nullptr ||
+ tls_data_size == nullptr ||
+ tls_bss_begin == nullptr ||
+ tls_bss_end == nullptr ||
+ tls_bss_size == nullptr ||
+ tls_size == nullptr ||
+ tls_alignment == nullptr)
+ {
+ if (tls_data_begin == nullptr &&
+ tls_data_end == nullptr &&
+ tls_data_size == nullptr &&
+ tls_bss_begin == nullptr &&
+ tls_bss_end == nullptr &&
+ tls_bss_size == nullptr &&
+ tls_size == nullptr &&
+ tls_alignment == nullptr)
+ {
+ std::cout << "No TLS data found" << std::endl;
+ return;
+ }
+ std::cout << "TLS environment is INVALID (please report):" << std::endl
+ << " _TLS_Data_begin : "
+ << (char*) (tls_data_begin == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_Data_end : "
+ << (char*) (tls_data_end == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_Data_size : "
+ << (char*) (tls_data_size == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_BSS_begin : "
+ << (char*) (tls_bss_begin == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_BSS_end : "
+ << (char*) (tls_bss_end == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_BSS_Size : "
+ << (char*) (tls_bss_size == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_Size : "
+ << (char*) (tls_size == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _TLS_Alignment : "
+ << (char*) (tls_alignment == nullptr ? "not-found" : "found")
+ << std::endl
+ << " _Thread_Maximum_TLS_size : "
+ << (char*) (tls_max_size == nullptr ? "not-found" : "found")
+ << std::endl
+ << std::endl;
+ return;
+ }
+
+ std::cout << "TLS size : " << tls_size->value () << std::endl
+ << " max size : ";
+ if (tls_max_size == nullptr)
+ std::cout << "not found" << std::endl;
+ else
+ std::cout << tls_max_size->value () << std::endl;
+ std::cout << " data size : " << tls_data_size->value () << std::endl
+ << " bss size : " << tls_bss_size->value () << std::endl
+ << " alignment : " << tls_alignment->value () << std::endl
+ << std::right << std::hex << std::setfill ('0')
+ << " data addr : 0x" << std::setw (8) << tls_data_begin->value ()
+ << std::endl
+ << std::dec << std::setfill (' ')
+ << std::endl;
+ }
+
+ void image::config(const std::string name)
+ {
+ std::string table_name = "_" + name + "_Information";
+ symbols::symbol* table = symbols.find_global(table_name);
+
+ if (table != nullptr)
+ std::cout << " " << name << std::endl;
+ }
+
+ void image::output_config()
+ {
+ std::cout << "Configurations:" << std::endl;
+ config("Thread");
+ config("Barrier");
+ config("Extension");
+ config("Message_queue");
+ config("Partition");
+ config("Rate_monotonic");
+ config("Dual_ported_memory");
+ config("Region");
+ config("Semaphore");
+ config("Timer");
+ config("RTEMS_tasks");
+ }
+
struct func_count
{
std::string name;
@@ -758,6 +882,9 @@ static struct option rld_opts[] = {
{ "init", no_argument, NULL, 'I' },
{ "fini", no_argument, NULL, 'F' },
{ "objects", no_argument, NULL, 'O' },
+ { "full-flags", no_argument, NULL, 'A' },
+ { "config", no_argument, NULL, 'C' },
+ { "tls", no_argument, NULL, 'T' },
{ "inlined", no_argument, NULL, 'i' },
{ "dwarf", no_argument, NULL, 'D' },
{ NULL, 0, NULL, 0 }
@@ -778,6 +905,9 @@ usage (int exit_code)
<< " -I : show init section tables (also --init)" << std::endl
<< " -F : show fini section tables (also --fini)" << std::endl
<< " -O : show object files (also --objects)" << std::endl
+ << " : add --full-flags for compiler options" << std::endl
+ << " -C : show configuration (also --config)" << std::endl
+ << " -T : show thread local storage data (also --tls)" << std::endl
<< " -i : show inlined code (also --inlined)" << std::endl
<< " -D : dump the DWARF data (also --dwarf)" << std::endl;
::exit (exit_code);
@@ -842,6 +972,9 @@ main (int argc, char* argv[])
bool init = false;
bool fini = false;
bool objects = false;
+ bool full_flags = false;
+ bool config = false;
+ bool tls = false;
bool inlined = false;
bool dwarf_data = false;
@@ -849,7 +982,7 @@ main (int argc, char* argv[])
while (true)
{
- int opt = ::getopt_long (argc, argv, "hvVMaSIFOiD", rld_opts, NULL);
+ int opt = ::getopt_long (argc, argv, "hvVMaSIFOCTiD", rld_opts, NULL);
if (opt < 0)
break;
@@ -890,6 +1023,18 @@ main (int argc, char* argv[])
objects = true;
break;
+ case 'A':
+ full_flags = true;
+ break;
+
+ case 'C':
+ config = true;
+ break;
+
+ case 'T':
+ tls = true;
+ break;
+
case 'i':
inlined = true;
break;
@@ -928,6 +1073,8 @@ main (int argc, char* argv[])
init = true;
fini = true;
objects = true;
+ config = true;
+ tls = true;
inlined = true;
}
@@ -958,13 +1105,17 @@ main (int argc, char* argv[])
/*
* Generate the output.
*/
- exe.output_compilation_unit (objects);
+ exe.output_compilation_unit (objects, full_flags);
if (sections)
exe.output_sections ();
if (init)
exe.output_init ();
if (fini)
exe.output_fini ();
+ if (config)
+ exe.output_config ();
+ if (tls)
+ exe.output_tls ();
if (inlined)
exe.output_inlined ();
if (dwarf_data)
diff --git a/linkers/rtems-score-object.ini b/linkers/rtems-score-object.ini
index 1e98a71..521398e 100644
--- a/linkers/rtems-score-object.ini
+++ b/linkers/rtems-score-object.ini
@@ -2,16 +2,16 @@
; RTEMS Supercore Objects Trace Configurations
;
[rtems-score-object-all]
-trace = _Objects_Do_initialize_information, _Objects_Extend_information
-trace = _Objects_Shrink_information, _Objects_Allocate_unprotected
-trace = _Objects_Allocate, _Objects_Free, _Objects_Get
+trace = _Objects_Initialize_information, _Objects_Extend_information
+trace = _Objects_Shrink_information
+trace = _Objects_Allocate, _Objects_Get
trace = _Objects_Get_no_protection, _Objects_Get_next, _Objects_Get_information
trace = _Objects_Get_information_id, _Objects_Get_name_as_string, _Objects_Set_name
-trace = _Objects_Namespace_remove, _Objects_Close, _Objects_Active_count
+trace = _Objects_Close, _Objects_Active_count
[rtems-score-object-alloc]
-trace = _Objects_Do_initialize_information, _Objects_Allocate_unprotected
-trace = _Objects_Allocate, _Objects_Free, _Objects_Close
+trace = _Objects_Initialize_information
+trace = _Objects_Allocate, _Objects_Close
[rtems-score-object-get]
trace = _Objects_Get, _Objects_Get_no_protection
@@ -19,12 +19,10 @@ trace = _Objects_Get_next, _Objects_Get_information, _Objects_Get_information_id
trace = _Objects_Get_name_as_string
[rtems-score-object-signatures]
-_Objects_Do_initialize_information = void, Objects_Information*, Objects_APIs, uint16_t, uint32_t, uint16_t, bool, uint32_t
-_Objects_Extend_information = void, Objects_Information*
+_Objects_Initialize_information = void, Objects_Information*
+_Objects_Extend_information = Objects_Maximum, Objects_Information*
_Objects_Shrink_information = void, Objects_Information*
-_Objects_Allocate_unprotected = Objects_Control*, Objects_Information*
_Objects_Allocate = Objects_Control*, Objects_Information*
-_Objects_Free = void, Objects_Information*, Objects_Control*
_Objects_Get = Objects_Control*, Objects_Id, ISR_lock_Context *, const Objects_Information*
_Objects_Get_no_protection = Objects_Control*, Objects_Id, const Objects_Information*
_Objects_Get_next = Objects_Control*, Objects_Id, const Objects_Information*, Objects_Id*
@@ -32,6 +30,5 @@ _Objects_Get_information = Objects_Information*, Objects_APIs, uint16_t
_Objects_Get_information_id = Objects_Information*, Objects_Id
_Objects_Get_name_as_string = char*, Objects_Id, size_t, char*
_Objects_Set_name = bool, const Objects_Information*, Objects_Control*, const char*
-_Objects_Namespace_remove = void, Objects_Information*, Objects_Control*
_Objects_Close = void, const Objects_Information*, Objects_Control*
_Objects_Active_count = Objects_Maximum, const Objects_Information*
diff --git a/linkers/rtems-score-thread.ini b/linkers/rtems-score-thread.ini
index 313831a..3aed2f1 100644
--- a/linkers/rtems-score-thread.ini
+++ b/linkers/rtems-score-thread.ini
@@ -3,9 +3,9 @@
;
[rtems-score-thread-all]
trace = _Thread_Handler_initialization, _Thread_Create_idle, _Thread_Start_multitasking
-trace = _Thread_Stack_Allocate, _Thread_Stack_Free, _Thread_Initialize, _Thread_Start
+trace = _Stack_Allocate, _Stack_Free, _Thread_Initialize, _Thread_Start
trace = _Thread_Restart_other, _Thread_Restart_self, _Thread_Yield, _Thread_Set_life_protection
-trace = _Thread_Life_action_handler, _Thread_Kill_zombies, _Thread_Close
+trace = _Thread_Kill_zombies, _Thread_Close
trace = _Thread_Clear_state, _Thread_Set_state, _Thread_Load_environment
trace = _Thread_Handler
trace = _Thread_Get
@@ -15,7 +15,7 @@ traces = rtems-score-thread-create, rtems-score-thread-destroy
[rtems-score-thread-create]
trace = _Thread_Handler_initialization, _Thread_Create_idle
-trace = _Thread_Stack_Allocate, _Thread_Initialize, _Thread_Start
+trace = _Stack_Allocate, _Thread_Initialize, _Thread_Start
trace = _Thread_Restart_other, _Thread_Restart_self, _Thread_Handler
[rtems-score-thread-destroy]
@@ -23,7 +23,7 @@ trace = _Thread_Kill_zombies, _Thread_Close
[rtems-score-thread-activity]
trace = _Thread_Restart_other, _Thread_Restart_self, _Thread_Yield, _Thread_Set_life_protection
-trace = _Thread_Life_action_handler, _Thread_Clear_state,
+trace = _Thread_Clear_state,
trace = _Thread_Set_state, _Thread_Load_environment
trace = _Thread_Get
trace = _Thread_Priority_update
@@ -32,15 +32,14 @@ trace = _Thread_Priority_update
_Thread_Handler_initialization = void, void
_Thread_Create_idle = void, void
_Thread_Start_multitasking = void, void
-_Thread_Stack_Allocate = size_t, Thread_Control*, size_t
-_Thread_Stack_Free = void, Thread_Control*
-_Thread_Initialize = bool, Thread_Information *, Thread_Control*, const Scheduler_Control*, void*, size_t, bool, Priority_Control, bool, Thread_CPU_budget_algorithms, Thread_CPU_budget_algorithm_callout, uint32_t, Objects_Name
+_Stack_Allocate = void*, size_t
+_Stack_Free = void, void*
+_Thread_Initialize = bool, Thread_Information *, Thread_Control*, const Thread_Configuration *
_Thread_Start = bool, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
_Thread_Restart_other = bool, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
_Thread_Restart_self = void, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
_Thread_Yield = void, Thread_Control*
_Thread_Set_life_protection = Thread_Life_state, Thread_Life_state
-_Thread_Life_action_handler = void, Thread_Control*, Thread_Action*, ISR_lock_Context*
_Thread_Kill_zombies = void, void
_Thread_Close = void, Thread_Control*, Thread_Control*, Thread_Close_context*
_Thread_Clear_state = States_Control, Thread_Control*, States_Control
diff --git a/linkers/rtems-syms.cpp b/linkers/rtems-syms.cpp
index 5ebdceb..bfe2a48 100644
--- a/linkers/rtems-syms.cpp
+++ b/linkers/rtems-syms.cpp
@@ -244,13 +244,21 @@ output_sym::operator ()(const rld::symbols::symtab::value_type& value)
if (embed)
{
+ c.write_line ("#if __riscv_xlen == 64");
+ c.write_line ("asm(\" .quad " + sym.name () + "\");");
+ c.write_line ("#else");
c.write_line ("asm(\" .long " + sym.name () + "\");");
+ c.write_line ("#endif");
}
else
{
std::stringstream oss;
oss << std::hex << std::setfill ('0') << std::setw (8) << sym.value ();
+ c.write_line ("#if __riscv_xlen == 64");
+ c.write_line ("asm(\" .quad 0x" + oss.str () + "\");");
+ c.write_line ("#else");
c.write_line ("asm(\" .long 0x" + oss.str () + "\");");
+ c.write_line ("#endif");
}
}
diff --git a/linkers/rtld-print.ini b/linkers/rtld-print.ini
index b9b524f..9f7f578 100644
--- a/linkers/rtld-print.ini
+++ b/linkers/rtld-print.ini
@@ -91,4 +91,4 @@ static inline void rtld_pg_printk_ret(const char* ret_type,
CODE
[printk-generator-headers]
-header = "#include <stdio.h>"
+header = "#include <rtems/bspIo.h>"
diff --git a/linkers/wscript b/linkers/wscript
index 23d3f9c..8591d60 100644
--- a/linkers/wscript
+++ b/linkers/wscript
@@ -33,7 +33,6 @@ def options(opt):
def configure(conf):
conf.load('compiler_c')
conf.load('compiler_cxx')
-
conf.write_config_header('config.h')
def build(bld):
diff --git a/tester/rtems/testing/bsps/arm1136js-run.ini b/misc/__init__.py
index 995d71d..6ff279c 100644
--- a/tester/rtems/testing/bsps/arm1136js-run.ini
+++ b/misc/__init__.py
@@ -1,6 +1,5 @@
-#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
+# Copyright 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,12 +27,4 @@
# POSSIBILITY OF SUCH DAMAGE.
#
-#
-# The arm1136js BSP
-#
-[arm1136js-run]
-bsp = arm1136js
-arch = arm
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
-bsp_run_opts = -a -nouartrx
+all = []
diff --git a/misc/bin2c/rtems-bin2c.c b/misc/bin2c/rtems-bin2c.c
index 1e59ac5..7ed79dd 100644
--- a/misc/bin2c/rtems-bin2c.c
+++ b/misc/bin2c/rtems-bin2c.c
@@ -42,6 +42,15 @@ int zeroterminated = 0;
int createC = 1;
int createH = 1;
+static void sanitize_file_name(char *p)
+{
+ while (*p != '\0') {
+ if (!isalnum((unsigned char)*p)) /* cast to avoid negative indexing */
+ *p = '_';
+ ++p;
+ }
+}
+
int myfgetc(FILE *f)
{
int c = fgetc(f);
@@ -55,7 +64,7 @@ int myfgetc(FILE *f)
void process(const char *ifname, const char *ofname, const char *forced_name)
{
FILE *ifile, *ocfile, *ohfile;
- char buf[PATH_MAX+1], *p;
+ char buf[PATH_MAX+1];
char obasename[PATH_MAX+1];
char ocname[PATH_MAX+5];
char ohname[PATH_MAX+5];
@@ -132,10 +141,7 @@ void process(const char *ifname, const char *ofname, const char *forced_name)
ifbasename = basename(ifbasename_to_free);
strcpy(buf, ifbasename);
- for (p = buf; *p != '\0'; ++p) {
- if (!isalnum((unsigned char)*p)) /* cast to avoid negative indexing */
- *p = '_';
- }
+ sanitize_file_name(buf);
if ( createC ) {
/* print C file header */
@@ -191,18 +197,12 @@ void process(const char *ifname, const char *ofname, const char *forced_name)
if ( createH ) {
/* print H file header */
char hbasename[PATH_MAX];
- char* p;
/* Clean up the file name if it is an abs path */
strcpy(
hbasename,
obasename
);
- p = hbasename;
- while (*p != '\0') {
- if (*p < '0' || *p > 'z')
- *p = '_';
- ++p;
- }
+ sanitize_file_name(hbasename);
fprintf(
ohfile,
"/*\n"
diff --git a/misc/rtems-boot-image b/misc/rtems-boot-image
index aa23b2e..7f7ac0d 100755
--- a/misc/rtems-boot-image
+++ b/misc/rtems-boot-image
@@ -1,7 +1,7 @@
-#! /bin/sh
+#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2019 Chris Johns (chrisj@rtems.org)
+# Copyright 2019, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,15 +28,18 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
-set -e
-base=$(dirname $(dirname $0))
-cmd=misc/tools/cmd-boot-image.py
-PYTHON_WRAPPER=rtemstoolkit/python-wrapper.sh
-if test -f ${base}/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/${cmd}
- . ${base}/${PYTHON_WRAPPER}
-elif test -f ${base}/share/rtems/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/share/rtems/${cmd}
- . ${base}/share/rtems/${PYTHON_WRAPPER}
-fi
-echo "error: RTEMS Toolkit python wrapper not found, please report"
+
+from __future__ import print_function
+
+import sys, os
+
+base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+rtems = os.path.join(base, 'share', 'rtems')
+sys.path = sys.path[0:1] + [rtems, base] + sys.path[1:]
+
+try:
+ import misc.tools.boot
+ misc.tools.boot.run()
+except ImportError:
+ print("Incorrect RTEMS Tools installation", file = sys.stderr)
+ sys.exit(1)
diff --git a/tester/rt/cmd-test.py b/misc/rtems-tftp-proxy
index 93e480f..2125662 100755
--- a/tester/rt/cmd-test.py
+++ b/misc/rtems-tftp-proxy
@@ -1,7 +1,7 @@
#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013, 2015 Chris Johns (chrisj@rtems.org)
+# Copyright 2019, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -34,12 +34,12 @@ from __future__ import print_function
import sys, os
base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
-rtems = os.path.dirname(base)
-sys.path = [rtems] + sys.path
+rtems = os.path.join(base, 'share', 'rtems')
+sys.path = sys.path[0:1] + [rtems, base] + sys.path[1:]
try:
- import test
- test.run(sys.argv[1:], command_path = base)
+ import misc.tools.tftpproxy
+ misc.tools.tftpproxy.run(sys.argv)
except ImportError:
print("Incorrect RTEMS Tools installation", file = sys.stderr)
sys.exit(1)
diff --git a/misc/tools/__init__.py b/misc/tools/__init__.py
new file mode 100644
index 0000000..6ad687a
--- /dev/null
+++ b/misc/tools/__init__.py
@@ -0,0 +1,30 @@
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2013-2014 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+all = ['boot']
diff --git a/misc/tools/boot.py b/misc/tools/boot.py
index 265d1b3..6e8065a 100644
--- a/misc/tools/boot.py
+++ b/misc/tools/boot.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2019 Chris Johns (chrisj@rtems.org)
+# Copyright 2019, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
diff --git a/misc/tools/cmd-boot-image.py b/misc/tools/cmd-boot-image.py
deleted file mode 100755
index f17a91e..0000000
--- a/misc/tools/cmd-boot-image.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2019 Chris Johns (chrisj@rtems.org)
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-from __future__ import print_function
-
-import sys, os
-
-base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
-rtems = os.path.dirname(base)
-sys.path = [rtems] + sys.path
-
-try:
- import boot
- boot.run(sys.argv[1:], command_path = base)
-except ImportError:
- print("Incorrect RTEMS Tools installation", file = sys.stderr)
- sys.exit(1)
diff --git a/tester/rt/tftpy/COPYING b/misc/tools/getmac/LICENSE
index c9f2c9c..1a71c0f 100644
--- a/tester/rt/tftpy/COPYING
+++ b/misc/tools/getmac/LICENSE
@@ -1,6 +1,6 @@
-The MIT License
+MIT License
-Copyright (c) 2009 Michael P. Soulier
+Copyright (c) 2019 Christopher Goes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/misc/tools/getmac/__init__.py b/misc/tools/getmac/__init__.py
new file mode 100644
index 0000000..fec68bc
--- /dev/null
+++ b/misc/tools/getmac/__init__.py
@@ -0,0 +1,2 @@
+from .getmac import __version__, get_mac_address
+__all__ = ['get_mac_address']
diff --git a/misc/tools/getmac/__main__.py b/misc/tools/getmac/__main__.py
new file mode 100644
index 0000000..5fbe0f7
--- /dev/null
+++ b/misc/tools/getmac/__main__.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function
+
+import argparse
+import logging
+import sys
+
+from . import getmac
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ 'getmac', description='Get the MAC address of system network '
+ 'interfaces or remote hosts on the LAN')
+ parser.add_argument(
+ '--version', action='version',
+ version='getmac %s' % getmac.__version__)
+ parser.add_argument(
+ '-v', '--verbose', action='store_true',
+ help='Enable output messages')
+ parser.add_argument(
+ '-d', '--debug', action='count',
+ help='Enable debugging output. Add characters to '
+ 'increase verbosity of output, e.g. \'-dd\'.')
+ parser.add_argument(
+ '-N', '--no-net', '--no-network-requests',
+ action='store_true', dest='NO_NET',
+ help='Do not send a UDP packet to refresh the ARP table')
+
+ group = parser.add_mutually_exclusive_group(required=False)
+ group.add_argument(
+ '-i', '--interface', type=str, default=None,
+ help='Name of a network interface on the system')
+ group.add_argument(
+ '-4', '--ip', type=str, default=None,
+ help='IPv4 address of a remote host')
+ group.add_argument(
+ '-6', '--ip6', type=str, default=None,
+ help='IPv6 address of a remote host')
+ group.add_argument(
+ '-n', '--hostname', type=str, default=None,
+ help='Hostname of a remote host')
+
+ args = parser.parse_args()
+
+ if args.debug or args.verbose:
+ logging.basicConfig(format='%(levelname)-8s %(message)s',
+ level=logging.DEBUG, stream=sys.stderr)
+ if args.debug:
+ getmac.DEBUG = args.debug
+
+ mac = getmac.get_mac_address(
+ interface=args.interface, ip=args.ip,
+ ip6=args.ip6, hostname=args.hostname,
+ network_request=not args.NO_NET)
+
+ if mac is not None:
+ print(mac) # noqa: T001
+ sys.exit(0) # Exit success!
+ else:
+ sys.exit(1) # Exit with error since it failed to find a MAC
+
+
+if __name__ == '__main__':
+ main()
diff --git a/misc/tools/getmac/getmac.py b/misc/tools/getmac/getmac.py
new file mode 100644
index 0000000..d5555fb
--- /dev/null
+++ b/misc/tools/getmac/getmac.py
@@ -0,0 +1,603 @@
+# -*- coding: utf-8 -*-
+# http://multivax.com/last_question.html
+
+"""Get the MAC address of remote hosts or network interfaces.
+
+It provides a platform-independent interface to get the MAC addresses of:
+
+* System network interfaces (by interface name)
+* Remote hosts on the local network (by IPv4/IPv6 address or hostname)
+
+It provides one function: `get_mac_address()`
+
+Examples:
+
+ from getmac import get_mac_address
+ eth_mac = get_mac_address(interface="eth0")
+ win_mac = get_mac_address(interface="Ethernet 3")
+ ip_mac = get_mac_address(ip="192.168.0.1")
+ ip6_mac = get_mac_address(ip6="::1")
+ host_mac = get_mac_address(hostname="localhost")
+ updated_mac = get_mac_address(ip="10.0.0.1", network_request=True)
+
+"""
+
+import ctypes
+import logging
+import os
+import platform
+import re
+import shlex
+import socket
+import struct
+import sys
+import traceback
+from subprocess import check_output
+
+try: # Python 3
+ from subprocess import DEVNULL # type: ignore
+except ImportError: # Python 2
+ DEVNULL = open(os.devnull, 'wb') # type: ignore
+
+# Configure logging
+log = logging.getLogger('getmac')
+log.addHandler(logging.NullHandler())
+
+__version__ = '0.8.1'
+PY2 = sys.version_info[0] == 2
+
+# Configurable settings
+DEBUG = 0
+PORT = 55555
+
+# Platform identifiers
+_SYST = platform.system()
+if _SYST == 'Java':
+ try:
+ import java.lang
+ _SYST = str(java.lang.System.getProperty("os.name"))
+ except ImportError:
+ log.critical("Can't determine OS: couldn't import java.lang on Jython")
+WINDOWS = _SYST == 'Windows'
+DARWIN = _SYST == 'Darwin'
+OPENBSD = _SYST == 'OpenBSD'
+FREEBSD = _SYST == 'FreeBSD'
+BSD = OPENBSD or FREEBSD # Not including Darwin for now
+WSL = False # Windows Subsystem for Linux (WSL)
+LINUX = False
+if _SYST == 'Linux':
+ if 'Microsoft' in platform.version():
+ WSL = True
+ else:
+ LINUX = True
+
+PATH = os.environ.get('PATH', os.defpath).split(os.pathsep)
+if not WINDOWS:
+ PATH.extend(('/sbin', '/usr/sbin'))
+
+# Use a copy of the environment so we don't
+# modify the process's current environment.
+ENV = dict(os.environ)
+ENV['LC_ALL'] = 'C' # Ensure ASCII output so we parse correctly
+
+# Constants
+IP4 = 0
+IP6 = 1
+INTERFACE = 2
+HOSTNAME = 3
+
+MAC_RE_COLON = r'([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})'
+MAC_RE_DASH = r'([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})'
+MAC_RE_DARWIN = r'([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})'
+
+# Used for mypy (a data type analysis tool)
+# If you're copying the code, this section can be safely removed
+try:
+ from typing import TYPE_CHECKING
+ if TYPE_CHECKING:
+ from typing import Optional
+except ImportError:
+ pass
+
+
+def get_mac_address(
+ interface=None, ip=None, ip6=None,
+ hostname=None, network_request=True
+):
+ # type: (Optional[str], Optional[str], Optional[str], Optional[str], bool) -> Optional[str]
+ """Get a Unicast IEEE 802 MAC-48 address from a local interface or remote host.
+
+ You must only use one of the first four arguments. If none of the arguments
+ are selected, the default network interface for the system will be used.
+
+ Exceptions will be handled silently and returned as a None.
+ For the time being, it assumes you are using Ethernet.
+
+ NOTES:
+ * You MUST provide str-typed arguments, REGARDLESS of Python version.
+ * localhost/127.0.0.1 will always return '00:00:00:00:00:00'
+
+ Args:
+ interface (str): Name of a local network interface (e.g "Ethernet 3", "eth0", "ens32")
+ ip (str): Canonical dotted decimal IPv4 address of a remote host (e.g 192.168.0.1)
+ ip6 (str): Canonical shortened IPv6 address of a remote host (e.g ff02::1:ffe7:7f19)
+ hostname (str): DNS hostname of a remote host (e.g "router1.mycorp.com", "localhost")
+ network_request (bool): Send a UDP packet to a remote host to populate
+ the ARP/NDP tables for IPv4/IPv6. The port this packet is sent to can
+ be configured using the module variable `getmac.PORT`.
+ Returns:
+ Lowercase colon-separated MAC address, or None if one could not be
+ found or there was an error.
+ """
+ if (hostname and hostname == 'localhost') or (ip and ip == '127.0.0.1'):
+ return '00:00:00:00:00:00'
+
+ # Resolve hostname to an IP address
+ if hostname:
+ ip = socket.gethostbyname(hostname)
+
+ # Populate the ARP table by sending a empty UDP packet to a high port
+ if network_request and (ip or ip6):
+ if ip:
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ else:
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ try:
+ if ip:
+ s.sendto(b'', (ip, PORT))
+ else:
+ s.sendto(b'', (ip6, PORT))
+ except Exception:
+ log.error("Failed to send ARP table population packet")
+ if DEBUG:
+ log.debug(traceback.format_exc())
+ finally:
+ s.close()
+
+ # Setup the address hunt based on the arguments specified
+ if ip6:
+ if not socket.has_ipv6:
+ log.error("Cannot get the MAC address of a IPv6 host: "
+ "IPv6 is not supported on this system")
+ return None
+ elif ':' not in ip6:
+ log.error("Invalid IPv6 address: %s", ip6)
+ return None
+ to_find = ip6
+ typ = IP6
+ elif ip:
+ to_find = ip
+ typ = IP4
+ else: # Default to searching for interface
+ typ = INTERFACE
+ if interface:
+ to_find = interface
+ else:
+ # Default to finding MAC of the interface with the default route
+ if WINDOWS and network_request:
+ to_find = _fetch_ip_using_dns()
+ typ = IP4
+ elif WINDOWS:
+ to_find = 'Ethernet'
+ elif BSD:
+ if OPENBSD:
+ to_find = _get_default_iface_openbsd() # type: ignore
+ else:
+ to_find = _get_default_iface_freebsd() # type: ignore
+ if not to_find:
+ to_find = 'em0'
+ else:
+ to_find = _hunt_linux_default_iface() # type: ignore
+ if not to_find:
+ to_find = 'en0'
+
+ mac = _hunt_for_mac(to_find, typ, network_request)
+ log.debug("Raw MAC found: %s", mac)
+
+ # Check and format the result to be lowercase, colon-separated
+ if mac is not None:
+ mac = str(mac)
+ if not PY2: # Strip bytestring conversion artifacts
+ mac = mac.replace("b'", '').replace("'", '')\
+ .replace('\\n', '').replace('\\r', '')
+ mac = mac.strip().lower().replace(' ', '').replace('-', ':')
+
+ # Fix cases where there are no colons
+ if ':' not in mac and len(mac) == 12:
+ log.debug("Adding colons to MAC %s", mac)
+ mac = ':'.join(mac[i:i + 2] for i in range(0, len(mac), 2))
+
+ # Pad single-character octets with a leading zero (e.g Darwin's ARP output)
+ elif len(mac) < 17:
+ log.debug("Length of MAC %s is %d, padding single-character "
+ "octets with zeros", mac, len(mac))
+ parts = mac.split(':')
+ new_mac = []
+ for part in parts:
+ if len(part) == 1:
+ new_mac.append('0' + part)
+ else:
+ new_mac.append(part)
+ mac = ':'.join(new_mac)
+
+ # MAC address should ALWAYS be 17 characters before being returned
+ if len(mac) != 17:
+ log.warning("MAC address %s is not 17 characters long!", mac)
+ mac = None
+ elif mac.count(':') != 5:
+ log.warning("MAC address %s is missing ':' characters", mac)
+ mac = None
+ return mac
+
+
+def _search(regex, text, group_index=0):
+ # type: (str, str, int) -> Optional[str]
+ match = re.search(regex, text)
+ if match:
+ return match.groups()[group_index]
+ return None
+
+
+def _popen(command, args):
+ # type: (str, str) -> str
+ for directory in PATH:
+ executable = os.path.join(directory, command)
+ if (os.path.exists(executable)
+ and os.access(executable, os.F_OK | os.X_OK)
+ and not os.path.isdir(executable)):
+ break
+ else:
+ executable = command
+ if DEBUG >= 3:
+ log.debug("Running: '%s %s'", executable, args)
+ return _call_proc(executable, args)
+
+
+def _call_proc(executable, args):
+ # type: (str, str) -> str
+ if WINDOWS:
+ cmd = executable + ' ' + args # type: ignore
+ else:
+ cmd = [executable] + shlex.split(args) # type: ignore
+ output = check_output(cmd, stderr=DEVNULL, env=ENV)
+ if DEBUG >= 4:
+ log.debug("Output from '%s' command: %s", executable, str(output))
+ if not PY2 and isinstance(output, bytes):
+ return str(output, 'utf-8')
+ else:
+ return str(output)
+
+
+def _windows_ctypes_host(host):
+ # type: (str) -> Optional[str]
+ if not PY2: # Convert to bytes on Python 3+ (Fixes GitHub issue #7)
+ host = host.encode() # type: ignore
+ try:
+ inetaddr = ctypes.windll.wsock32.inet_addr(host) # type: ignore
+ if inetaddr in (0, -1):
+ raise Exception
+ except Exception:
+ hostip = socket.gethostbyname(host)
+ inetaddr = ctypes.windll.wsock32.inet_addr(hostip) # type: ignore
+
+ buffer = ctypes.c_buffer(6)
+ addlen = ctypes.c_ulong(ctypes.sizeof(buffer))
+
+ send_arp = ctypes.windll.Iphlpapi.SendARP # type: ignore
+ if send_arp(inetaddr, 0, ctypes.byref(buffer), ctypes.byref(addlen)) != 0:
+ return None
+
+ # Convert binary data into a string.
+ macaddr = ''
+ for intval in struct.unpack('BBBBBB', buffer): # type: ignore
+ if intval > 15:
+ replacestr = '0x'
+ else:
+ replacestr = 'x'
+ macaddr = ''.join([macaddr, hex(intval).replace(replacestr, '')])
+ return macaddr
+
+
+def _fcntl_iface(iface):
+ # type: (str) -> str
+ import fcntl
+ if not PY2:
+ iface = iface.encode() # type: ignore
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ # 0x8927 = SIOCGIFADDR
+ info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15]))
+ if PY2:
+ return ':'.join(['%02x' % ord(char) for char in info[18:24]])
+ else:
+ return ':'.join(['%02x' % ord(chr(char)) for char in info[18:24]])
+
+
+def _uuid_ip(ip):
+ # type: (str) -> Optional[str]
+ from uuid import _arp_getnode # type: ignore
+ backup = socket.gethostbyname
+ try:
+ socket.gethostbyname = lambda x: ip
+ mac1 = _arp_getnode()
+ if mac1 is not None:
+ mac1 = _uuid_convert(mac1)
+ mac2 = _arp_getnode()
+ mac2 = _uuid_convert(mac2)
+ if mac1 == mac2:
+ return mac1
+ except Exception:
+ raise
+ finally:
+ socket.gethostbyname = backup
+ return None
+
+
+def _uuid_lanscan_iface(iface):
+ # type: (str) -> Optional[str]
+ from uuid import _find_mac # type: ignore
+ if not PY2:
+ iface = bytes(iface, 'utf-8') # type: ignore
+ mac = _find_mac('lanscan', '-ai', [iface], lambda i: 0)
+ if mac:
+ return _uuid_convert(mac)
+ return None
+
+
+def _uuid_convert(mac):
+ # type: (int) -> str
+ return ':'.join(('%012X' % mac)[i:i+2] for i in range(0, 12, 2))
+
+
+def _read_sys_iface_file(iface):
+ # type: (str) -> Optional[str]
+ data = _read_file('/sys/class/net/' + iface + '/address')
+ # Sometimes this can be empty or a single newline character
+ return None if data is not None and len(data) < 17 else data
+
+
+def _read_arp_file(host):
+ # type: (str) -> Optional[str]
+ data = _read_file('/proc/net/arp')
+ if data is not None and len(data) > 1:
+ # Need a space, otherwise a search for 192.168.16.2
+ # will match 192.168.16.254 if it comes first!
+ return _search(re.escape(host) + r' .+' + MAC_RE_COLON, data)
+ return None
+
+
+def _read_file(filepath):
+ # type: (str) -> Optional[str]
+ try:
+ with open(filepath) as f:
+ return f.read()
+ except (OSError, IOError): # This is IOError on Python 2.7
+ log.debug("Could not find file: '%s'", filepath)
+ return None
+
+
+def _hunt_for_mac(to_find, type_of_thing, net_ok=True):
+ # type: (Optional[str], int, bool) -> Optional[str]
+ """Tries a variety of methods to get a MAC address.
+
+ Format of method lists:
+ Tuple: (regex, regex index, command, command args)
+ Command args is a list of strings to attempt to use as arguments
+ lambda: Function to call
+ """
+ if to_find is None:
+ log.warning("_hunt_for_mac() failed: to_find is None")
+ return None
+ if not PY2 and isinstance(to_find, bytes):
+ to_find = str(to_find, 'utf-8')
+
+ if WINDOWS and type_of_thing == INTERFACE:
+ methods = [
+ # getmac - Connection Name
+ (r'\r\n' + to_find + r'.*' + MAC_RE_DASH + r'.*\r\n',
+ 0, 'getmac.exe', ['/NH /V']),
+
+ # ipconfig
+ (to_find + r'(?:\n?[^\n]*){1,8}Physical Address[ .:]+' + MAC_RE_DASH + r'\r\n',
+ 0, 'ipconfig.exe', ['/all']),
+
+ # getmac - Network Adapter (the human-readable name)
+ (r'\r\n.*' + to_find + r'.*' + MAC_RE_DASH + r'.*\r\n',
+ 0, 'getmac.exe', ['/NH /V']),
+
+ # wmic - WMI command line utility
+ lambda x: _popen('wmic.exe', 'nic where "NetConnectionID = \'%s\'" get '
+ 'MACAddress /value' % x).strip().partition('=')[2],
+ ]
+ elif (WINDOWS or WSL) and type_of_thing in [IP4, IP6, HOSTNAME]:
+ methods = [
+ # arp -a - Parsing result with a regex
+ (MAC_RE_DASH, 0, 'arp.exe', ['-a %s' % to_find]),
+ ]
+
+ # Add methods that make network requests
+ # Insert it *after* arp.exe since that's probably faster.
+ if net_ok and type_of_thing != IP6 and not WSL:
+ methods.insert(1, _windows_ctypes_host)
+ elif (DARWIN or FREEBSD) and type_of_thing == INTERFACE:
+ methods = [
+ (r'ether ' + MAC_RE_COLON,
+ 0, 'ifconfig', [to_find]),
+
+ # Alternative match for ifconfig if it fails
+ (to_find + r'.*ether ' + MAC_RE_COLON,
+ 0, 'ifconfig', ['']),
+
+ (MAC_RE_COLON,
+ 0, 'networksetup', ['-getmacaddress %s' % to_find]),
+ ]
+ elif FREEBSD and type_of_thing in [IP4, IP6, HOSTNAME]:
+ methods = [
+ (r'\(' + re.escape(to_find) + r'\)\s+at\s+' + MAC_RE_COLON,
+ 0, 'arp', [to_find])
+ ]
+ elif OPENBSD and type_of_thing == INTERFACE:
+ methods = [
+ (r'lladdr ' + MAC_RE_COLON,
+ 0, 'ifconfig', [to_find]),
+ ]
+ elif OPENBSD and type_of_thing in [IP4, IP6, HOSTNAME]:
+ methods = [
+ (re.escape(to_find) + r'[ ]+' + MAC_RE_COLON,
+ 0, 'arp', ['-an']),
+ ]
+ elif type_of_thing == INTERFACE:
+ methods = [
+ _read_sys_iface_file,
+ _fcntl_iface,
+
+ # Fast modern Ubuntu ifconfig
+ (r'ether ' + MAC_RE_COLON,
+ 0, 'ifconfig', [to_find]),
+
+ # Fast ifconfig
+ (r'HWaddr ' + MAC_RE_COLON,
+ 0, 'ifconfig', [to_find]),
+
+ # ip link (Don't use 'list' due to SELinux [Android 24+])
+ (to_find + r'.*\n.*link/ether ' + MAC_RE_COLON,
+ 0, 'ip', ['link %s' % to_find, 'link']),
+
+ # netstat
+ (to_find + r'.*HWaddr ' + MAC_RE_COLON,
+ 0, 'netstat', ['-iae']),
+
+ # More variations of ifconfig
+ (to_find + r'.*ether ' + MAC_RE_COLON,
+ 0, 'ifconfig', ['']),
+ (to_find + r'.*HWaddr ' + MAC_RE_COLON,
+ 0, 'ifconfig', ['', '-a', '-v']),
+
+ # Tru64 ('-av')
+ (to_find + r'.*Ether ' + MAC_RE_COLON,
+ 0, 'ifconfig', ['-av']),
+ _uuid_lanscan_iface,
+ ]
+ elif type_of_thing in [IP4, IP6, HOSTNAME]:
+ esc = re.escape(to_find)
+ methods = [
+ _read_arp_file,
+ lambda x: _popen('ip', 'neighbor show %s' % x)
+ .partition(x)[2].partition('lladdr')[2].strip().split()[0],
+
+ (r'\(' + esc + r'\)\s+at\s+' + MAC_RE_COLON,
+ 0, 'arp', [to_find, '-an', '-an %s' % to_find]),
+
+ # Darwin oddness
+ (r'\(' + esc + r'\)\s+at\s+' + MAC_RE_DARWIN,
+ 0, 'arp', [to_find, '-a', '-a %s' % to_find]),
+ _uuid_ip,
+ ]
+ else:
+ log.critical("Reached end of _hunt_for_mac() if-else chain!")
+ return None
+ return _try_methods(methods, to_find)
+
+
+def _try_methods(methods, to_find=None):
+ # type: (list, Optional[str]) -> Optional[str]
+ """Runs the methods specified by _hunt_for_mac().
+
+ We try every method and see if it returned a MAC address. If it returns
+ None or raises an exception, we continue and try the next method.
+ """
+ found = None
+ for m in methods:
+ try:
+ if isinstance(m, tuple):
+ for arg in m[3]: # list(str)
+ if DEBUG:
+ log.debug("Trying: '%s %s'", m[2], arg)
+ # Arguments: (regex, _popen(command, arg), regex index)
+ found = _search(m[0], _popen(m[2], arg), m[1])
+ if DEBUG:
+ log.debug("Result: %s\n", found)
+ if found: # Skip remaining args AND remaining methods
+ break
+ elif callable(m):
+ if DEBUG:
+ log.debug("Trying: '%s' (to_find: '%s')", m.__name__, str(to_find))
+ if to_find is not None:
+ found = m(to_find)
+ else:
+ found = m()
+ if DEBUG:
+ log.debug("Result: %s\n", found)
+ else:
+ log.critical("Invalid type '%s' for method '%s'", type(m), str(m))
+ except Exception as ex:
+ if DEBUG:
+ log.debug("Exception: %s", str(ex))
+ if DEBUG >= 2:
+ log.debug(traceback.format_exc())
+ continue
+ if found: # Skip remaining methods
+ break
+ return found
+
+
+def _get_default_iface_linux():
+ # type: () -> Optional[str]
+ """Get the default interface by reading /proc/net/route.
+
+ This is the same source as the `route` command, however it's much
+ faster to read this file than to call `route`. If it fails for whatever
+ reason, we can fall back on the system commands (e.g for a platform
+ that has a route command, but maybe doesn't use /proc?).
+ """
+ data = _read_file('/proc/net/route')
+ if data is not None and len(data) > 1:
+ for line in data.split('\n')[1:-1]:
+ iface_name, dest = line.split('\t')[:2]
+ if dest == '00000000':
+ return iface_name
+ return None
+
+
+def _hunt_linux_default_iface():
+ # type: () -> Optional[str]
+ # NOTE: for now, we check the default interface for WSL using the
+ # same methods as POSIX, since those parts of the net stack work fine.
+ methods = [
+ _get_default_iface_linux,
+ lambda: _popen('route', '-n').partition('0.0.0.0')[2].partition('\n')[0].split()[-1],
+ lambda: _popen('ip', 'route list 0/0').partition('dev')[2].partition('proto')[0].strip(),
+ ]
+ return _try_methods(methods)
+
+
+def _get_default_iface_openbsd():
+ # type: () -> Optional[str]
+ methods = [
+ lambda: _popen('route', '-nq show -inet -gateway -priority 1')
+ .partition('127.0.0.1')[0].strip().rpartition(' ')[2],
+ ]
+ return _try_methods(methods)
+
+
+def _get_default_iface_freebsd():
+ # type: () -> Optional[str]
+ methods = [
+ (r'default[ ]+\S+[ ]+\S+[ ]+(\S+)\n',
+ 0, 'netstat', ['-r']),
+ ]
+ return _try_methods(methods)
+
+
+def _fetch_ip_using_dns():
+ # type: () -> str
+ """Determines the IP address of the default network interface.
+
+ Sends a UDP packet to Cloudflare's DNS (1.1.1.1), which should go through
+ the default interface. This populates the source address of the socket,
+ which we then inspect and return.
+ """
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(('1.1.1.1', 53))
+ ip = s.getsockname()[0]
+ s.close() # NOTE: sockets don't have context manager in 2.7 :(
+ return ip
diff --git a/misc/tools/mkimage.py b/misc/tools/mkimage.py
new file mode 100755
index 0000000..fd75f0a
--- /dev/null
+++ b/misc/tools/mkimage.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+
+# A quickly bashed together replacement for u-boot's mkimage written in python
+#
+# Copyright 2010 Craig Barker
+# Copyright 2020 Amar Takhar <amar@rtems.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# We support Python 2.6+ so this is okay.
+from __future__ import print_function
+
+import argparse
+from struct import *
+import sys
+import os.path
+import time
+import binascii
+
+
+
+MAGIC = 0x27051956
+IMG_NAME_LENGTH = 32
+
+archs = {'invalid':0, 'alpha':1, 'arm':2, 'x86':3, 'ia64':4, 'm68k':12,
+ 'microblaze':14, 'mips':5, 'mips64':6, 'nios':13, 'nios2':15,
+ 'powerpc':7, 'ppc':7, 's390':8, 'sh':9, 'sparc':10,
+ 'sparc64':11, 'blackfin':16, 'arv32':17, 'st200':18 }
+
+oss = {'invalid':0, 'openbsd':1, 'netbsd':2, 'freebsd':3, '4_4bsd':4,
+ 'linux':5, 'svr4':6, 'esix':7, 'solaris':8, 'irix':9,
+ 'sco':10, 'dell':11, 'ncr':12, 'lynos':13, 'vxworks':14,
+ 'psos':15, 'qnx':16, 'u-boot':17, 'rtems':18, 'artos':19,
+ 'unity':20, 'integrity':21 }
+
+types = {'invalid':0, 'standalone':1, 'kernel':2, 'ramdisk':3, 'multi':4,
+ 'firmware':5,'script':6, 'filesystem':7, 'flat_dt':8 }
+
+comps = {'none':0, 'bzip2':2, 'gzip':1, 'lzma':3 }
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument("-A","--arch", dest="arch", default="powerpc",
+ help="set architecture to 'arch'", metavar="ARCH")
+parser.add_argument("-O","--os", dest="os", default="linux",
+ help="set operating system to 'os'", metavar="OS")
+parser.add_argument("-T","--type", dest="type", default="kernel",
+ help="set image type to 'type'", metavar="TYPE")
+parser.add_argument("-C","--comp", dest="comp", default="gzip",
+ help="set compression type 'comp'", metavar="COMP")
+parser.add_argument("-a","--addr", dest="addr", default="0",
+ help="set load address to 'addr' (hex)", metavar="ADDR")
+parser.add_argument("-e","--ep", dest="ep", default="0",
+ help="set entry point to 'ep' (hex)", metavar="EP")
+parser.add_argument("-n","--name", dest="name", default="",
+ help="set image name to 'name'", metavar="NAME")
+parser.add_argument("-d","--datafile", dest="datafile",
+ help="use image data from 'datafile'", metavar="DATAFILE", required=True)
+parser.add_argument("-x","--xip", action="store_true", dest="xip", default=False,
+ help="set XIP (execute in place)")
+parser.add_argument("outputfile",
+ help="Output file.", metavar="OUTPUTFILE")
+
+
+options = parser.parse_args()
+
+if options.arch not in archs:
+ print("Invalid architecture specified, aborting")
+ sys.exit(2)
+
+if options.os not in oss:
+ print("Invalid operating system specified, aborting")
+ sys.exit(2)
+
+if options.comp not in comps:
+ print("Invalid compression specified, aborting")
+ sys.exit(2)
+
+if options.type not in types:
+ print("Invalid image type specified, aborting")
+ sys.exit(2)
+
+try:
+ inputsize = os.path.getsize(options.datafile)
+ inputfile = open(options.datafile, 'rb')
+
+except OSError as e:
+ print("Invalid datafile specified, aborting: %s" % e)
+ sys.exit(2)
+
+try:
+ outputfile = open(options.outputfile,'wb')
+
+except IOError as e:
+ print("Error opening output file for writing, aborting: %s" % e)
+ sys.exit(1)
+
+struct = Struct("!IIIIIIIBBBB"+str(IMG_NAME_LENGTH)+"s")
+
+outputfile.seek(struct.size);
+
+inputcrc = 0;
+
+while True:
+ inputblock = inputfile.read(4096)
+ if not inputblock: break
+ inputcrc = binascii.crc32(inputblock, inputcrc)
+ outputfile.write(inputblock)
+
+inputcrc = inputcrc & 0xffffffff
+
+timestamp = int(time.time())
+
+structdata = struct.pack(MAGIC, 0, timestamp, inputsize,
+ int(options.addr,16), int(options.ep,16), inputcrc,
+ oss[options.os], archs[options.arch], types[options.type],
+ comps[options.comp], options.name.encode("utf-8"))
+
+headercrc = binascii.crc32(structdata) & 0xFFFFFFFF
+
+structdata = struct.pack(MAGIC, headercrc, timestamp, inputsize,
+ int(options.addr,16), int(options.ep,16), inputcrc,
+ oss[options.os], archs[options.arch], types[options.type],
+ comps[options.comp], options.name.encode("utf-8"))
+
+outputfile.seek(0)
+outputfile.write(structdata)
+outputfile.close()
+inputfile.close()
+
+print("Image Name: ", options.name)
+print("Created: ", time.ctime(timestamp))
+print("Image Type: ", options.comp)
+print("Data Size: ", inputsize)
+print("Load Address: ", options.addr)
+print("Entry Point: ", options.ep)
diff --git a/misc/tools/tftpproxy.py b/misc/tools/tftpproxy.py
new file mode 100644
index 0000000..c0aebb0
--- /dev/null
+++ b/misc/tools/tftpproxy.py
@@ -0,0 +1,423 @@
+#
+# Copyright 2019, 2020 Chris Johns (chris@contemporary.software)
+# All rights reserved.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# The TFTP proxy redirects a TFTP session to another host. If you have a
+# farm of boards you can configure them to point to this proxy and it will
+# redirect the requests to another machine that is testing it.
+#
+
+from __future__ import print_function
+
+import argparse
+import os
+import socket
+import sys
+import time
+import threading
+
+try:
+ import socketserver
+except:
+ import SocketServer as socketserver
+
+from rtemstoolkit import configuration
+from rtemstoolkit import error
+from rtemstoolkit import log
+from rtemstoolkit import version
+
+import misc.tools.getmac
+
+def host_port_split(ip_port):
+ ips = ip_port.split(':')
+ port = 0
+ if len(ips) >= 1:
+ ip = ips[0]
+ if len(ips) == 2:
+ port = int(ips[1])
+ else:
+ raise error.general('invalid host:port: %s' % (ip_port))
+ return ip, port
+
+class tftp_session(object):
+
+ opcodes = ['nul', 'RRQ', 'WRQ', 'DATA', 'ACK', 'ERROR', 'OACK']
+
+ def __init__(self):
+ self.packets = []
+ self.block = 0
+ self.block_size = 512
+ self.timeout = 0
+ self.finished = True
+
+ def __str__(self):
+ return os.linesep.join([self.decode(p[0], p[1], p[2]) for p in self.packets])
+
+ def data(self, host, port, data):
+ finished = False
+ self.packets += [(host, port, data)]
+ opcode = (data[0] << 8) | data[1]
+ if opcode == 1 or opcode == 2:
+ self.block = 0
+ self.finished = False
+ value = self.get_option('timeout', data)
+ if value is not None:
+ self.timeout = int(value)
+ value = self.get_option('blksize', data)
+ if value is not None:
+ self.block_size = int(value)
+ else:
+ self.block_size = 512
+ elif opcode == 3:
+ self.block = (data[2] << 8) | data[3]
+ if len(data) - 4 < self.block_size:
+ self.finished = True
+ elif opcode == 4:
+ self.block = (data[2] << 8) | data[3]
+ if self.finished:
+ finished = True
+ return finished
+
+ def decode(self, host, port, data):
+ s = ''
+ dlen = len(data)
+ if dlen > 2:
+ opcode = (data[0] << 8) | data[1]
+ if opcode < len(self.opcodes):
+ if opcode == 1 or opcode == 2:
+ s += ' ' + self.opcodes[opcode] + ', '
+ i = 2
+ while data[i] != 0:
+ s += chr(data[i])
+ i += 1
+ while i < dlen - 1:
+ s += ', '
+ i += 1
+ while data[i] != 0:
+ s += chr(data[i])
+ i += 1
+ elif opcode == 3:
+ block = (data[2] << 8) | data[3]
+ s += ' ' + self.opcodes[opcode] + ', '
+ s += '#' + str(block) + ', '
+ if dlen > 4:
+ s += '%02x%02x..%02x%02x' % (data[4], data[5], data[-2], data[-1])
+ else:
+ s += '%02x%02x%02x%02x' % (data[4], data[5], data[6], data[6])
+ s += ',' + str(dlen - 4)
+ elif opcode == 4:
+ block = (data[2] << 8) | data[3]
+ s += ' ' + self.opcodes[opcode] + ' ' + str(block)
+ elif opcode == 5:
+ s += 'E ' + self.opcodes[opcode] + ', '
+ s += str((data[2] << 8) | (data[3]))
+ i = 2
+ while data[i] != 0:
+ s += chr(data[i])
+ i += 1
+ elif opcode == 6:
+ s += ' ' + self.opcodes[opcode]
+ i = 1
+ while i < dlen - 1:
+ s += ', '
+ i += 1
+ while data[i] != 0:
+ s += chr(data[i])
+ i += 1
+ else:
+ s += 'E INV(%d)' % (opcode)
+ else:
+ s += 'E INVALID LENGTH'
+ return s[:2] + '[%s:%d] ' % (host, port) + s[2:]
+
+ def get_option(self, option, data):
+ dlen = len(data)
+ opcode = (data[0] << 8) | data[1]
+ next_option = False
+ if opcode == 1 or opcode == 2:
+ i = 1
+ while i < dlen - 1:
+ o = ''
+ i += 1
+ while data[i] != 0:
+ o += chr(data[i])
+ i += 1
+ if o == option:
+ next_option = True
+ elif next_option:
+ return o
+ return None
+
+ def get_timeout(self, default_timeout, timeout_guard):
+ if self.timeout == 0:
+ return self.timeout + timeout_guard
+ return default_timeout
+
+ def get_block_size(self):
+ return self.block_size
+
+class udp_handler(socketserver.BaseRequestHandler):
+
+ def handle(self):
+ client_ip = self.client_address[0]
+ client_port = self.client_address[1]
+ client = '%s:%i' % (client_ip, client_port)
+ session = tftp_session()
+ finished = session.data(client_ip, client_port, self.request[0])
+ if not finished:
+ timeout = session.get_timeout(self.server.proxy.session_timeout, 1)
+ host = self.server.proxy.get_host(client_ip)
+ if host is not None:
+ session_count = self.server.proxy.get_session_count()
+ log.notice(' ] %6d: session: %s -> %s: start' % (session_count,
+ client,
+ host))
+ host_ip, host_server_port = host_port_split(host)
+ host_port = host_server_port
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.settimeout(timeout)
+ log.trace(' > ' + session.decode(client_ip,
+ client_port,
+ self.request[0]))
+ sock.sendto(self.request[0], (host_ip, host_port))
+ while not finished:
+ try:
+ data, address = sock.recvfrom(16 * 1024)
+ except socket.error as se:
+ log.notice(' ] session: %s -> %s: error: %s' % (client,
+ host,
+ se))
+ return
+ except socket.gaierror as se:
+ log.notice(' ] session: %s -> %s: error: %s' % (client,
+ host,
+ se))
+ return
+ except:
+ return
+ finished = session.data(address[0], address[1], data)
+ if address[0] == host_ip:
+ if host_port == host_server_port:
+ host_port = address[1]
+ if address[1] == host_port:
+ log.trace(' < ' + session.decode(address[0],
+ address[1],
+ data))
+ sock.sendto(data, (client_ip, client_port))
+ elif address[0] == client_ip and address[1] == client_port:
+ log.trace(' > ' + session.decode(address[0],
+ address[1],
+ data))
+ sock.sendto(data, (host_ip, host_port))
+ log.notice(' ] %6d: session: %s -> %s: end' % (session_count,
+ client,
+ host))
+ else:
+ mac = misc.tools.getmac.get_mac_address(ip = client_ip)
+ log.trace(' . request: host not found: %s (%s)' % (client, mac))
+
+class udp_server(socketserver.ThreadingMixIn, socketserver.UDPServer):
+ pass
+
+class proxy_server(object):
+ def __init__(self, config, host, port):
+ self.lock = threading.Lock()
+ self.session_timeout = 10
+ self.host = host
+ self.port = port
+ self.server = None
+ self.clients = { }
+ self.config = configuration.configuration()
+ self._load(config)
+ self.session_counter = 0
+
+ def __del__(self):
+ self.stop()
+
+ def _lock(self):
+ self.lock.acquire()
+
+ def _unlock(self):
+ self.lock.release()
+
+ def _load_client(self, client, depth = 0):
+ if depth > 32:
+ raise error.general('\'clients\'" nesting too deep; circular?')
+ if not self.config.has_section(client):
+ raise error.general('client not found: %s' % (client))
+ for c in self.config.comma_list(client, 'clients', err = False):
+ self._load_client(c, depth + 1)
+ if client in self.clients:
+ raise error.general('repeated client: %s' % (client))
+ host = self.config.get_item(client, 'host', err = False)
+ if host is not None:
+ ips = self.config.comma_list(client, 'ip', err = False)
+ macs = self.config.comma_list(client, 'mac', err = False)
+ if len(ips) != 0 and len(macs) != 0:
+ raise error.general('client has ip and mac: %s' % (client))
+ if len(ips) != 0:
+ keys = ips
+ elif len(macs) != 0:
+ keys = macs
+ else:
+ raise error.general('not client ip or mac: %s' % (client))
+ for key in keys:
+ self.clients[key] = host
+
+ def _load(self, config):
+ self.config.load(config)
+ clients = self.config.comma_list('default', 'clients', err = False)
+ if len(clients) == 0:
+ raise error.general('\'clients\'" entry not found in config [defaults]')
+ for client in clients:
+ self._load_client(client)
+
+ def start(self):
+ log.notice('Proxy: %s:%i' % (self.host, self.port))
+ if self.host == 'all':
+ host = ''
+ else:
+ host = self.host
+ try:
+ self.server = udp_server((host, self.port), udp_handler)
+ except Exception as e:
+ raise error.general('proxy create: %s' % (e))
+ self.server.proxy = self
+ self._lock()
+ try:
+ self.server_thread = threading.Thread(target = self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+ finally:
+ self._unlock()
+
+ def stop(self):
+ self._lock()
+ try:
+ if self.server is not None:
+ self.server.shutdown()
+ self.server.server_close()
+ self.server = None
+ finally:
+ self._unlock()
+
+ def run(self):
+ while True:
+ time.sleep(1)
+
+ def get_host(self, client):
+ host = None
+ self._lock()
+ try:
+ if client in self.clients:
+ host = self.clients[client]
+ else:
+ mac = getmac.get_mac_address(ip = client)
+ if mac in self.clients:
+ host = self.clients[mac]
+ finally:
+ self._unlock()
+ return host
+
+ def get_session_count(self):
+ count = 0
+ self._lock()
+ try:
+ self.session_counter += 1
+ count = self.session_counter
+ finally:
+ self._unlock()
+ return count
+
+
+def load_log(logfile):
+ if logfile is None:
+ log.default = log.log(streams = ['stdout'])
+ else:
+ log.default = log.log(streams = [logfile])
+
+def run(args = sys.argv, command_path = None):
+ ec = 0
+ notice = None
+ proxy = None
+ try:
+ description = 'Proxy TFTP sessions from the host running this proxy'
+ description += 'to hosts and ports defined in the configuration file. '
+ description += 'The tool lets you create a farm of hardware and to run '
+ description += 'more than one TFTP test session on a host or multiple '
+ description += 'hosts at once. This proxy service is not considered secure'
+ description += 'and is for use in a secure environment.'
+
+ argsp = argparse.ArgumentParser(prog = 'rtems-tftp-proxy',
+ description = description)
+ argsp.add_argument('-l', '--log',
+ help = 'log file.',
+ type = str, default = None)
+ argsp.add_argument('-v', '--trace',
+ help = 'enable trace logging for debugging.',
+ action = 'store_true', default = False)
+ argsp.add_argument('-c', '--config',
+ help = 'proxy configuation (default: %(default)s).',
+ type = str, default = None)
+ argsp.add_argument('-B', '--bind',
+ help = 'address to bind the proxy too (default: %(default)s).',
+ type = str, default = 'all')
+ argsp.add_argument('-P', '--port',
+ help = 'port to bind the proxy too(default: %(default)s).',
+ type = int, default = '69')
+
+ argopts = argsp.parse_args(args[1:])
+
+ load_log(argopts.log)
+ log.notice('RTEMS Tools - TFTP Proxy, %s' % (version.string()))
+ log.output(log.info(args))
+ log.tracing = argopts.trace
+
+ if argopts.config is None:
+ raise error.general('no config file, see -h')
+
+ proxy = proxy_server(argopts.config, argopts.bind, argopts.port)
+
+ try:
+ proxy.start()
+ proxy.run()
+ except:
+ proxy.stop()
+ raise
+
+ except error.general as gerr:
+ notice = str(gerr)
+ ec = 1
+ except error.internal as ierr:
+ notice = str(ierr)
+ ec = 1
+ except error.exit as eerr:
+ pass
+ except KeyboardInterrupt:
+ notice = 'abort: user terminated'
+ ec = 1
+ except:
+ raise
+ notice = 'abort: unknown error'
+ ec = 1
+ if proxy is not None:
+ del proxy
+ if notice is not None:
+ log.stderr(notice)
+ sys.exit(ec)
+
+if __name__ == "__main__":
+ run()
diff --git a/misc/wscript b/misc/wscript
index f83c74b..521eddf 100644
--- a/misc/wscript
+++ b/misc/wscript
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2014-2016 Chris Johns (chrisj@rtems.org)
+# Copyright 2014, 2020 Chris Johns (chrisj@rtems.org)
# Copyright 2018 embedded brains GmbH
# All rights reserved.
#
@@ -74,12 +74,18 @@ def build(bld):
# Install the boot image code.
#
bld(features = 'py',
- source = ['tools/boot.py',
- 'tools/cmd-boot-image.py'],
+ source = ['__init__.py',
+ 'tools/__init__.py',
+ 'tools/boot.py',
+ 'tools/tftpproxy.py',
+ 'tools/getmac/__init__.py',
+ 'tools/getmac/getmac.py'],
install_from = '.',
install_path = '${PREFIX}/share/rtems/misc')
bld.install_files('${PREFIX}/bin',
- ['rtems-boot-image'],
+ ['rtems-boot-image',
+ 'rtems-tftp-proxy',
+ 'tools/mkimage.py'],
chmod = 0o755)
bld.install_files('${PREFIX}/share/rtems/tools/config',
'tools/config/rtems-boot.ini')
diff --git a/rtemstoolkit/configuration.py b/rtemstoolkit/configuration.py
index a73fd9b..1f57de4 100644
--- a/rtemstoolkit/configuration.py
+++ b/rtemstoolkit/configuration.py
@@ -140,7 +140,7 @@ class configuration:
def comma_list(self, section, label, err = True):
items = self.get_item(section, label, err)
- if items is None:
+ if items is None or len(items) == 0:
return []
return sorted(set([a.strip() for a in items.split(',')]))
diff --git a/rtemstoolkit/elftoolchain/common/native-elf-format b/rtemstoolkit/elftoolchain/common/native-elf-format
index 7a5ca5b..18f817a 100755
--- a/rtemstoolkit/elftoolchain/common/native-elf-format
+++ b/rtemstoolkit/elftoolchain/common/native-elf-format
@@ -41,6 +41,8 @@ $1 ~ "Machine:" {
elfarch = "EM_MIPS";
} else if (match($0, ".*[xX]86[-_]64")) {
elfarch = "EM_X86_64";
+ } else if (match($0, "Sparc v9")) {
+ elfarch = "EM_SPARCV9";
} else {
elfarch = "unknown";
}
diff --git a/rtemstoolkit/execute.py b/rtemstoolkit/execute.py
index 35f616c..ed81589 100755
--- a/rtemstoolkit/execute.py
+++ b/rtemstoolkit/execute.py
@@ -139,12 +139,20 @@ class execute(object):
tmp = bytes('temp', sys.stdin.encoding)
except:
encoding = False
+ input_types = [str, bytes]
+ try:
+ # Unicode is not valid in python3, not added to the list
+ input_types += [unicode]
+ except:
+ pass
try:
while True:
if trace_threads:
print('execute:_writethread: call input', input)
lines = input()
- if type(lines) == str or type(lines) == bytes:
+ if trace_threads:
+ print('execute:_writethread: input returned:', type(lines))
+ if type(lines) in input_types:
try:
if encoding:
lines = bytes(lines, sys.stdin.encoding)
@@ -216,6 +224,9 @@ class execute(object):
sd = sd[:-1]
if len(sd) > 0:
for l in sd:
+ if trace_threads:
+ print('execute:_readthread: output-line:',
+ count, type(l))
_output_line(l + '\n', exe, prefix, out, count)
count += 1
if count > 10:
@@ -576,8 +587,10 @@ if __name__ == "__main__":
def capture_output(text):
print(text, end = '')
- cmd_shell_test = 'if "%OS%" == "Windows_NT" (echo It is WinNT) else echo Is is not WinNT'
- sh_shell_test = 'x="me"; if [ $x = "me" ]; then echo "It was me"; else "It was him"; fi'
+ cmd_shell_test = ('if "%OS%" == "Windows_NT" (echo It is WinNT) '
+ 'else echo It is not WinNT')
+ sh_shell_test = ('x="me"; if [ $x = "me" ]; then echo "It was me"; '
+ 'else "It was him"; fi')
commands = {}
commands['windows'] = {}
diff --git a/rtemstoolkit/linux.py b/rtemstoolkit/linux.py
index 7e45409..21f6f97 100644
--- a/rtemstoolkit/linux.py
+++ b/rtemstoolkit/linux.py
@@ -33,26 +33,14 @@
# RTEMS project's spec files.
#
+import multiprocessing
import os
import platform
-from rtemstoolkit import execute
from rtemstoolkit import path
def cpus():
- processors = '/bin/grep processor /proc/cpuinfo'
- e = execute.capture_execution()
- exit_code, proc, output = e.shell(processors)
- ncpus = 0
- if exit_code == 0:
- try:
- for l in output.split('\n'):
- count = l.split(':')[1].strip()
- if int(count) > ncpus:
- ncpus = int(count)
- except:
- pass
- return ncpus + 1
+ return multiprocessing.cpu_count()
def overrides():
uname = os.uname()
@@ -83,8 +71,9 @@ def overrides():
try:
distro = platform.dist()[0]
distro_ver = float(platform.dist()[1])
- except ValueError:
+ except (AttributeError, ValueError):
# Non LSB distro found, use failover"
+ distro = ''
pass
# Non LSB - fail over to issue
diff --git a/rtemstoolkit/path.py b/rtemstoolkit/path.py
index 9401e99..b15164b 100644
--- a/rtemstoolkit/path.py
+++ b/rtemstoolkit/path.py
@@ -314,8 +314,12 @@ def copy_tree(src, dst):
hsrc = host(src)
hdst = host(dst)
- if exists(src):
- names = listdir(src)
+ if exists(hsrc):
+ if isdir(hsrc):
+ names = listdir(hsrc)
+ else:
+ names = [basename(hsrc)]
+ hsrc = dirname(hsrc)
else:
names = []
diff --git a/rtemstoolkit/rld-dwarf.cpp b/rtemstoolkit/rld-dwarf.cpp
index 6f249bf..d9ac6f3 100644
--- a/rtemstoolkit/rld-dwarf.cpp
+++ b/rtemstoolkit/rld-dwarf.cpp
@@ -32,6 +32,7 @@
#include <rld.h>
#include <rld-path.h>
#include <rld-dwarf.h>
+#include <rld-symbols.h>
namespace rld
{
@@ -574,6 +575,104 @@ namespace rld
return *this;
}
+ variable::variable (file& debug, debug_info_entry& die)
+ : debug (debug),
+ external_ (false),
+ declaration_ (false)
+ {
+ dwarf_bool db;
+
+ if (die.attribute (DW_AT_external, db, false))
+ external_ = db ? true : false;
+
+ if (die.attribute (DW_AT_declaration, db, false))
+ declaration_ = db ? true : false;
+
+ /*
+ * Get the name attribute. (if present)
+ */
+ die.attribute (DW_AT_name, name_);
+ die.attribute (DW_AT_decl_file, decl_file_);
+ die.attribute (DW_AT_decl_line, decl_line_);
+
+ if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+ {
+ std::cout << "dwarf::variable: ";
+ dump (std::cout);
+ std::cout << std::endl;
+ }
+ }
+
+ variable::variable (const variable& orig)
+ : debug (orig.debug),
+ external_ (orig.external_),
+ declaration_ (orig.declaration_),
+ name_ (orig.name_),
+ decl_file_ (orig.decl_file_),
+ decl_line_ (orig. decl_line_)
+ {
+ }
+
+ variable::~variable ()
+ {
+ }
+
+ std::string
+ variable::name () const
+ {
+ return name_;
+ }
+
+ bool
+ variable::is_external () const
+ {
+ return external_;
+ }
+
+ bool
+ variable::is_declaration () const
+ {
+ return declaration_;
+ }
+
+ size_t
+ variable::size () const
+ {
+ size_t s = 0;
+ return s;
+ }
+
+ variable&
+ variable::operator = (const variable& rhs)
+ {
+ if (this != &rhs)
+ {
+ debug = rhs.debug;
+ external_ = rhs.external_;
+ declaration_ = rhs.declaration_;
+ name_ = rhs.name_;
+ decl_file_ = rhs.decl_file_;
+ decl_line_ = rhs.decl_line_;
+ }
+ return *this;
+ }
+
+ void
+ variable::dump (std::ostream& out) const
+ {
+ if (name_.empty ())
+ out << "NO-NAME";
+ else
+ out << name_;
+ out << " ["
+ << (char) (external_ ? 'E' : '-')
+ << (char) (declaration_ ? 'D' : '-')
+ << "] size=" << size ()
+ << std::hex << std::setfill ('0')
+ << " (0x" << size () << ')'
+ << std::dec << std::setfill (' ');
+ }
+
function::function (file& debug, debug_info_entry& die)
: debug (debug),
@@ -635,82 +734,78 @@ namespace rld
}
}
- if (declaration_)
- {
- die.attribute (DW_AT_name, name_);
- }
- else
+ /*
+ * Get the name attribute. (if present)
+ */
+ if (!die.attribute (DW_AT_name, name_, false))
{
+ bool found = false;
+
/*
- * Get the name attribute. (if present)
+ * For inlined function, the actual name is probably in the DIE
+ * referenced by DW_AT_abstract_origin. (if present)
*/
- if (!die.attribute (DW_AT_name, name_, false))
+ dwarf_attribute abst_at;
+ if (die.attribute (DW_AT_abstract_origin, abst_at, false))
{
- bool found = false;
-
- /*
- * For inlined function, the actual name is probably in the DIE
- * referenced by DW_AT_abstract_origin. (if present)
- */
- dwarf_attribute abst_at;
- if (die.attribute (DW_AT_abstract_origin, abst_at, false))
+ dwarf_offset abst_at_die_offset;
+ dwarf_error de;
+ int dr;
+ dr = ::dwarf_global_formref (abst_at, &abst_at_die_offset, &de);
+ if (dr == DW_DLV_OK)
{
- dwarf_offset abst_at_die_offset;
+ debug_info_entry abst_at_die (debug, abst_at_die_offset);
+ if (abst_at_die.attribute (DW_AT_name, name_, false))
+ {
+ found = true;
+ abst_at_die.attribute (DW_AT_inline, inline_, false);
+ if (abst_at_die.attribute (DW_AT_external, db, false))
+ external_ = db ? true : false;
+ if (abst_at_die.attribute (DW_AT_declaration, db, false))
+ declaration_ = db ? true : false;
+ abst_at_die.attribute (DW_AT_linkage_name, linkage_name_, false);
+ abst_at_die.attribute (DW_AT_decl_file, decl_file_, false);
+ abst_at_die.attribute (DW_AT_decl_line, decl_line_, false);
+ }
+ }
+ }
+
+ /*
+ * If DW_AT_name is not present, but DW_AT_specification is present,
+ * then probably the actual name is in the DIE referenced by
+ * DW_AT_specification.
+ */
+ if (!found)
+ {
+ dwarf_attribute spec;
+ if (die.attribute (DW_AT_specification, spec, false))
+ {
+ dwarf_offset spec_die_offset;
dwarf_error de;
int dr;
- dr = ::dwarf_global_formref (abst_at, &abst_at_die_offset, &de);
+ dr = ::dwarf_global_formref (spec, &spec_die_offset, &de);
if (dr == DW_DLV_OK)
{
- debug_info_entry abst_at_die (debug, abst_at_die_offset);
- if (abst_at_die.attribute (DW_AT_name, name_, false))
+ debug_info_entry spec_die (debug, spec_die_offset);
+ if (spec_die.attribute (DW_AT_name, name_, false))
{
found = true;
- abst_at_die.attribute (DW_AT_inline, inline_, false);
- if (abst_at_die.attribute (DW_AT_external, db, false))
+ if (spec_die.attribute (DW_AT_external, db, false))
external_ = db ? true : false;
- if (abst_at_die.attribute (DW_AT_declaration, db, false))
+ if (spec_die.attribute (DW_AT_declaration, db, false))
declaration_ = db ? true : false;
- abst_at_die.attribute (DW_AT_linkage_name, linkage_name_, false);
- abst_at_die.attribute (DW_AT_decl_file, decl_file_, false);
- abst_at_die.attribute (DW_AT_decl_line, decl_line_, false);
- }
- }
- }
-
- /*
- * If DW_AT_name is not present, but DW_AT_specification is present,
- * then probably the actual name is in the DIE referenced by
- * DW_AT_specification.
- */
- if (!found)
- {
- dwarf_attribute spec;
- if (die.attribute (DW_AT_specification, spec, false))
- {
- dwarf_offset spec_die_offset;
- dwarf_error de;
- int dr;
- dr = ::dwarf_global_formref (spec, &spec_die_offset, &de);
- if (dr == DW_DLV_OK)
- {
- debug_info_entry spec_die (debug, spec_die_offset);
- if (spec_die.attribute (DW_AT_name, name_, false))
- {
- found = true;
- if (spec_die.attribute (DW_AT_external, db, false))
- external_ = db ? true : false;
- if (spec_die.attribute (DW_AT_declaration, db, false))
- declaration_ = db ? true : false;
- spec_die.attribute (DW_AT_linkage_name, linkage_name_, false);
- spec_die.attribute (DW_AT_decl_file, decl_file_, false);
- spec_die.attribute (DW_AT_decl_line, decl_line_, false);
- }
+ spec_die.attribute (DW_AT_linkage_name, linkage_name_, false);
+ spec_die.attribute (DW_AT_decl_file, decl_file_, false);
+ spec_die.attribute (DW_AT_decl_line, decl_line_, false);
}
}
}
}
}
+ if (!linkage_name_.empty() && name_.empty())
+ rld::symbols::demangle_name(linkage_name_, name_);
+
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
{
std::cout << "dwarf::function: ";
@@ -911,7 +1006,11 @@ namespace rld
out << " pc_low=0x" << pc_low_
<< " pc_high=0x" << pc_high_;
if (!linkage_name_.empty ())
- out << " ln=" << linkage_name_;
+ {
+ std::string show_name;
+ rld::symbols::demangle_name(linkage_name_, show_name);
+ out << " ln=" << show_name;
+ }
out << std::dec << std::setfill (' ');
if (!call_file_.empty ())
out << " cf=" << call_file_ << ':' << call_line_;
@@ -1330,7 +1429,11 @@ namespace rld
const char* s;
::dwarf_get_TAG_name (tag (), &s);
out << level_prefix.substr (0, level_prefix.length () - 1)
- << "+- " << s << std::endl;
+ << "+- " << s << " ("
+ << std::hex << std::setfill ('0')
+ << std::setw (8) << offset_
+ << std::dec << std::setfill (' ')
+ << ')' << std::endl;
dwarf_attribute* attributes;
dwarf_signed attr_count;
@@ -1428,6 +1531,12 @@ namespace rld
dr = ::dwarf_attrval_string (die, attr, &s, &de);
libdwarf_error_check ("debug_info_entry::dump", dr, de);
out << " : " << s;
+ if (rld::symbols::is_cplusplus(s))
+ {
+ std::string cpps;
+ rld::symbols::demangle_name(s, cpps);
+ out << " `" << cpps << '`';
+ }
break;
case DW_FORM_sec_offset:
switch (attr)
@@ -1506,7 +1615,6 @@ namespace rld
source_ (debug, die_offset)
{
die.attribute (DW_AT_name, name_);
- name_ = name_;
die.attribute (DW_AT_producer, producer_);
@@ -1609,11 +1717,11 @@ namespace rld
auto first = addr_lines_.begin ();
auto last = addr_lines_.end () - 1;
std::cout << "dwarf::compilation_unit: line_low=0x"
- << std::hex
+ << std::hex << std::setfill('0')
<< std::setw (8) << first->location ()
<< ", line_high=0x"
<< std::setw (8) << last->location ()
- << std::dec
+ << std::dec << std::setfill(' ')
<< std::endl;
}
}
@@ -1625,7 +1733,9 @@ namespace rld
{
std::cout << "dwarf::compilation_unit: " << std::setw (3) << ++lc
<< ": 0x"
- << std::hex << std::setw (8) << l.location () << std::dec
+ << std::hex << std::setfill('0')
+ << std::setw (8) << l.location ()
+ << std::dec << std::setfill(' ')
<< " - "
<< (char) (l.is_a_begin_statement () ? 'B' : '.')
<< (char) (l.is_in_a_block () ? 'I' : '.')
@@ -1665,6 +1775,38 @@ namespace rld
}
void
+ compilation_unit::load_variables ()
+ {
+ debug_info_entry die (debug, die_offset);
+ debug_info_entry child (debug);
+ if (die.get_child (child))
+ load_variables (child);
+ }
+
+ void
+ compilation_unit::load_variables (debug_info_entry& die)
+ {
+ while (true)
+ {
+ if (die.tag () == DW_TAG_variable)
+ {
+ function func (debug, die);
+ functions_.push_back (func);
+ }
+
+ debug_info_entry next (die.get_debug ());
+
+ if (die.get_child (next))
+ load_functions (next);
+
+ if (!die.get_sibling (next))
+ break;
+
+ die = next;
+ }
+ }
+
+ void
compilation_unit::load_functions ()
{
debug_info_entry die (debug, die_offset);
@@ -1984,6 +2126,13 @@ namespace rld
}
void
+ file::load_variables ()
+ {
+ for (auto& cu : cus)
+ cu.load_variables ();
+ }
+
+ void
file::load_functions ()
{
for (auto& cu : cus)
diff --git a/rtemstoolkit/rld-dwarf.h b/rtemstoolkit/rld-dwarf.h
index 030be3a..45fbab1 100644
--- a/rtemstoolkit/rld-dwarf.h
+++ b/rtemstoolkit/rld-dwarf.h
@@ -277,6 +277,59 @@ namespace rld
};
/**
+ * Variable.
+ */
+ class variable
+ {
+ public:
+
+ variable (file& debug, debug_info_entry& die);
+ variable (const variable& orig);
+ ~variable ();
+
+ /**
+ * Get the name of the variable.
+ */
+ std::string name () const;
+
+ /**
+ * Is the variable external?
+ */
+ bool is_external () const;
+
+ /**
+ * Is this just a declaration?
+ */
+ bool is_declaration () const;
+
+ /**
+ * Size of the variable.
+ */
+ size_t size () const;
+
+ /**
+ * Assigment operator.
+ */
+ variable& operator = (const variable& rhs);
+
+ /**
+ * Dump the variable.
+ */
+ void dump (std::ostream& out) const;
+
+ private:
+
+ file& debug;
+ bool external_;
+ bool declaration_;
+ std::string name_;
+ std::string decl_file_;
+ dwarf_unsigned decl_line_;
+ };
+
+ typedef std::vector < variable > variables;
+
+ /**
* Function.
*/
class function
@@ -620,6 +673,11 @@ namespace rld
void load_types ();
/**
+ * Load the variables.
+ */
+ void load_variables ();
+
+ /**
* Load the functions.
*/
void load_functions ();
@@ -677,6 +735,7 @@ namespace rld
private:
+ void load_variables (debug_info_entry& die);
void load_functions (debug_info_entry& die);
file& debug; ///< The DWARF debug handle.
@@ -781,6 +840,11 @@ namespace rld
void load_functions ();
/**
+ * Load the DWARF variables information.
+ */
+ void load_variables ();
+
+ /**
* Get the source location given an address.
*/
bool get_source (const unsigned int address,
@@ -793,6 +857,11 @@ namespace rld
void get_producer_sources (producer_sources& producers);
/**
+ * Get the variable given a name. Raises an exception if not found.
+ */
+ variable& get_variable (std::string& name);
+
+ /**
* Does the function exist.
*/
bool function_valid (std::string&name);
diff --git a/rtemstoolkit/rld-elf.cpp b/rtemstoolkit/rld-elf.cpp
index 231f2bf..ffa3376 100644
--- a/rtemstoolkit/rld-elf.cpp
+++ b/rtemstoolkit/rld-elf.cpp
@@ -1189,6 +1189,11 @@ namespace rld
{ "m68k", EM_COLDFIRE },
{ "mips", EM_MIPS },
{ "powerpc", EM_PPC },
+#ifndef EM_RISCV
+ { "riscv", 243 }, /* If not in libelf yet */
+#else
+ { "riscv", EM_RISCV },
+#endif
{ "sh", EM_SH },
{ "sparc", EM_SPARC },
{ "sparc64", EM_SPARC },
diff --git a/rtemstoolkit/rld-rap.cpp b/rtemstoolkit/rld-rap.cpp
index 455328a..235de27 100644
--- a/rtemstoolkit/rld-rap.cpp
+++ b/rtemstoolkit/rld-rap.cpp
@@ -924,7 +924,7 @@ namespace rld
{
uint32_t relocs = 0;
for (int s = 0; s < rap_secs; ++s)
- relocs += secs[s].relocs.size ();
+ relocs += get_relocations (s);
return relocs;
}
@@ -1355,6 +1355,8 @@ namespace rld
void
image::write_relocations (compress::compressor& comp)
{
+ uint32_t rr = 0;
+
for (int s = 0; s < rap_secs; ++s)
{
uint32_t count = get_relocations (s);
@@ -1390,7 +1392,7 @@ namespace rld
for (relocations::const_iterator ri = relocs.begin ();
ri != relocs.end ();
- ++ri, ++sr, ++rc)
+ ++ri)
{
const relocation& reloc = *ri;
uint32_t info = GELF_R_TYPE (reloc.info);
@@ -1399,6 +1401,9 @@ namespace rld
bool write_addend = sec.rela;
bool write_symname = false;
+ if (reloc.symsect == 0)
+ continue;
+
offset = sec.offset + reloc.offset;
if (rld::verbose () >= RLD_VERBOSE_TRACE)
@@ -1498,6 +1503,10 @@ namespace rld
if (write_symname)
comp << reloc.symname;
+
+ ++rc;
+ ++sr;
+ ++rr;
}
}
}
@@ -1717,7 +1726,8 @@ namespace rld
compressor.transferred ()) % 10;
std::cout << "rap: objects: " << app_objects.size ()
<< ", size: " << compressor.compressed ()
- << ", compression: " << pcent << '.' << premand << '%'
+ << ", expanded: " << compressor.transferred ()
+ << ", compressed: " << pcent << '.' << premand << '%'
<< std::endl;
}
}
diff --git a/rtemstoolkit/rld-symbols.cpp b/rtemstoolkit/rld-symbols.cpp
index 00af9b5..661b598 100644
--- a/rtemstoolkit/rld-symbols.cpp
+++ b/rtemstoolkit/rld-symbols.cpp
@@ -50,7 +50,8 @@ namespace rld
if (name.compare (0, wrapper.length (), wrapper) == 0)
offset = wrapper.length ();
char* demangled_name = ::cplus_demangle (name.c_str () + offset,
- DMGL_ANSI | DMGL_PARAMS);
+ DMGL_ANSI | DMGL_PARAMS | DMGL_TYPES |
+ DMGL_RET_POSTFIX);
if (demangled_name)
{
demangled = demangled_name;
diff --git a/rtemstoolkit/rtems.py b/rtemstoolkit/rtems.py
index fc46946..c7dcd9c 100755
--- a/rtemstoolkit/rtems.py
+++ b/rtemstoolkit/rtems.py
@@ -71,10 +71,7 @@ def configuration_path(prog = None):
2. Ok to directly call os.path.
'''
if prog is None:
- if len(sys.argv) == 1:
- exec_name = sys.argv[0]
- else:
- exec_name = sys.argv[1]
+ exec_name = sys.argv[0]
else:
exec_name = prog
exec_name = os.path.abspath(exec_name)
@@ -108,7 +105,7 @@ def bsp_configuration_file(prog = None):
class configuration:
def __init__(self):
- self.config = configuration_.configuration()
+ self.config = configuration_.configuration(raw = False)
self.archs = { }
self.profiles = { }
@@ -157,8 +154,10 @@ class configuration:
for arch in profile['archs']:
bsps = 'bsps_%s' % (arch)
profile[bsps] = self.config.comma_list(profile['name'], bsps)
- self.profiles[profile['name']] = profile
+ self.profiles[profile['name']] = dict(profile)
+ profile = None
invalid_chars = re.compile(r'[^a-zA-Z0-9_-]')
+ archs = sorted(list(set(archs)))
for a in set(archs):
if len(invalid_chars.findall(a)) != 0:
raise error.general('invalid character(s) in arch name: %s' % (a))
diff --git a/rtemstoolkit/wscript b/rtemstoolkit/wscript
index d9f01b9..bd7254b 100644
--- a/rtemstoolkit/wscript
+++ b/rtemstoolkit/wscript
@@ -74,8 +74,8 @@ def build(bld):
#
conf['warningflags'] = ['-Wall', '-Wextra', '-pedantic']
conf['optflags'] = bld.env.C_OPTS
- conf['cflags'] = list(set(['-pipe', '-g'] + conf['optflags']))
- conf['cxxflags'] = list(set(['-pipe', '-g', '-std=c++11'] + conf['optflags']))
+ conf['cflags'] = ['-pipe', '-g'] + conf['optflags']
+ conf['cxxflags'] = ['-pipe', '-g', '-std=c++11'] + conf['optflags']
conf['linkflags'] = ['-g']
#
@@ -208,7 +208,7 @@ def bld_elftoolchain(bld, conf):
bld.stlib(target = 'elf',
features = 'c',
install_path = None,
- uses = ['native-elf-format'],
+ use = ['native-elf-format'],
includes = [bld.bldnode.abspath(),
'elftoolchain/libelf', 'elftoolchain/common'] + includes,
cflags = conf['cflags'],
diff --git a/tester/__init__.py b/tester/__init__.py
new file mode 100644
index 0000000..6ff279c
--- /dev/null
+++ b/tester/__init__.py
@@ -0,0 +1,30 @@
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2020 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+all = []
diff --git a/tester/covoar/wscript b/tester/covoar/wscript
index 7efa0dd..165a1b8 100644
--- a/tester/covoar/wscript
+++ b/tester/covoar/wscript
@@ -138,3 +138,4 @@ def build(bld):
cflags = ['-O2', '-g'],
cxxflags = ['-std=c++11', '-O2', '-g'],
includes = ['.'] + rtl_includes)
+ bld.install_files('${PREFIX}/share/rtems/tester/covoar', ['covoar.css', 'table.js'])
diff --git a/tester/rt/check.py b/tester/rt/check.py
index 3bb24e3..2a38d99 100755
--- a/tester/rt/check.py
+++ b/tester/rt/check.py
@@ -284,7 +284,7 @@ class warnings_errors:
common = warnings['common']
for build in builds:
build_warnings = warnings[build]
- if build is not 'common':
+ if build != 'common':
build_warnings = [w for w in build_warnings if w not in common]
s += textbox.row(cols_1,
[' %s : %d warning(s)' % (build,
@@ -1020,7 +1020,7 @@ class builder:
self._failures_report()
def run_jobs(self, jobs):
- if path.exists(self.build_dir):
+ if path.exists(self.build_dir) and not self.options['no-clean']:
log.notice('Cleaning: %s' % (self.build_dir))
path.removeall(self.build_dir)
self.start = _now()
diff --git a/tester/rt/cmd-run.py b/tester/rt/cmd-run.py
deleted file mode 100755
index 221c3f8..0000000
--- a/tester/rt/cmd-run.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2017 Chris Johns (chrisj@rtems.org)
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-from __future__ import print_function
-
-import sys, os
-
-base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
-rtems = os.path.dirname(base)
-sys.path = [rtems] + sys.path
-
-try:
- import run
- run.run(sys.argv[1:], command_path = base)
-except ImportError:
- print("Incorrect RTEMS Tools installation", file = sys.stderr)
- sys.exit(1)
diff --git a/tester/rt/config.py b/tester/rt/config.py
index aa4a2d8..2c36e14 100644
--- a/tester/rt/config.py
+++ b/tester/rt/config.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2017 Chris Johns (chrisj@rtems.org)
+# Copyright 2013-2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -48,9 +48,10 @@ from rtemstoolkit import path
from rtemstoolkit import stacktraces
-import console
-import gdb
-import tftp
+import tester.rt.console
+import tester.rt.exe
+import tester.rt.gdb
+import tester.rt.tftp
timeout = 15
@@ -78,6 +79,7 @@ class file(config.file):
self.report = report
self.name = name
self.timedout = False
+ self.test_too_long = False
self.test_started = False
self.kill_good = False
self.kill_on_end = False
@@ -102,6 +104,12 @@ class file(config.file):
self._unlock()
self.capture('*** TIMEOUT TIMEOUT')
+ def _test_too_long(self):
+ self._lock()
+ self.test_too_long = True
+ self._unlock()
+ self.capture('*** TEST TOO LONG')
+
def _ok_kill(self):
self._lock()
self.kill_good = True
@@ -178,6 +186,7 @@ class file(config.file):
else:
pat += c
raise error.general('invalid exe filter: %s' % (f))
+ return exe
def _output_length(self):
self._lock()
@@ -203,7 +212,7 @@ class file(config.file):
console_trace = trace = self.exe_trace('console')
if not self.opts.dry_run():
if data[0] == 'stdio':
- self.console = console.stdio(trace = console_trace)
+ self.console = tester.rt.console.stdio(trace = console_trace)
elif data[0] == 'tty':
if len(data) < 2 or len(data) >3:
raise error.general(self._name_line_msg('no tty configuration provided'))
@@ -211,38 +220,36 @@ class file(config.file):
settings = data[2]
else:
settings = None
- self.console = console.tty(data[1],
- output = self.capture,
- setup = settings,
- trace = console_trace)
+ self.console = tester.rt.console.tty(data[1],
+ output = self.capture,
+ setup = settings,
+ trace = console_trace)
else:
raise error.general(self._name_line_msg('invalid console type'))
- def _dir_execute(self, data, total, index, exe, bsp_arch, bsp):
- self.process = execute.execute(output = self.capture)
- if self.console:
- self.console.open()
+ def _dir_execute(self, data, total, index, rexe, bsp_arch, bsp):
+ self.process = tester.rt.exe.exe(bsp_arch, bsp, trace = self.exe_trace('exe'))
if not self.in_error:
- self.capture_console('run: %s' % (' '.join(data)))
+ if self.console:
+ self.console.open()
if not self.opts.dry_run():
- ec, proc = self.process.open(data,
- timeout = (int(self.expand('%{timeout}')),
- self._timeout))
- self._lock()
- if not (self.kill_good or self.defined('exe_ignore_ret')) and ec > 0:
- self._error('execute failed: %s: exit-code:%d' % (' '.join(data), ec))
- elif self.timedout:
- self.process.kill()
- self._unlock()
+ self.process.open(data,
+ ignore_exit_code = self.defined('exe_ignore_ret'),
+ output = self.capture,
+ console = self.capture_console,
+ timeout = (int(self.expand('%{timeout}')),
+ int(self.expand('%{max_test_period}')),
+ self._timeout,
+ self._test_too_long))
if self.console:
self.console.close()
def _dir_gdb(self, data, total, index, exe, bsp_arch, bsp):
if len(data) < 3 or len(data) > 4:
raise error.general('invalid %gdb arguments')
- self.process = gdb.gdb(bsp_arch, bsp,
- trace = self.exe_trace('gdb'),
- mi_trace = self.exe_trace('gdb-mi'))
+ self.process = tester.rt.gdb.gdb(bsp_arch, bsp,
+ trace = self.exe_trace('gdb'),
+ mi_trace = self.exe_trace('gdb-mi'))
script = self.expand('%%{%s}' % data[2])
if script:
script = [l.strip() for l in script.splitlines()]
@@ -254,7 +261,10 @@ class file(config.file):
script = script,
output = self.capture,
gdb_console = self.capture_console,
- timeout = int(self.expand('%{timeout}')))
+ timeout = (int(self.expand('%{timeout}')),
+ int(self.expand('%{max_test_period}')),
+ self._timeout,
+ self._test_too_long))
if self.console:
self.console.close()
@@ -267,8 +277,8 @@ class file(config.file):
raise error.general('invalid %tftp port')
self.kill_on_end = True
if not self.opts.dry_run():
- self.process = tftp.tftp(bsp_arch, bsp,
- trace = self.exe_trace('tftp'))
+ self.process = tester.rt.tftp.tftp(bsp_arch, bsp,
+ trace = self.exe_trace('tftp'))
if not self.in_error:
if self.console:
self.console.open()
@@ -277,7 +287,9 @@ class file(config.file):
output_length = self._output_length,
console = self.capture_console,
timeout = (int(self.expand('%{timeout}')),
- self._timeout))
+ int(self.expand('%{max_test_period}')),
+ self._timeout,
+ self._test_too_long))
if self.console:
self.console.close()
diff --git a/tester/rt/console.py b/tester/rt/console.py
index ef35a0e..7e09bb9 100644
--- a/tester/rt/console.py
+++ b/tester/rt/console.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2014 Chris Johns (chrisj@rtems.org)
+# Copyright 2013-2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -41,13 +41,13 @@ import time
from rtemstoolkit import path
-import telnet
+import tester.rt.telnet
#
# Not available on Windows. Not sure what this means.
#
if os.name != 'nt':
- import stty
+ import tester.rt.stty as stty
else:
stty = None
@@ -127,7 +127,7 @@ class tty(console):
if stty and path.exists(self.dev):
self.tty = stty.tty(self.dev)
else:
- self.tty = telnet.tty(self.dev)
+ self.tty = tester.rt.telnet.tty(self.dev)
self.tty.set(self.setup)
self.tty.on()
self.read_thread = threading.Thread(target = _readthread,
diff --git a/tester/rt/coverage.py b/tester/rt/coverage.py
index 31e2cd7..0f225a9 100644
--- a/tester/rt/coverage.py
+++ b/tester/rt/coverage.py
@@ -48,8 +48,6 @@ from rtemstoolkit import macros
from rtemstoolkit import version
-import options
-
class summary:
def __init__(self, p_summary_dir):
self.summary_file_path = path.join(p_summary_dir, 'summary.txt')
diff --git a/tester/rt/exe.py b/tester/rt/exe.py
new file mode 100644
index 0000000..5655073
--- /dev/null
+++ b/tester/rt/exe.py
@@ -0,0 +1,172 @@
+# SPDX-License-Identifier: BSD-2-Clause
+'''Executable test target.'''
+
+# Copyright (C) 2013-2020 Chris Johns (chrisj@rtems.org)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from __future__ import print_function
+
+import datetime
+import os
+import threading
+import time
+
+from rtemstoolkit import execute
+
+
+class exe(object):
+ '''RTEMS Testing EXE base.'''
+
+ # pylint: disable=useless-object-inheritance
+ # pylint: disable=too-many-instance-attributes
+
+ def __init__(self, bsp_arch, bsp, trace=False):
+ self.trace = trace
+ self.lock_trace = False
+ self.lock_locked = None
+ self.lock = threading.RLock()
+ self.bsp = bsp
+ self.bsp_arch = bsp_arch
+ self.output = None
+ self.output_length = 0
+ self.process = None
+ self.ecode = None
+ self.output = None
+ self.output_buffer = ''
+ self.console = None
+ self.timeout = None
+ self.test_too_long = None
+ self.kill_good = True
+
+ def _lock(self, msg):
+ if self.lock_trace:
+ print('|[ LOCK:%s ]|' % (msg))
+ self.lock_locked = datetime.datetime.now()
+ self.lock.acquire()
+
+ def _unlock(self, msg):
+ if self.lock_trace:
+ period = datetime.datetime.now() - self.lock_locked
+ print('|] UNLOCK:%s [| : %s' % (msg, period))
+ self.lock.release()
+
+ def _capture(self, text):
+ self._lock('_capture')
+ self.output_length += len(text)
+ self._unlock('_capture')
+ if self.output is not None:
+ self.output(text)
+
+ def _timeout(self):
+ self._kill()
+ if self.timeout is not None:
+ self.timeout()
+
+ def _test_too_long(self):
+ self._kill()
+ if self.test_too_long is not None:
+ self.test_too_long()
+
+ def _kill(self):
+ self._lock('_kill')
+ self.kill_good = True
+ self._unlock('_kill')
+ if self.process:
+ # pylint: disable=bare-except
+ try:
+ self.process.kill()
+ except:
+ pass
+ self.process = None
+
+ def _execute(self, args):
+ '''Thread to execute the test and to wait for it to finish.'''
+ # pylint: disable=unused-variable
+ cmds = args
+ if self.console is not None:
+ self.console('exe: %s' % (' '.join(cmds)))
+ ecode, proc = self.process.open(cmds)
+ if self.trace:
+ print('gdb done', ecode)
+ self._lock('_execute')
+ self.ecode = ecode
+ self.process = None
+ self._unlock('_execute')
+
+ def _monitor(self, timeout):
+ output_length = self.output_length
+ step = 0.25
+ period = timeout[0]
+ seconds = timeout[1]
+ while self.process and period > 0 and seconds > 0:
+ current_length = self.output_length
+ if output_length != current_length:
+ period = timeout[0]
+ output_length = current_length
+ if seconds < step:
+ seconds = 0
+ else:
+ seconds -= step
+ if period < step:
+ step = period
+ period = 0
+ else:
+ period -= step
+ self._unlock('_monitor')
+ time.sleep(step)
+ self._lock('_monitor')
+ if self.process is not None:
+ if period == 0:
+ self._timeout()
+ elif seconds == 0:
+ self._test_too_long()
+
+ def open(self, command, ignore_exit_code, output, console, timeout):
+ '''Open the execute test run'''
+ # pylint: disable=too-many-arguments
+ self._lock('_open')
+ self.timeout = timeout[2]
+ self.test_too_long = timeout[3]
+ try:
+ cmds = execute.arg_list(command)
+ self.output = output
+ self.console = console
+ self.process = execute.execute(output=self._capture)
+ exec_thread = threading.Thread(target=self._execute, args=[cmds])
+ exec_thread.start()
+ self._monitor(timeout)
+ if self.ecode is not None and \
+ not (self.kill_good or ignore_exit_code) and self.ecode > 0:
+ if self.output:
+ self.output('*** TARGET ERROR %d %s ***' %
+ (self.ecode, os.strerror(self.ecode)))
+ finally:
+ self._unlock('_open')
+
+ def kill(self):
+ '''Kill the test run.'''
+ self._lock('_kill')
+ try:
+ self._kill()
+ finally:
+ self._unlock('_kill')
diff --git a/tester/rt/gdb.py b/tester/rt/gdb.py
index 15a3862..4abbca5 100644
--- a/tester/rt/gdb.py
+++ b/tester/rt/gdb.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2014 Chris Johns (chrisj@rtems.org)
+# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -34,6 +34,7 @@
from __future__ import print_function
+import datetime
import os
try:
import Queue
@@ -42,48 +43,55 @@ except ImportError:
import queue
import sys
import threading
+import time
from rtemstoolkit import error
from rtemstoolkit import execute
from rtemstoolkit import options
from rtemstoolkit import path
-import console
-import pygdb
+import tester.rt.pygdb
class gdb(object):
'''RTEMS Testing GDB base.'''
def __init__(self, bsp_arch, bsp, trace = False, mi_trace = False):
- self.session = pygdb.mi_parser.session()
+ self.session = tester.rt.pygdb.mi_parser.session()
self.trace = trace
self.mi_trace = mi_trace
self.lock_trace = False
+ self.lock_locked = None
self.lock = threading.RLock()
self.script = None
self.script_line = 0
self.bsp = bsp
self.bsp_arch = bsp_arch
self.output = None
+ self.output_length = 0
self.gdb_console = None
self.input = queue.Queue()
self.commands = queue.Queue()
self.process = None
+ self.ecode = None
self.state = {}
self.running = False
self.breakpoints = {}
self.output = None
self.output_buffer = ''
+ self.timeout = None
+ self.test_too_long = None
self.lc = 0
def _lock(self, msg):
if self.lock_trace:
print('|[ LOCK:%s ]|' % (msg))
+ self.lock_locked = datetime.datetime.now()
self.lock.acquire()
def _unlock(self, msg):
if self.lock_trace:
- print('|] UNLOCK:%s [|' % (msg))
+ period = datetime.datetime.now() - self.lock_locked
+ print('|] UNLOCK:%s [| : %s' % (msg, period))
self.lock.release()
def _put(self, text):
@@ -149,13 +157,14 @@ class gdb(object):
return False
def _timeout(self):
- self._lock('_timeout')
- try:
- if self.output:
- self.output('*** TIMEOUT TIMEOUT')
- self._gdb_quit(backtrace = True)
- finally:
- self._unlock('_timeout')
+ self._stop()
+ if self.timeout is not None:
+ self.timeout()
+
+ def _test_too_long(self):
+ self._stop()
+ if self.test_too_long is not None:
+ self.test_too_long()
def _cleanup(self, proc):
self._lock('_cleanup')
@@ -177,10 +186,73 @@ class gdb(object):
finally:
self._unlock('_gdb_quit')
+ def _stop(self):
+ self._gdb_quit(backtrace=True)
+ seconds = 5
+ step = 0.1
+ while self.process and seconds > 0:
+ if seconds > step:
+ seconds -= step
+ else:
+ seconds = 0
+ self._unlock('_stop')
+ time.sleep(step)
+ self._lock('_stop')
+ if self.process and seconds == 0:
+ self._kill()
+
+ def _kill(self):
+ if self.process:
+ self.process.kill()
+ self.process = None
+
+ def _execute_gdb(self, args):
+ '''Thread to execute GDB and to wait for it to finish.'''
+ cmds = args
+ self.gdb_console('gdb: %s' % (' '.join(cmds)))
+ ecode, proc = self.process.open(cmds)
+ if self.trace:
+ print('gdb done', ecode)
+ self._lock('_execute_gdb')
+ self.ecode = ecode
+ self.process = None
+ self.running = False
+ self._unlock('_execute_gdb')
+
+ def _monitor(self, timeout):
+ output_length = self.output_length
+ step = 0.25
+ period = timeout[0]
+ seconds = timeout[1]
+ while self.process and period > 0 and seconds > 0:
+ current_length = self.output_length
+ if output_length != current_length:
+ period = timeout[0]
+ output_length = current_length
+ if seconds < step:
+ seconds = 0
+ else:
+ seconds -= step
+ if period < step:
+ step = period
+ period = 0
+ else:
+ period -= step
+ self._unlock('_monitor')
+ time.sleep(step)
+ self._lock('_monitor')
+ if self.process is not None:
+ if period == 0:
+ self._timeout()
+ elif seconds == 0:
+ self._test_too_long()
+
def open(self, command, executable,
- output, gdb_console, script = None, tty = None,
- timeout = 300):
+ output, gdb_console, timeout,
+ script = None, tty = None):
self._lock('_open')
+ self.timeout = timeout[2]
+ self.test_too_long = timeout[3]
try:
cmds = execute.arg_list(command) + ['-i=mi',
'--nx',
@@ -192,32 +264,25 @@ class gdb(object):
self.output = output
self.gdb_console = gdb_console
self.script = script
- self.running = False
self.process = execute.execute(output = self._reader,
input = self._writer,
cleanup = self._cleanup)
- finally:
- self._unlock('_open')
- self.gdb_console('gdb: %s' % (' '.join(cmds)))
- ec, proc = self.process.open(cmds, timeout = (timeout, self._timeout))
- if self.trace:
- print('gdb done', ec)
- if ec > 0:
- raise error.general('gdb exec: %s: %s' % (cmds[0], os.strerror(ec)))
- self._lock('_open')
- try:
- self.process = None
+ exec_thread = threading.Thread(target=self._execute_gdb,
+ args=[cmds])
+ exec_thread.start()
+ self._monitor(timeout)
+ if self.ecode is not None and self.ecode > 0:
+ raise error.general('gdb exec: %s: %s' % (cmds[0],
+ os.strerror(self.ecode)))
finally:
self._unlock('_open')
def kill(self):
- self._lock('_open')
+ self._lock('_kill')
try:
- if self.process:
- self.process.kill()
- self.process = None
+ self._kill()
finally:
- self._unlock('_open')
+ self._unlock('_kill')
def gdb_expect(self):
if self.trace:
@@ -228,7 +293,6 @@ class gdb(object):
else:
if self.script_line == 0:
self._put('-gdb-set confirm no')
- self._put('-data-list-register-names')
line = self.script[self.script_line]
self.script_line += 1
self._put(line)
@@ -279,8 +343,9 @@ class gdb(object):
if last_lf >= 0:
lines = self.output_buffer[:last_lf]
if self.trace:
- print('/// console output')
+ print('/// console output: ', len(lines))
for line in lines.splitlines():
+ self.output_length += len(line)
self.output(line)
self.output_buffer = self.output_buffer[last_lf + 1:]
except:
@@ -290,7 +355,8 @@ class gdb(object):
self.output(line)
if __name__ == "__main__":
- stdtty = console.save()
+ import tester.rt.console
+ stdtty = tester.rt.console.save()
try:
def output(text):
print(']', text)
@@ -310,7 +376,7 @@ if __name__ == "__main__":
g = gdb('sparc', 'sis', mi_trace = True)
g.open('sparc-rtems4.11-gdb', executable, output, gdb_console, script)
except:
- console.restore(stdtty)
+ tester.rt.console.restore(stdtty)
raise
finally:
- console.restore(stdtty)
+ tester.rt.console.restore(stdtty)
diff --git a/tester/rt/report.py b/tester/rt/report.py
index 06acb21..0e19edc 100644
--- a/tester/rt/report.py
+++ b/tester/rt/report.py
@@ -63,6 +63,7 @@ class report(object):
self.indeterminate = 0
self.benchmark = 0
self.timeouts = 0
+ self.test_too_long = 0
self.invalids = 0
self.wrong_version = 0
self.wrong_build = 0
@@ -79,6 +80,7 @@ class report(object):
msg += 'Indeterminate: %*d%s' % (self.total_len, self.self.indeterminate, os.linesep)
msg += 'Benchmark: %*d%s' % (self.total_len, self.self.benchmark, os.linesep)
msg += 'Timeout: %*d%s' % (self.total_len, self.timeouts, os.linesep)
+ msg += 'Test too long: %*d%s' % (self.total_len, self.test_too_long, os.linesep)
msg += 'Invalid: %*d%s' % (self.total_len, self.invalids, os.linesep)
msg += 'Wrong Version %*d%s' % (self.total_len, self.wrong_version, os.linesep)
msg += 'Wrong Build %*d%s' % (self.total_len, self.wrong_build, os.linesep)
@@ -87,7 +89,7 @@ class report(object):
def start(self, index, total, name, executable, bsp_arch, bsp, show_header):
header = '[%*d/%*d] p:%-*d f:%-*d u:%-*d e:%-*d I:%-*d B:%-*d ' \
- 't:%-*d i:%-*d W:%-*d | %s/%s: %s' % \
+ 't:%-*d L:%-*d i:%-*d W:%-*d | %s/%s: %s' % \
(len(str(total)), index,
len(str(total)), total,
len(str(total)), self.passed,
@@ -97,6 +99,7 @@ class report(object):
len(str(total)), self.indeterminate,
len(str(total)), self.benchmark,
len(str(total)), self.timeouts,
+ len(str(total)), self.test_too_long,
len(str(total)), self.invalids,
len(str(total)), self.wrong_version + self.wrong_build + self.wrong_tools,
bsp_arch,
@@ -123,11 +126,13 @@ class report(object):
def end(self, name, output, output_prefix):
start = False
end = False
+ fatal = False
state = None
version = None
build = None
tools = None
timeout = False
+ test_too_long = False
prefixed_output = []
for line in output:
if line[0] == output_prefix:
@@ -137,12 +142,18 @@ class report(object):
start = True
elif line[1][4:].startswith('END OF '):
end = True
+ elif line[1][4:].startswith('FATAL'):
+ fatal = True
elif banner.startswith('TIMEOUT TIMEOUT'):
timeout = True
+ elif banner.startswith('TEST TOO LONG'):
+ test_too_long = True
elif banner.startswith('TEST VERSION:'):
version = banner[13:].strip()
elif banner.startswith('TEST STATE:'):
- state = banner[11:].strip()
+ # Depending on the RTESM version '-' or '_' is used in
+ # test state strings. Normalize it to '_'.
+ state = banner[11:].strip().replace('-', '_')
elif banner.startswith('TEST BUILD:'):
build = ','.join(banner[11:].strip().split(' '))
elif banner.startswith('TEST TOOLS:'):
@@ -164,27 +175,33 @@ class report(object):
self.config['version'] = version
else:
if version != self.config['version']:
- state = 'WRONG-VERSION'
+ state = 'WRONG_VERSION'
if build:
if 'build' not in self.config:
self.config['build'] = build
else:
if build != self.config['build']:
- state = 'WRONG-BUILD'
+ state = 'WRONG_BUILD'
if tools:
if 'tools' not in self.config:
self.config['tools'] = tools
else:
if tools != self.config['tools']:
- state = 'WRONG-TOOLS'
- if state is None or state == 'EXPECTED-PASS':
+ state = 'WRONG_TOOLS'
+ if state is None or state == 'EXPECTED_PASS':
if start and end:
- if state is None or state == 'EXPECTED-PASS':
+ if state is None or state == 'EXPECTED_PASS':
status = 'passed'
self.passed += 1
+ elif fatal:
+ status = 'fatal-error'
+ self.failed += 1
elif timeout:
status = 'timeout'
self.timeouts += 1
+ elif test_too_long:
+ status = 'test-too-long'
+ self.test_too_long += 1
elif start:
if not end:
status = 'failed'
@@ -218,13 +235,13 @@ class report(object):
elif state == 'BENCHMARK':
status = 'benchmark'
self.benchmark += 1
- elif state == 'WRONG-VERSION':
+ elif state == 'WRONG_VERSION':
status = 'wrong-version'
self.wrong_version += 1
- elif state == 'WRONG-BUILD':
+ elif state == 'WRONG_BUILD':
status = 'wrong-build'
self.wrong_build += 1
- elif state == 'WRONG-TOOLS':
+ elif state == 'WRONG_TOOLS':
status = 'wrong-tools'
self.wrong_tools += 1
else:
@@ -238,7 +255,7 @@ class report(object):
return status
def log(self, name, mode):
- status_fails = ['failed', 'timeout', 'invalid',
+ status_fails = ['failed', 'timeout', 'test-too-long', 'invalid',
'wrong-version', 'wrong-build', 'wrong-tools']
if mode != 'none':
self.lock.acquire()
@@ -271,8 +288,9 @@ class report(object):
def score_card(self, mode = 'full'):
if mode == 'short':
wrongs = self.wrong_version + self.wrong_build + self.wrong_tools
- return 'Passed:%d Failed:%d Timeout:%d Invalid:%d Wrong:%d' % \
- (self.passed, self.failed, self.timeouts, self.invalids, wrongs)
+ return 'Passed:%d Failed:%d Timeout:%d Test-Too-long:%d Invalid:%d Wrong:%d' % \
+ (self.passed, self.failed, self.timeouts, self.test_too_long,
+ self.invalids, wrongs)
elif mode == 'full':
l = []
l += ['Passed: %*d' % (self.total_len, self.passed)]
@@ -282,6 +300,7 @@ class report(object):
l += ['Indeterminate: %*d' % (self.total_len, self.indeterminate)]
l += ['Benchmark: %*d' % (self.total_len, self.benchmark)]
l += ['Timeout: %*d' % (self.total_len, self.timeouts)]
+ l += ['Test too long: %*d' % (self.total_len, self.test_too_long)]
l += ['Invalid: %*d' % (self.total_len, self.invalids)]
l += ['Wrong Version: %*d' % (self.total_len, self.wrong_version)]
l += ['Wrong Build: %*d' % (self.total_len, self.wrong_build)]
@@ -302,6 +321,7 @@ class report(object):
if self.failed:
l += ['Failures:']
l += show_state(self.results, 'failed', self.name_max_len)
+ l += show_state(self.results, 'fatal-error', self.name_max_len)
if self.user_input:
l += ['User Input:']
l += show_state(self.results, 'user-input', self.name_max_len)
@@ -317,6 +337,9 @@ class report(object):
if self.timeouts:
l += ['Timeouts:']
l += show_state(self.results, 'timeout', self.name_max_len)
+ if self.test_too_long:
+ l += ['Test too long:']
+ l += show_state(self.results, 'test-too-long', self.name_max_len)
if self.invalids:
l += ['Invalid:']
l += show_state(self.results, 'invalid', self.name_max_len)
diff --git a/tester/rt/run.py b/tester/rt/run.py
index ff95091..ea32a23 100644
--- a/tester/rt/run.py
+++ b/tester/rt/run.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2017 Chris Johns (chrisj@rtems.org)
+# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -42,11 +42,11 @@ from rtemstoolkit import path
from rtemstoolkit import stacktraces
from rtemstoolkit import version
-import bsps
-import config
-import console
-import options
-import report
+import tester.rt.bsps
+import tester.rt.config
+import tester.rt.console
+import tester.rt.options
+import tester.rt.report
class test(object):
def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
@@ -68,7 +68,7 @@ class test(object):
if not path.isdir(rtems_tools_bin):
raise error.general('cannot find RTEMS tools path: %s' % (rtems_tools_bin))
self.opts.defaults['rtems_tools'] = rtems_tools_bin
- self.config = config.file(index, total, self.report, self.bsp_config, self.opts, '')
+ self.config = tester.rt.config.file(index, total, self.report, self.bsp_config, self.opts, '')
def run(self):
if self.config:
@@ -94,9 +94,9 @@ def list_bsps(opts):
log.notice(' %s' % (path.basename(bsp[:-3])))
raise error.exit()
-def run(args, command_path = None):
+def run(args):
tests = []
- stdtty = console.save()
+ stdtty = tester.rt.console.save()
opts = None
default_exefilter = '*.exe'
try:
@@ -106,12 +106,10 @@ def run(args, command_path = None):
'--list-bsps': 'List the supported BSPs',
'--debug-trace': 'Debug trace based on specific flags',
'--stacktrace': 'Dump a stack trace on a user termination (^C)' }
- opts = options.load(args,
- optargs = optargs,
- command_path = command_path)
+ opts = tester.rt.options.load(args, optargs = optargs)
log.notice('RTEMS Testing - Run, %s' % (version.string()))
if opts.find_arg('--list-bsps'):
- bsps.list(opts)
+ tester.rt.bsps.list(opts)
opts.log_info()
log.output('Host: ' + host.label(mode = 'all'))
debug_trace = opts.find_arg('--debug-trace')
@@ -133,13 +131,13 @@ def run(args, command_path = None):
bsp = opts.find_arg('--rtems-bsp')
if bsp is None or len(bsp) != 2:
raise error.general('RTEMS BSP not provided or an invalid option')
- bsp = config.load(bsp[1], opts)
+ bsp = tester.rt.config.load(bsp[1], opts)
bsp_config = opts.defaults.expand(opts.defaults['tester'])
executables = find_executables(opts.params())
if len(executables) != 1:
raise error.general('one executable required, found %d' % (len(executables)))
opts.defaults['test_disable_header'] = '1'
- reports = report.report(1)
+ reports = tester.rt.report.report(1)
start_time = datetime.datetime.now()
opts.defaults['exe_trace'] = debug_trace
tst = test(1, 1, reports, executables[0], rtems_tools, bsp, bsp_config, opts)
@@ -165,7 +163,7 @@ def run(args, command_path = None):
log.notice('abort: user terminated')
sys.exit(1)
finally:
- console.restore(stdtty)
+ tester.rt.console.restore(stdtty)
sys.exit(0)
if __name__ == "__main__":
diff --git a/tester/rt/test.py b/tester/rt/test.py
index da0a11e..66f1756 100644
--- a/tester/rt/test.py
+++ b/tester/rt/test.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2018 Chris Johns (chrisj@rtems.org)
+# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -50,12 +50,12 @@ from rtemstoolkit import stacktraces
from rtemstoolkit import version
from rtemstoolkit import check
-import bsps
-import config
-import console
-import options
-import report
-import coverage
+import tester.rt.bsps
+import tester.rt.config
+import tester.rt.console
+import tester.rt.options
+import tester.rt.report
+import tester.rt.coverage
class log_capture(object):
def __init__(self):
@@ -108,7 +108,7 @@ class test(object):
if not path.isdir(rtems_tools_bin):
raise error.general('cannot find RTEMS tools path: %s' % (rtems_tools_bin))
self.opts.defaults['rtems_tools'] = rtems_tools_bin
- self.config = config.file(index, total, self.report, self.bsp_config, self.opts)
+ self.config = tester.rt.config.file(index, total, self.report, self.bsp_config, self.opts)
def run(self):
if self.config:
@@ -178,7 +178,7 @@ def find_executables(paths, glob):
executables = [e for e in executables if not norun.match(e)]
return sorted(executables)
-def report_finished(reports, report_mode, reporting, finished, job_trace):
+def report_finished(reports, log_mode, reporting, finished, job_trace):
processing = True
while processing:
processing = False
@@ -192,7 +192,7 @@ def report_finished(reports, report_mode, reporting, finished, job_trace):
'reporting',
reporting))
processing = True
- reports.log(tst.executable, report_mode)
+ reports.log(tst.executable, log_mode)
reported += [tst]
reporting += 1
finished[:] = [t for t in finished if t not in reported]
@@ -217,17 +217,268 @@ def killall(tests):
for test in tests:
test.kill()
-def run(args, command_path = None):
+
+def generate_json_report(args, reports, start_time, end_time,
+ total, json_file):
+ import json
+ import sys
+ json_log = {}
+ json_log['Command Line'] = " ".join(args)
+ json_log['Python'] = sys.version.replace('\n', '')
+ json_log['test_groups'] = []
+ json_log['Host'] = host.label(mode='all')
+ json_log['summary'] = {}
+ json_log['summary']['passed_count'] = reports.passed
+ json_log['summary']['failed_count'] = reports.failed
+ json_log['summary']['user-input_count'] = reports.user_input
+ json_log['summary']['expected-fail_count'] = reports.expected_fail
+ json_log['summary']['indeterminate_count'] = reports.indeterminate
+ json_log['summary']['benchmark_count'] = reports.benchmark
+ json_log['summary']['timeout_count'] = reports.timeouts
+ json_log['summary']['test-too-long_count'] = reports.test_too_long
+ json_log['summary']['invalid_count'] = reports.invalids
+ json_log['summary']['wrong-version_count'] = reports.wrong_version
+ json_log['summary']['wrong-build_count'] = reports.wrong_build
+ json_log['summary']['wrong-tools_count'] = reports.wrong_tools
+ json_log['summary']['total_count'] = reports.total
+ time_delta = end_time - start_time
+ json_log['summary']['average_test_time'] = str(time_delta / total)
+ json_log['summary']['testing_time'] = str(time_delta)
+
+ result_types = [
+ 'failed', 'user-input', 'expected-fail', 'indeterminate',
+ 'benchmark', 'timeout', 'test-too-long', 'invalid', 'wrong-version',
+ 'wrong-build', 'wrong-tools'
+ ]
+ json_results = {}
+ for result_type in result_types:
+ json_log['summary'][result_type] = []
+
+ # collate results for JSON log
+ for name in reports.results:
+ result_type = reports.results[name]['result']
+ # map fatal-error on to failed since the report adds both to the failed count
+ if result_type == "fatal-error":
+ result_type = "failed"
+ test_parts = name.split("/")
+ test_category = test_parts[-2]
+ test_name = test_parts[-1]
+ if result_type != 'passed':
+ json_log['summary'][result_type].append(test_name)
+ if test_category not in json_results:
+ json_results[test_category] = []
+ json_result = {}
+ # remove the file extension
+ json_result["name"] = test_name.split('.')[0]
+ json_result["result"] = result_type
+ if result_type == "failed" or result_type == "timeout":
+ json_result["output"] = reports.results[name]["output"]
+ json_results[test_category].append(json_result)
+
+ # convert results to a better format for report generation
+ sorted_keys = sorted(json_results.keys())
+ for i in range(len(sorted_keys)):
+ results_log = {}
+ results_log["index"] = i + 1
+ results_log["name"] = sorted_keys[i]
+ results_log["results"] = json_results[sorted_keys[i]]
+ json_log["test_groups"].append(results_log)
+
+ # write out JSON log
+ with open(json_file, 'w') as outfile:
+ json.dump(json_log, outfile, sort_keys=True, indent=4)
+
+
+def generate_junit_report(args, reports, start_time, end_time,
+ total, junit_file):
+
+ from junit_xml import TestSuite, TestCase
+ import sys
+ junit_log = []
+
+ junit_prop = {}
+ junit_prop['Command Line'] = ' '.join(args)
+ junit_prop['Python'] = sys.version.replace('\n', '')
+ junit_prop['test_groups'] = []
+ junit_prop['Host'] = host.label(mode = 'all')
+ junit_prop['passed_count'] = reports.passed
+ junit_prop['failed_count'] = reports.failed
+ junit_prop['user-input_count'] = reports.user_input
+ junit_prop['expected-fail_count'] = reports.expected_fail
+ junit_prop['indeterminate_count'] = reports.indeterminate
+ junit_prop['benchmark_count'] = reports.benchmark
+ junit_prop['timeout_count'] = reports.timeouts
+ junit_prop['test-too-long_count'] = reports.test_too_long
+ junit_prop['invalid_count'] = reports.invalids
+ junit_prop['wrong-version_count'] = reports.wrong_version
+ junit_prop['wrong-build_count'] = reports.wrong_build
+ junit_prop['wrong-tools_count'] = reports.wrong_tools
+ junit_prop['total_count'] = reports.total
+ time_delta = end_time - start_time
+ junit_prop['average_test_time'] = str(time_delta / total)
+ junit_prop['testing_time'] = str(time_delta)
+
+ for name in reports.results:
+ result_type = reports.results[name]['result']
+ test_parts = name.split('/')
+ test_category = test_parts[-2]
+ test_name = test_parts[-1]
+
+ junit_result = TestCase(test_name.split('.')[0])
+ junit_result.category = test_category
+ if result_type == 'failed' or result_type == 'timeout':
+ junit_result.add_failure_info(None, reports.results[name]['output'], result_type)
+
+ junit_log.append(junit_result)
+
+ ts = TestSuite('RTEMS Test Suite', junit_log)
+ ts.properties = junit_prop
+ ts.hostname = host.label(mode = 'all')
+
+ # write out junit log
+ with open(junit_file, 'w') as f:
+ TestSuite.to_file(f, [ts], prettyprint = True)
+
+
+def generate_yaml_report(args, reports, start_time, end_time,
+ total, yaml_file):
+ """ Generates a YAML file containing information about the test run,
+ including all test outputs """
+
+ import yaml
+
+ def format_output(output_list):
+ return "\n".join(output_list).replace("] ", '').replace('=> ', '')
+
+ yaml_log = {}
+ yaml_log['command-line'] = args
+ yaml_log['host'] = host.label(mode='all')
+ yaml_log['python'] = sys.version.replace('\n', '')
+ yaml_log['summary'] = {}
+ yaml_log['summary']['passed-count'] = reports.passed
+ yaml_log['summary']['failed-count'] = reports.failed
+ yaml_log['summary']['user-input-count'] = reports.user_input
+ yaml_log['summary']['expected-fail-count'] = reports.expected_fail
+ yaml_log['summary']['indeterminate-count'] = reports.indeterminate
+ yaml_log['summary']['benchmark-count'] = reports.benchmark
+ yaml_log['summary']['timeout-count'] = reports.timeouts
+ yaml_log['summary']['test-too-long_count'] = reports.test_too_long
+ yaml_log['summary']['invalid-count'] = reports.invalids
+ yaml_log['summary']['wrong-version-count'] = reports.wrong_version
+ yaml_log['summary']['wrong-build-count'] = reports.wrong_build
+ yaml_log['summary']['wrong-tools-count'] = reports.wrong_tools
+ yaml_log['summary']['total-count'] = reports.total
+ time_delta = end_time - start_time
+ yaml_log['summary']['average-test-time'] = str(time_delta / total)
+ yaml_log['summary']['testing-time'] = str(time_delta)
+
+ result_types = [
+ 'failed', 'user-input', 'expected-fail', 'indeterminate',
+ 'benchmark', 'timeout', 'test-too-long', 'invalid', 'wrong-version',
+ 'wrong-build', 'wrong-tools'
+ ]
+ for result_type in result_types:
+ yaml_log['summary'][result_type] = []
+
+ result_element = {}
+ yaml_log['outputs'] = []
+
+ # process output of each test
+ for exe_name in reports.results:
+ test_parts = exe_name.split("/")
+ test_name = test_parts[-1]
+ result_element['executable-name'] = test_name
+ result_element['executable-sha512'] = get_hash512(exe_name)
+ result_element['execution-start'] = reports.results[exe_name]['start'].isoformat()
+ result_element['execution-end'] = reports.results[exe_name]['end'].isoformat()
+ date_diff = reports.results[exe_name]['end'] - reports.results[exe_name]['start']
+ result_element['execution-duration'] = str(date_diff)
+ result_element['execution-result'] = reports.results[exe_name]['result']
+ result_element['bsp'] = reports.results[exe_name]['bsp']
+ result_element['bsp-arch'] = reports.results[exe_name]['bsp_arch']
+ result_output = reports.results[exe_name]['output']
+
+ dbg_output = []
+ test_output = []
+ idxs_output = [] # store indices of given substrings
+ for elem in result_output:
+ if '=> ' in elem:
+ idxs_output.append(result_output.index(elem))
+ if '*** END' in elem:
+ idxs_output.append(result_output.index(elem))
+
+ if len(idxs_output) == 3: # test executed and has result
+ dbg_output = result_output[idxs_output[0]:idxs_output[1]]
+ dbg_output.append("=== Executed Test ===")
+ dbg_output = dbg_output + result_output[idxs_output[2]+1:len(result_output)]
+ test_output = result_output[idxs_output[1]:idxs_output[2]+1]
+ else:
+ dbg_output = result_output
+
+ result_element['debugger-output'] = format_output(dbg_output)
+ result_element['console-output'] = format_output(test_output)
+ yaml_log['outputs'].append(result_element)
+
+ result_type = reports.results[exe_name]['result']
+ # map "fatal-error" on to "failed"
+ if result_type == "fatal-error":
+ result_type = "failed"
+
+ if result_type != 'passed':
+ yaml_log['summary'][result_type].append(test_name)
+
+ result_element = {}
+
+ with open(yaml_file, 'w') as outfile:
+ yaml.dump(yaml_log, outfile, default_flow_style=False, allow_unicode=True)
+
+
+def get_hash512(exe):
+ """ returns SHA512 hash string of a given binary file passed as argument """
+ import hashlib
+
+ hash = hashlib.sha512()
+ with open(exe, "rb") as f:
+ for byte_block in iter(lambda: f.read(4096), b""):
+ hash.update(byte_block)
+ return hash.hexdigest()
+
+
+report_formatters = {
+ 'json': generate_json_report,
+ 'junit': generate_junit_report,
+ 'yaml': generate_yaml_report
+}
+
+
+def check_report_formats(report_formats, report_location):
+ if report_location is None:
+ raise error.general('report base path missing')
+ for report_format in report_formats:
+ if report_format not in report_formatters:
+ raise error.general('invalid RTEMS report formatter: %s'
+ % report_format)
+ if report_format == 'yaml':
+ try:
+ import yaml
+ except ImportError:
+ raise error.general('Please install PyYAML module. HINT: You can '
+ 'use pip to install it!')
+
+
+def run(args):
import sys
tests = []
- stdtty = console.save()
+ stdtty = tester.rt.console.save()
opts = None
default_exefilter = '*.exe'
try:
optargs = { '--rtems-tools': 'The path to the RTEMS tools',
'--rtems-bsp': 'The RTEMS BSP to run the test on',
'--user-config': 'Path to your local user configuration INI file',
- '--report-mode': 'Reporting modes, failures (default),all,none',
+ '--report-path': 'Report output base path (file extension will be added)',
+ '--report-format': 'Formats in which to report test results in addition to txt: json, yaml',
+ '--log-mode': 'Reporting modes, failures (default),all,none',
'--list-bsps': 'List the supported BSPs',
'--debug-trace': 'Debug trace based on specific flags (console,gdb,output,cov)',
'--filter': 'Glob that executables must match to run (default: ' +
@@ -235,9 +486,7 @@ def run(args, command_path = None):
'--stacktrace': 'Dump a stack trace on a user termination (^C)',
'--coverage': 'Perform coverage analysis of test executables.'}
mailer.append_options(optargs)
- opts = options.load(args,
- optargs = optargs,
- command_path = command_path)
+ opts = tester.rt.options.load(args, optargs = optargs)
mail = None
output = None
if opts.find_arg('--mail'):
@@ -251,9 +500,21 @@ def run(args, command_path = None):
else:
to_addr = 'build@rtems.org'
output = log_capture()
+ report_location = opts.find_arg('--report-path')
+ if report_location is not None:
+ report_location = report_location[1]
+
+ report_formats = opts.find_arg('--report-format')
+ if report_formats is not None:
+ if len(report_formats) != 2:
+ raise error.general('invalid RTEMS report formats option')
+ report_formats = report_formats[1].split(',')
+ check_report_formats(report_formats, report_location)
+ else:
+ report_formats = []
log.notice('RTEMS Testing - Tester, %s' % (version.string()))
if opts.find_arg('--list-bsps'):
- bsps.list(opts)
+ tester.rt.bsps.list(opts)
exe_filter = opts.find_arg('--filter')
if exe_filter:
exe_filter = exe_filter[1]
@@ -273,7 +534,7 @@ def run(args, command_path = None):
opts.defaults['exe_trace'] = debug_trace
job_trace = 'jobs' in debug_trace.split(',')
rtems_tools = opts.find_arg('--rtems-tools')
- if rtems_tools:
+ if rtems_tools is not None:
if len(rtems_tools) != 2:
raise error.general('invalid RTEMS tools option')
rtems_tools = rtems_tools[1]
@@ -282,36 +543,36 @@ def run(args, command_path = None):
bsp = opts.find_arg('--rtems-bsp')
if bsp is None or len(bsp) != 2:
raise error.general('RTEMS BSP not provided or an invalid option')
- bsp = config.load(bsp[1], opts)
+ bsp = tester.rt.config.load(bsp[1], opts)
bsp_config = opts.defaults.expand(opts.defaults['tester'])
coverage_enabled = opts.find_arg('--coverage')
if coverage_enabled:
cov_trace = 'cov' in debug_trace.split(',')
if len(coverage_enabled) == 2:
- coverage_runner = coverage.coverage_run(opts.defaults,
- executables,
- rtems_tools,
- symbol_set = coverage_enabled[1],
- trace = cov_trace)
+ coverage_runner = tester.rt.coverage.coverage_run(opts.defaults,
+ executables,
+ rtems_tools,
+ symbol_set = coverage_enabled[1],
+ trace = cov_trace)
else:
- coverage_runner = coverage.coverage_run(opts.defaults,
- executables,
- rtems_tools,
- trace = cov_trace)
- report_mode = opts.find_arg('--report-mode')
- if report_mode:
- if report_mode[1] != 'failures' and \
- report_mode[1] != 'all' and \
- report_mode[1] != 'none':
+ coverage_runner = tester.rt.coverage.coverage_run(opts.defaults,
+ executables,
+ rtems_tools,
+ trace = cov_trace)
+ log_mode = opts.find_arg('--log-mode')
+ if log_mode:
+ if log_mode[1] != 'failures' and \
+ log_mode[1] != 'all' and \
+ log_mode[1] != 'none':
raise error.general('invalid report mode')
- report_mode = report_mode[1]
+ log_mode = log_mode[1]
else:
- report_mode = 'failures'
+ log_mode = 'failures'
if len(executables) == 0:
raise error.general('no executables supplied')
start_time = datetime.datetime.now()
total = len(executables)
- reports = report.report(total)
+ reports = tester.rt.report.report(total)
reporting = 1
jobs = int(opts.jobs(opts.defaults['_ncpus']))
exe = 0
@@ -344,22 +605,26 @@ def run(args, command_path = None):
time.sleep(0.250)
if len(finished):
reporting = report_finished(reports,
- report_mode,
+ log_mode,
reporting,
finished,
job_trace)
finished_time = datetime.datetime.now()
- reporting = report_finished(reports, report_mode,
+ reporting = report_finished(reports, log_mode,
reporting, finished, job_trace)
if reporting < total:
log.warning('finished jobs does match: %d' % (reporting))
- report_finished(reports, report_mode, -1, finished, job_trace)
+ report_finished(reports, log_mode, -1, finished, job_trace)
reports.summary()
end_time = datetime.datetime.now()
average_time = 'Average test time: %s' % (str((end_time - start_time) / total))
total_time = 'Testing time : %s' % (str(end_time - start_time))
log.notice(average_time)
log.notice(total_time)
+ for report_format in report_formats:
+ report_formatters[report_format](args, reports, start_time,
+ end_time, total, '.'.join([report_location, report_format]))
+
if mail is not None and output is not None:
m_arch = opts.defaults.expand('%{arch}')
m_bsp = opts.defaults.expand('%{bsp}')
@@ -404,7 +669,7 @@ def run(args, command_path = None):
killall(tests)
sys.exit(1)
finally:
- console.restore(stdtty)
+ tester.rt.console.restore(stdtty)
sys.exit(0)
if __name__ == "__main__":
diff --git a/tester/rt/tftp.py b/tester/rt/tftp.py
index c91ae51..af5029a 100644
--- a/tester/rt/tftp.py
+++ b/tester/rt/tftp.py
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2017 Chris Johns (chrisj@rtems.org)
+# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -43,7 +43,7 @@ import sys
from rtemstoolkit import error
from rtemstoolkit import reraise
-import tftpy
+import tester.rt.tftpserver
class tftp(object):
'''RTEMS Testing TFTP base.'''
@@ -66,6 +66,7 @@ class tftp(object):
self.port = 0
self.exe = None
self.timeout = None
+ self.test_too_long = None
self.timer = None
self.running = False
self.finished = False
@@ -88,7 +89,8 @@ class tftp(object):
def _stop(self):
try:
if self.server:
- self.server.stop(now = True)
+ self.server.stop()
+ self.finished = True
except:
pass
@@ -101,9 +103,22 @@ class tftp(object):
def _timeout(self):
self._stop()
+ while self.running or not self.finished:
+ self._unlock('_timeout')
+ time.sleep(0.1)
+ self._lock('_timeout')
if self.timeout is not None:
self.timeout()
+ def _test_too_long(self):
+ self._stop()
+ while self.running or not self.finished:
+ self._unlock('_test_too_long')
+ time.sleep(0.1)
+ self._lock('_test_too_long')
+ if self.test_too_long is not None:
+ self.test_too_long()
+
def _exe_handle(self, req_file, raddress, rport):
self._lock('_exe_handle')
exe = self.exe
@@ -119,22 +134,21 @@ class tftp(object):
return None
def _listener(self):
- tftpy_log = logging.getLogger('tftpy')
- tftpy_log.setLevel(100)
+ self._lock('_listener')
+ exe = self.exe
+ self.exe = None
+ self._unlock('_listener')
+ self.server = tester.rt.tftpserver.tftp_server(host = 'all',
+ port = self.port,
+ timeout = 1,
+ forced_file = exe,
+ sessions = 1)
try:
- self.server = tftpy.TftpServer(tftproot = '.',
- dyn_file_func = self._exe_handle)
- except tftpy.TftpException as te:
- raise error.general('tftp: %s' % (str(te)))
- if self.server is not None:
- try:
- self.server.listen('0.0.0.0', self.port, 0.5)
- except tftpy.TftpException as te:
- raise error.general('tftp: %s' % (str(te)))
- except IOError as ie:
- if ie.errno == errno.EACCES:
- raise error.general('tftp: permissions error: check tftp server port')
- raise error.general('tftp: io error: %s' % (str(ie)))
+ self.server.start()
+ self.server.run()
+ except:
+ self.server.stop()
+ raise
def _runner(self):
self._lock('_runner')
@@ -146,9 +160,7 @@ class tftp(object):
except:
caught = sys.exc_info()
self._lock('_runner')
- self._init()
self.running = False
- self.finished = True
self.caught = caught
self._unlock('_runner')
@@ -162,29 +174,43 @@ class tftp(object):
self.console = console
self.port = port
self.exe = executable
- self.timeout = timeout[1]
+ if self.console:
+ self.console('tftp: exe: %s' % (executable))
+ self.timeout = timeout[2]
+ self.test_too_long = timeout[3]
self.listener = threading.Thread(target = self._runner,
name = 'tftp-listener')
self.listener.start()
- step = 0.5
+ step = 0.25
period = timeout[0]
+ seconds = timeout[1]
output_len = self.output_length()
- while not self.finished and period > 0:
+ while not self.finished and period > 0 and seconds > 0:
+ if not self.running and self.caught:
+ break
current_length = self.output_length()
if output_length != current_length:
period = timeout[0]
output_length = current_length
+ if seconds < step:
+ seconds = 0
+ else:
+ seconds -= step
if period < step:
+ step = period
period = 0
else:
period -= step
self._unlock('_open')
time.sleep(step)
self._lock('_open')
- if not self.finished and period == 0:
- self._timeout()
+ if not self.finished:
+ if period == 0:
+ self._timeout()
+ elif seconds == 0:
+ self._test_too_long()
caught = self.caught
- self.caught = None
+ self._init()
self._unlock('_open')
if caught is not None:
reraise.reraise(*caught)
diff --git a/tester/rt/tftpserver.py b/tester/rt/tftpserver.py
new file mode 100644
index 0000000..d733301
--- /dev/null
+++ b/tester/rt/tftpserver.py
@@ -0,0 +1,723 @@
+# SPDX-License-Identifier: BSD-2-Clause
+'''The TFTP Server handles a read only TFTP session.'''
+
+# Copyright (C) 2020 Chris Johns (chrisj@rtems.org)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from __future__ import print_function
+
+import argparse
+import os
+import socket
+import sys
+import time
+import threading
+
+try:
+ import socketserver
+except ImportError:
+ import SocketServer as socketserver
+
+from rtemstoolkit import error
+from rtemstoolkit import log
+from rtemstoolkit import version
+
+
+class tftp_session(object):
+ '''Handle the TFTP session packets initiated on the TFTP port (69).
+ '''
+ # pylint: disable=useless-object-inheritance
+ # pylint: disable=too-many-instance-attributes
+
+ opcodes = ['nul', 'RRQ', 'WRQ', 'DATA', 'ACK', 'ERROR', 'OACK']
+
+ OP_RRQ = 1
+ OP_WRQ = 2
+ OP_DATA = 3
+ OP_ACK = 4
+ OP_ERROR = 5
+ OP_OACK = 6
+
+ E_NOT_DEFINED = 0
+ E_FILE_NOT_FOUND = 1
+ E_ACCESS_VIOLATION = 2
+ E_DISK_FULL = 3
+ E_ILLEGAL_TFTP_OP = 4
+ E_UKNOWN_TID = 5
+ E_FILE_ALREADY_EXISTS = 6
+ E_NO_SUCH_USER = 7
+ E_NO_ERROR = 10
+
+ def __init__(self, host, port, base, forced_file, reader=None):
+ # pylint: disable=too-many-arguments
+ self.host = host
+ self.port = port
+ self.base = base
+ self.forced_file = forced_file
+ if reader is None:
+ self.data_reader = self._file_reader
+ else:
+ self.data_reader = reader
+ self.filein = None
+ self.resends_limit = 5
+ # These are here to shut pylint up
+ self.block = 0
+ self.last_data = None
+ self.block_size = 512
+ self.timeout = 0
+ self.resends = 0
+ self.finished = False
+ self.filename = None
+ self._reinit()
+
+ def _reinit(self):
+ '''Reinitialise all the class variables used by the protocol.'''
+ if self.filein is not None:
+ self.filein.close()
+ self.filein = None
+ self.block = 0
+ self.last_data = None
+ self.block_size = 512
+ self.timeout = 0
+ self.resends = 0
+ self.finished = False
+ self.filename = None
+
+ def _file_reader(self, command, **kwargs):
+ '''The default file reader if the user does not provide one.
+
+ The call returns a two element tuple where the first element
+ is an error code, and the second element is data if the error
+ code is 0 else it is an error message.
+ '''
+ # pylint: disable=too-many-return-statements
+ if command == 'open':
+ if 'filename' not in kwargs:
+ raise error.general('tftp-reader: invalid open: no filename')
+ filename = kwargs['filename']
+ try:
+ self.filein = open(filename, 'rb')
+ filesize = os.stat(filename).st_size
+ except FileNotFoundError:
+ return self.E_FILE_NOT_FOUND, 'file not found (%s)' % (
+ filename)
+ except PermissionError:
+ return self.E_ACCESS_VIOLATION, 'access violation'
+ except IOError as ioe:
+ return self.E_NOT_DEFINED, str(ioe)
+ return self.E_NO_ERROR, str(filesize)
+ if command == 'read':
+ if self.filein is None:
+ raise error.general('tftp-reader: read when not open')
+ if 'blksize' not in kwargs:
+ raise error.general('tftp-reader: invalid read: no blksize')
+ # pylint: disable=bare-except
+ try:
+ return self.E_NO_ERROR, self.filein.read(kwargs['blksize'])
+ except IOError as ioe:
+ return self.E_NOT_DEFINED, str(ioe)
+ except:
+ return self.E_NOT_DEFINED, 'unknown error'
+ if command == 'close':
+ if self.filein is not None:
+ self.filein.close()
+ self.filein = None
+ return self.E_NO_ERROR, "closed"
+ return self.E_NOT_DEFINED, 'invalid reader state'
+
+ @staticmethod
+ def _pack_bytes(data=None):
+ bdata = bytearray()
+ if data is not None:
+ if not isinstance(data, list):
+ data = [data]
+ for item in data:
+ if isinstance(item, int):
+ bdata.append(item >> 8)
+ bdata.append(item & 0xff)
+ elif isinstance(item, str):
+ bdata.extend(item.encode())
+ bdata.append(0)
+ else:
+ bdata.extend(item)
+ return bdata
+
+ def _response(self, opcode, data):
+ code = self.opcodes.index(opcode)
+ if code == 0 or code >= len(self.opcodes):
+ raise error.general('invalid opcode: ' + opcode)
+ bdata = self._pack_bytes([code, data])
+ #print(''.join(format(x, '02x') for x in bdata))
+ return bdata
+
+ def _error_response(self, code, message):
+ if log.tracing:
+ log.trace('tftp: error: %s:%d: %d: %s' %
+ (self.host, self.port, code, message))
+ self.finished = True
+ return self._response('ERROR', self._pack_bytes([code, message, 0]))
+
+ def _data_response(self, block, data):
+ if len(data) < self.block_size:
+ self.finished = True
+ return self._response('DATA', self._pack_bytes([block, data]))
+
+ def _oack_response(self, data):
+ self.resends += 1
+ if self.resends >= self.resends_limit:
+ return self._error_response(self.E_NOT_DEFINED,
+ 'resend limit reached')
+ return self._response('OACK', self._pack_bytes(data))
+
+ def _next_block(self, block):
+ # has the current block been acknowledged?
+ if block == self.block:
+ self.resends = 0
+ self.block += 1
+ err, data = self.data_reader('read', blksize=self.block_size)
+ data = bytearray(data)
+ if err != self.E_NO_ERROR:
+ return self._error_response(err, data)
+ # close if the length of data is less than the block size
+ if len(data) < self.block_size:
+ self.data_reader('close')
+ self.last_data = data
+ else:
+ self.resends += 1
+ if self.resends >= self.resends_limit:
+ return self._error_response(self.E_NOT_DEFINED,
+ 'resend limit reached')
+ data = self.last_data
+ return self._data_response(self.block, data)
+
+ def _read_req(self, data):
+ # if the last block is not 0 something has gone wrong and
+ # TID match. Restart the session. It could be the client
+ # is a simple implementation that does not move the send
+ # port on each retry.
+ if self.block != 0:
+ self.data_reader('close')
+ self._reinit()
+ # Get the filename, mode and options
+ self.filename = self.get_option('filename', data)
+ if self.filename is None:
+ return self._error_response(self.E_NOT_DEFINED,
+ 'filename not found in request')
+ if self.forced_file is not None:
+ self.filename = self.forced_file
+ # open the reader
+ err, message = self.data_reader('open', filename=self.filename)
+ if err != self.E_NO_ERROR:
+ return self._error_response(err, message)
+ # the no error on open message is the file size
+ try:
+ tsize = int(message)
+ except ValueError:
+ tsize = 0
+ mode = self.get_option('mode', data)
+ if mode is None:
+ return self._error_response(self.E_NOT_DEFINED,
+ 'mode not found in request')
+ oack_data = self._pack_bytes()
+ value = self.get_option('timeout', data)
+ if value is not None:
+ oack_data += self._pack_bytes(['timeout', value])
+ self.timeout = int(value)
+ value = self.get_option('blksize', data)
+ if value is not None:
+ oack_data += self._pack_bytes(['blksize', value])
+ self.block_size = int(value)
+ else:
+ self.block_size = 512
+ value = self.get_option('tsize', data)
+ if value is not None and tsize > 0:
+ oack_data += self._pack_bytes(['tsize', str(tsize)])
+ # Send the options ack
+ return self._oack_response(oack_data)
+
+ def _write_req(self):
+ # WRQ is not supported
+ return self._error_response(self.E_ILLEGAL_TFTP_OP,
+ "writes not supported")
+
+ def _op_ack(self, data):
+ # send the next block of data
+ block = (data[2] << 8) | data[3]
+ return self._next_block(block)
+
+ def process(self, host, port, data):
+ '''Process the incoming client data sending a response. If the session
+ has finished return None.
+ '''
+ if host != self.host and port != self.port:
+ return self._error_response(self.E_UKNOWN_TID,
+ 'unkown transfer ID')
+ if self.finished:
+ return None
+ opcode = (data[0] << 8) | data[1]
+ if opcode == self.OP_RRQ:
+ return self._read_req(data)
+ if opcode in [self.OP_WRQ, self.OP_DATA]:
+ return self._write_req()
+ if opcode == self.OP_ACK:
+ return self._op_ack(data)
+ return self._error_response(self.E_ILLEGAL_TFTP_OP,
+ "unknown or unsupported opcode")
+
+ def decode(self, host, port, data):
+ '''Decode the packet for diagnostic purposes.
+ '''
+ # pylint: disable=too-many-branches
+ out = ''
+ dlen = len(data)
+ if dlen > 2:
+ opcode = (data[0] << 8) | data[1]
+ if 0 < opcode < len(self.opcodes):
+ if opcode in [self.OP_RRQ, self.OP_WRQ]:
+ out += ' ' + self.opcodes[opcode] + ', '
+ i = 2
+ while data[i] != 0:
+ out += chr(data[i])
+ i += 1
+ while i < dlen - 1:
+ out += ', '
+ i += 1
+ while data[i] != 0:
+ out += chr(data[i])
+ i += 1
+ elif opcode == self.OP_DATA:
+ block = (data[2] << 8) | data[3]
+ out += ' ' + self.opcodes[opcode] + ', '
+ out += '#' + str(block) + ', '
+ if dlen > 4:
+ out += '%02x%02x..%02x%02x' % (data[4], data[5],
+ data[-2], data[-1])
+ else:
+ out += '%02x%02x%02x%02x' % (data[4], data[5], data[6],
+ data[6])
+ out += ',' + str(dlen - 4)
+ elif opcode == self.OP_ACK:
+ block = (data[2] << 8) | data[3]
+ out += ' ' + self.opcodes[opcode] + ' ' + str(block)
+ elif opcode == self.OP_ERROR:
+ out += 'E ' + self.opcodes[opcode] + ', '
+ out += str((data[2] << 8) | (data[3]))
+ out += ': ' + str(data[4:].decode())
+ i = 2
+ while data[i] != 0:
+ out += chr(data[i])
+ i += 1
+ elif opcode == self.OP_OACK:
+ out += ' ' + self.opcodes[opcode]
+ i = 1
+ while i < dlen - 1:
+ out += ', '
+ i += 1
+ while data[i] != 0:
+ out += chr(data[i])
+ i += 1
+ else:
+ out += 'E INV(%d)' % (opcode)
+ else:
+ out += 'E INVALID LENGTH'
+ return out[:2] + '[%s:%d] (%d) ' % (host, port, len(data)) + out[2:]
+
+ @staticmethod
+ def get_option(option, data):
+ '''Get the option from the TFTP packet.'''
+ dlen = len(data) - 1
+ opcode = (data[0] << 8) | data[1]
+ next_option = False
+ if opcode in [1, 2]:
+ count = 0
+ i = 2
+ while i < dlen:
+ value = ''
+ while data[i] != 0:
+ value += chr(data[i])
+ i += 1
+ i += 1
+ if option == 'filename' and count == 0:
+ return value
+ if option == 'mode' and count == 1:
+ return value
+ if value == option and (count % 1) == 0:
+ next_option = True
+ elif next_option:
+ return value
+ count += 1
+ return None
+
+ def get_timeout(self, default_timeout, timeout_guard):
+ '''Get the timeout. The timeout can be an option.'''
+ if self.timeout == 0:
+ return self.timeout + timeout_guard
+ return default_timeout
+
+ def get_block_size(self):
+ '''Get the block size. The block size can be an option.'''
+ return self.block_size
+
+
+class udp_handler(socketserver.BaseRequestHandler):
+ '''TFTP UDP handler for a TFTP session.'''
+ def _notice(self, text):
+ if self.server.tftp.notices:
+ log.notice(text)
+ else:
+ log.trace(text)
+
+ def handle_session(self, index):
+ '''Handle the TFTP session data.'''
+ # pylint: disable=too-many-locals
+ # pylint: disable=broad-except
+ # pylint: disable=too-many-branches
+ # pylint: disable=too-many-statements
+ client_ip = self.client_address[0]
+ client_port = self.client_address[1]
+ client = '%s:%i' % (client_ip, client_port)
+ self._notice('] tftp: %d: start: %s' % (index, client))
+ try:
+ session = tftp_session(client_ip, client_port,
+ self.server.tftp.base,
+ self.server.tftp.forced_file,
+ self.server.tftp.reader)
+ data = bytearray(self.request[0])
+ response = session.process(client_ip, client_port, data)
+ if response is not None:
+ if log.tracing and self.server.tftp.packet_trace:
+ log.trace(' > ' +
+ session.decode(client_ip, client_port, data))
+ timeout = session.get_timeout(self.server.tftp.timeout, 1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.bind(('', 0))
+ sock.settimeout(timeout)
+ while response is not None:
+ if log.tracing and self.server.tftp.packet_trace:
+ log.trace(
+ ' < ' +
+ session.decode(client_ip, client_port, response))
+ sock.sendto(response, (client_ip, client_port))
+ if session.finished:
+ break
+ try:
+ data, address = sock.recvfrom(2 + 2 +
+ session.get_block_size())
+ data = bytearray(data)
+ if log.tracing and self.server.tftp.packet_trace:
+ log.trace(
+ ' > ' +
+ session.decode(address[0], address[1], data))
+ except socket.error as serr:
+ if log.tracing:
+ log.trace('] tftp: %d: receive: %s: error: %s' \
+ % (index, client, serr))
+ return
+ except socket.gaierror as serr:
+ if log.tracing:
+ log.trace('] tftp: %d: receive: %s: error: %s' \
+ % (index, client, serr))
+ return
+ response = session.process(address[0], address[1], data)
+ except error.general as gerr:
+ self._notice('] tftp: %dd: error: %s' % (index, gerr))
+ except error.internal as ierr:
+ self._notice('] tftp: %d: error: %s' % (index, ierr))
+ except error.exit:
+ pass
+ except KeyboardInterrupt:
+ pass
+ except Exception as exp:
+ if self.server.tftp.exception_is_raise:
+ raise
+ self._notice('] tftp: %d: error: %s: %s' % (index, type(exp), exp))
+ self._notice('] tftp: %d: end: %s' % (index, client))
+
+ def handle(self):
+ '''The UDP server handle method.'''
+ if self.server.tftp.sessions is None \
+ or self.server.tftp.session < self.server.tftp.sessions:
+ self.handle_session(self.server.tftp.next_session())
+
+
+class udp_server(socketserver.ThreadingMixIn, socketserver.UDPServer):
+ '''UDP server. Default behaviour.'''
+
+
+class tftp_server(object):
+ '''TFTP server runs a UDP server to handle TFTP sessions.'''
+
+ # pylint: disable=useless-object-inheritance
+ # pylint: disable=too-many-instance-attributes
+
+ def __init__(self,
+ host,
+ port,
+ timeout=10,
+ base=None,
+ forced_file=None,
+ sessions=None,
+ reader=None):
+ # pylint: disable=too-many-arguments
+ self.lock = threading.Lock()
+ self.notices = False
+ self.packet_trace = False
+ self.exception_is_raise = False
+ self.timeout = timeout
+ self.host = host
+ self.port = port
+ self.server = None
+ self.server_thread = None
+ if base is None:
+ base = os.getcwd()
+ self.base = base
+ self.forced_file = forced_file
+ if sessions is not None and not isinstance(sessions, int):
+ raise error.general('tftp session count is not a number')
+ self.sessions = sessions
+ self.session = 0
+ self.reader = reader
+
+ def __del__(self):
+ self.stop()
+
+ def _lock(self):
+ self.lock.acquire()
+
+ def _unlock(self):
+ self.lock.release()
+
+ def start(self):
+ '''Start the TFTP server. Returns once started.'''
+ # pylint: disable=attribute-defined-outside-init
+ if log.tracing:
+ log.trace('] tftp: server: %s:%i' % (self.host, self.port))
+ if self.host == 'all':
+ host = ''
+ else:
+ host = self.host
+ try:
+ self.server = udp_server((host, self.port), udp_handler)
+ except Exception as exp:
+ raise error.general('tftp server create: %s' % (exp))
+ # We cannot set tftp in __init__ because the object is created
+ # in a separate package.
+ self.server.tftp = self
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+
+ def stop(self):
+ '''Stop the TFTP server and close the server port.'''
+ self._lock()
+ try:
+ if self.server is not None:
+ self.server.shutdown()
+ self.server.server_close()
+ self.server = None
+ finally:
+ self._unlock()
+
+ def run(self):
+ '''Run the TFTP server for the specified number of sessions.'''
+ running = True
+ while running:
+ period = 1
+ self._lock()
+ if self.server is None:
+ running = False
+ period = 0
+ elif self.sessions is not None:
+ if self.sessions == 0:
+ running = False
+ period = 0
+ else:
+ period = 0.25
+ self._unlock()
+ if period > 0:
+ time.sleep(period)
+ self.stop()
+
+ def get_session(self):
+ '''Return the session count.'''
+ count = 0
+ self._lock()
+ try:
+ count = self.session
+ finally:
+ self._unlock()
+ return count
+
+ def next_session(self):
+ '''Return the next session number.'''
+ count = 0
+ self._lock()
+ try:
+ self.session += 1
+ count = self.session
+ finally:
+ self._unlock()
+ return count
+
+ def enable_notices(self):
+ '''Call to enable notices. The server is quiet without this call.'''
+ self._lock()
+ self.notices = True
+ self._unlock()
+
+ def trace_packets(self):
+ '''Call to enable packet tracing as a diagnostic.'''
+ self._lock()
+ self.packet_trace = True
+ self._unlock()
+
+ def except_is_raise(self):
+ '''If True a standard exception will generate a backtrace.'''
+ self.exception_is_raise = True
+
+
+def load_log(logfile):
+ '''Set the log file.'''
+ if logfile is None:
+ log.default = log.log(streams=['stdout'])
+ else:
+ log.default = log.log(streams=[logfile])
+
+
+def run(args=sys.argv, command_path=None):
+ '''Run a TFTP server session.'''
+ # pylint: disable=dangerous-default-value
+ # pylint: disable=unused-argument
+ # pylint: disable=too-many-branches
+ # pylint: disable=too-many-statements
+ ecode = 0
+ notice = None
+ server = None
+ # pylint: disable=bare-except
+ try:
+ description = 'A TFTP Server that supports a read only TFTP session.'
+
+ nice_cwd = os.path.relpath(os.getcwd())
+ if len(nice_cwd) > len(os.path.abspath(nice_cwd)):
+ nice_cwd = os.path.abspath(nice_cwd)
+
+ argsp = argparse.ArgumentParser(prog='rtems-tftp-server',
+ description=description)
+ argsp.add_argument('-l',
+ '--log',
+ help='log file.',
+ type=str,
+ default=None)
+ argsp.add_argument('-v',
+ '--trace',
+ help='enable trace logging for debugging.',
+ action='store_true',
+ default=False)
+ argsp.add_argument('--trace-packets',
+ help='enable trace logging of packets.',
+ action='store_true',
+ default=False)
+ argsp.add_argument('--show-backtrace',
+ help='show the exception backtrace.',
+ action='store_true',
+ default=False)
+ argsp.add_argument(
+ '-B',
+ '--bind',
+ help='address to bind the server too (default: %(default)s).',
+ type=str,
+ default='all')
+ argsp.add_argument(
+ '-P',
+ '--port',
+ help='port to bind the server too (default: %(default)s).',
+ type=int,
+ default='69')
+ argsp.add_argument('-t', '--timeout',
+ help = 'timeout in seconds, client can override ' \
+ '(default: %(default)s).',
+ type = int, default = '10')
+ argsp.add_argument(
+ '-b',
+ '--base',
+ help='base path, not checked (default: %(default)s).',
+ type=str,
+ default=nice_cwd)
+ argsp.add_argument(
+ '-F',
+ '--force-file',
+ help='force the file to be downloaded overriding the client.',
+ type=str,
+ default=None)
+ argsp.add_argument('-s', '--sessions',
+ help = 'number of TFTP sessions to run before exiting ' \
+ '(default: forever.',
+ type = int, default = None)
+
+ argopts = argsp.parse_args(args[1:])
+
+ load_log(argopts.log)
+ log.notice('RTEMS Tools - TFTP Server, %s' % (version.string()))
+ log.output(log.info(args))
+ log.tracing = argopts.trace
+
+ server = tftp_server(argopts.bind, argopts.port, argopts.timeout,
+ argopts.base, argopts.force_file,
+ argopts.sessions)
+ server.enable_notices()
+ if argopts.trace_packets:
+ server.trace_packets()
+ if argopts.show_backtrace:
+ server.except_is_raise()
+
+ try:
+ server.start()
+ server.run()
+ finally:
+ server.stop()
+
+ except error.general as gerr:
+ notice = str(gerr)
+ ecode = 1
+ except error.internal as ierr:
+ notice = str(ierr)
+ ecode = 1
+ except error.exit:
+ pass
+ except KeyboardInterrupt:
+ notice = 'abort: user terminated'
+ ecode = 1
+ except SystemExit:
+ pass
+ except:
+ notice = 'abort: unknown error'
+ ecode = 1
+ if server is not None:
+ del server
+ if notice is not None:
+ log.stderr(notice)
+ sys.exit(ecode)
+
+
+if __name__ == "__main__":
+ run()
diff --git a/tester/rt/tftpy/README b/tester/rt/tftpy/README
deleted file mode 100644
index ad7a871..0000000
--- a/tester/rt/tftpy/README
+++ /dev/null
@@ -1,115 +0,0 @@
-Copyright, Michael P. Soulier, 2010.
-
-About Release 0.6.2:
-====================
-Maintenance release to fix a couple of reported issues.
-
-About Release 0.6.1:
-====================
-Maintenance release to fix several reported problems, including a rollover
-at 2^16 blocks, and some contributed work on dynamic file objects.
-
-About Release 0.6.0:
-====================
-Maintenance update to fix several reported issues, including proper
-retransmits on timeouts, and further expansion of unit tests.
-
-About Release 0.5.1:
-====================
-Maintenance update to fix a bug in the server, overhaul the documentation for
-the website, fix a typo in the unit tests, fix a failure to set default
-blocksize, and a divide by zero error in speed calculations for very short
-transfers.
-
-Also, this release adds support for input/output in client as stdin/stdout
-
-About Release 0.5.0:
-====================
-Complete rewrite of the state machine.
-Now fully implements downloading and uploading.
-
-About Release 0.4.6:
-====================
-Feature release to add the tsize option.
-Thanks to Kuba Kończyk for the patch.
-
-About Release 0.4.5:
-====================
-Bugfix release for compatability issues on Win32, among other small issues.
-
-About Release 0.4.4:
-====================
-Bugfix release for poor tolerance of unsupported options in the server.
-
-About Release 0.4.3:
-====================
-Bugfix release for an issue with the server's detection of the end of the file
-during a download.
-
-About Release 0.4.2:
-====================
-Bugfix release for some small installation issues with earlier Python
-releases.
-
-About Release 0.4.1:
-====================
-Bugfix release to fix the installation path, with some restructuring into a
-tftpy package from the single module used previously.
-
-About Release 0.4:
-==================
-This release adds a TftpServer class with a sample implementation in bin.
-The server uses a single thread with multiple handlers and a select() loop to
-handle multiple clients simultaneously.
-
-Only downloads are supported at this time.
-
-About Release 0.3:
-==================
-This release fixes a major RFC 1350 compliance problem with the remote TID.
-
-About Release 0.2:
-==================
-This release adds variable block sizes, and general option support,
-implementing RFCs 2347 and 2348. This is accessible in the TftpClient class
-via the options dict, or in the sample client via the --blocksize option.
-
-About Release 0.1:
-==================
-
-This is an initial release in the spirit of "release early, release often".
-Currently the sample client works, supporting RFC 1350. The server is not yet
-implemented, and RFC 2347 and 2348 support (variable block sizes) is underway,
-planned for 0.2.
-
-About Tftpy:
-============
-
-Purpose:
---------
-Tftpy is a TFTP library for the Python programming language. It includes
-client and server classes, with sample implementations. Hooks are included for
-easy inclusion in a UI for populating progress indicators. It supports RFCs
-1350, 2347, 2348 and the tsize option from RFC 2349.
-
-Dependencies:
--------------
-Python 2.3+, hopefully. Let me know if it fails to work.
-
-Trifles:
---------
-Home page: http://tftpy.sf.net/
-Project page: http://sourceforge.net/projects/tftpy/
-
-License is the MIT License
-
-See COPYING in this distribution.
-
-Limitations:
-------------
-- Only 'octet' mode is supported.
-- The only options supported are blksize and tsize.
-
-Author:
-=======
-Michael P. Soulier <msoulier@digitaltorque.ca>
diff --git a/tester/rt/tftpy/TftpClient.py b/tester/rt/tftpy/TftpClient.py
deleted file mode 100644
index eb82c05..0000000
--- a/tester/rt/tftpy/TftpClient.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module implements the TFTP Client functionality. Instantiate an
-instance of the client, and then use its upload or download method. Logging is
-performed via a standard logging object set in TftpShared."""
-
-
-import types
-import logging
-from .TftpShared import *
-from .TftpPacketTypes import *
-from .TftpContexts import TftpContextClientDownload, TftpContextClientUpload
-
-log = logging.getLogger('tftpy.TftpClient')
-
-class TftpClient(TftpSession):
- """This class is an implementation of a tftp client. Once instantiated, a
- download can be initiated via the download() method, or an upload via the
- upload() method."""
-
- def __init__(self, host, port=69, options={}, localip = ""):
- TftpSession.__init__(self)
- self.context = None
- self.host = host
- self.iport = port
- self.filename = None
- self.options = options
- self.localip = localip
- if 'blksize' in self.options:
- size = self.options['blksize']
- tftpassert(int == type(size), "blksize must be an int")
- if size < MIN_BLKSIZE or size > MAX_BLKSIZE:
- raise TftpException("Invalid blksize: %d" % size)
-
- def download(self, filename, output, packethook=None, timeout=SOCK_TIMEOUT):
- """This method initiates a tftp download from the configured remote
- host, requesting the filename passed. It writes the file to output,
- which can be a file-like object or a path to a local file. If a
- packethook is provided, it must be a function that takes a single
- parameter, which will be a copy of each DAT packet received in the
- form of a TftpPacketDAT object. The timeout parameter may be used to
- override the default SOCK_TIMEOUT setting, which is the amount of time
- that the client will wait for a receive packet to arrive.
-
- Note: If output is a hyphen, stdout is used."""
- # We're downloading.
- log.debug("Creating download context with the following params:")
- log.debug("host = %s, port = %s, filename = %s" % (self.host, self.iport, filename))
- log.debug("options = %s, packethook = %s, timeout = %s" % (self.options, packethook, timeout))
- self.context = TftpContextClientDownload(self.host,
- self.iport,
- filename,
- output,
- self.options,
- packethook,
- timeout,
- localip = self.localip)
- self.context.start()
- # Download happens here
- self.context.end()
-
- metrics = self.context.metrics
-
- log.info('')
- log.info("Download complete.")
- if metrics.duration == 0:
- log.info("Duration too short, rate undetermined")
- else:
- log.info("Downloaded %.2f bytes in %.2f seconds" % (metrics.bytes, metrics.duration))
- log.info("Average rate: %.2f kbps" % metrics.kbps)
- log.info("%.2f bytes in resent data" % metrics.resent_bytes)
- log.info("Received %d duplicate packets" % metrics.dupcount)
-
- def upload(self, filename, input, packethook=None, timeout=SOCK_TIMEOUT):
- """This method initiates a tftp upload to the configured remote host,
- uploading the filename passed. It reads the file from input, which
- can be a file-like object or a path to a local file. If a packethook
- is provided, it must be a function that takes a single parameter,
- which will be a copy of each DAT packet sent in the form of a
- TftpPacketDAT object. The timeout parameter may be used to override
- the default SOCK_TIMEOUT setting, which is the amount of time that
- the client will wait for a DAT packet to be ACKd by the server.
-
- Note: If input is a hyphen, stdin is used."""
- self.context = TftpContextClientUpload(self.host,
- self.iport,
- filename,
- input,
- self.options,
- packethook,
- timeout,
- localip = self.localip)
- self.context.start()
- # Upload happens here
- self.context.end()
-
- metrics = self.context.metrics
-
- log.info('')
- log.info("Upload complete.")
- if metrics.duration == 0:
- log.info("Duration too short, rate undetermined")
- else:
- log.info("Uploaded %d bytes in %.2f seconds" % (metrics.bytes, metrics.duration))
- log.info("Average rate: %.2f kbps" % metrics.kbps)
- log.info("%.2f bytes in resent data" % metrics.resent_bytes)
- log.info("Resent %d packets" % metrics.dupcount)
diff --git a/tester/rt/tftpy/TftpContexts.py b/tester/rt/tftpy/TftpContexts.py
deleted file mode 100644
index da85886..0000000
--- a/tester/rt/tftpy/TftpContexts.py
+++ /dev/null
@@ -1,429 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module implements all contexts for state handling during uploads and
-downloads, the main interface to which being the TftpContext base class.
-
-The concept is simple. Each context object represents a single upload or
-download, and the state object in the context object represents the current
-state of that transfer. The state object has a handle() method that expects
-the next packet in the transfer, and returns a state object until the transfer
-is complete, at which point it returns None. That is, unless there is a fatal
-error, in which case a TftpException is returned instead."""
-
-
-from .TftpShared import *
-from .TftpPacketTypes import *
-from .TftpPacketFactory import TftpPacketFactory
-from .TftpStates import *
-import socket
-import time
-import sys
-import os
-import logging
-
-log = logging.getLogger('tftpy.TftpContext')
-
-###############################################################################
-# Utility classes
-###############################################################################
-
-class TftpMetrics(object):
- """A class representing metrics of the transfer."""
- def __init__(self):
- # Bytes transferred
- self.bytes = 0
- # Bytes re-sent
- self.resent_bytes = 0
- # Duplicate packets received
- self.dups = {}
- self.dupcount = 0
- # Times
- self.start_time = 0
- self.end_time = 0
- self.duration = 0
- # Rates
- self.bps = 0
- self.kbps = 0
- # Generic errors
- self.errors = 0
-
- def compute(self):
- # Compute transfer time
- self.duration = self.end_time - self.start_time
- if self.duration == 0:
- self.duration = 1
- log.debug("TftpMetrics.compute: duration is %s", self.duration)
- self.bps = (self.bytes * 8.0) / self.duration
- self.kbps = self.bps / 1024.0
- log.debug("TftpMetrics.compute: kbps is %s", self.kbps)
- for key in self.dups:
- self.dupcount += self.dups[key]
-
- def add_dup(self, pkt):
- """This method adds a dup for a packet to the metrics."""
- log.debug("Recording a dup of %s", pkt)
- s = str(pkt)
- if s in self.dups:
- self.dups[s] += 1
- else:
- self.dups[s] = 1
- tftpassert(self.dups[s] < MAX_DUPS, "Max duplicates reached")
-
-###############################################################################
-# Context classes
-###############################################################################
-
-class TftpContext(object):
- """The base class of the contexts."""
-
- def __init__(self, host, port, timeout, localip = ""):
- """Constructor for the base context, setting shared instance
- variables."""
- self.file_to_transfer = None
- self.fileobj = None
- self.options = None
- self.packethook = None
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- if localip != "":
- self.sock.bind((localip, 0))
- self.sock.settimeout(timeout)
- self.timeout = timeout
- self.state = None
- self.next_block = 0
- self.factory = TftpPacketFactory()
- # Note, setting the host will also set self.address, as it's a property.
- self.host = host
- self.port = port
- # The port associated with the TID
- self.tidport = None
- # Metrics
- self.metrics = TftpMetrics()
- # Fluag when the transfer is pending completion.
- self.pending_complete = False
- # Time when this context last received any traffic.
- # FIXME: does this belong in metrics?
- self.last_update = 0
- # The last packet we sent, if applicable, to make resending easy.
- self.last_pkt = None
- # Count the number of retry attempts.
- self.retry_count = 0
-
- def getBlocksize(self):
- """Fetch the current blocksize for this session."""
- return int(self.options.get('blksize', 512))
-
- def __del__(self):
- """Simple destructor to try to call housekeeping in the end method if
- not called explicitely. Leaking file descriptors is not a good
- thing."""
- self.end()
-
- def checkTimeout(self, now):
- """Compare current time with last_update time, and raise an exception
- if we're over the timeout time."""
- log.debug("checking for timeout on session %s", self)
- if now - self.last_update > self.timeout:
- raise TftpTimeout("Timeout waiting for traffic")
-
- def start(self):
- raise NotImplementedError("Abstract method")
-
- def end(self, close_fileobj=True):
- """Perform session cleanup, since the end method should always be
- called explicitely by the calling code, this works better than the
- destructor.
- Set close_fileobj to False so fileobj can be returned open."""
- log.debug("in TftpContext.end - closing socket")
- self.sock.close()
- if close_fileobj and self.fileobj is not None and not self.fileobj.closed:
- log.debug("self.fileobj is open - closing")
- self.fileobj.close()
-
- def gethost(self):
- "Simple getter method for use in a property."
- return self.__host
-
- def sethost(self, host):
- """Setter method that also sets the address property as a result
- of the host that is set."""
- self.__host = host
- self.address = socket.gethostbyname(host)
-
- host = property(gethost, sethost)
-
- def setNextBlock(self, block):
- if block >= 2 ** 16:
- log.debug("Block number rollover to 0 again")
- block = 0
- self.__eblock = block
-
- def getNextBlock(self):
- return self.__eblock
-
- next_block = property(getNextBlock, setNextBlock)
-
- def cycle(self):
- """Here we wait for a response from the server after sending it
- something, and dispatch appropriate action to that response."""
- try:
- (buffer, (raddress, rport)) = self.sock.recvfrom(MAX_BLKSIZE)
- except socket.timeout:
- log.warning("Timeout waiting for traffic, retrying...")
- raise TftpTimeout("Timed-out waiting for traffic")
-
- # Ok, we've received a packet. Log it.
- log.debug("Received %d bytes from %s:%s",
- len(buffer), raddress, rport)
- # And update our last updated time.
- self.last_update = time.time()
-
- # Decode it.
- recvpkt = self.factory.parse(buffer)
-
- # Check for known "connection".
- if raddress != self.address:
- log.warning("Received traffic from %s, expected host %s. Discarding"
- % (raddress, self.host))
-
- if self.tidport and self.tidport != rport:
- log.warning("Received traffic from %s:%s but we're "
- "connected to %s:%s. Discarding."
- % (raddress, rport,
- self.host, self.tidport))
-
- # If there is a packethook defined, call it. We unconditionally
- # pass all packets, it's up to the client to screen out different
- # kinds of packets. This way, the client is privy to things like
- # negotiated options.
- if self.packethook:
- self.packethook(recvpkt)
-
- # And handle it, possibly changing state.
- self.state = self.state.handle(recvpkt, raddress, rport)
- # If we didn't throw any exceptions here, reset the retry_count to
- # zero.
- self.retry_count = 0
-
-class TftpContextServer(TftpContext):
- """The context for the server."""
- def __init__(self,
- host,
- port,
- timeout,
- root,
- dyn_file_func=None,
- upload_open=None):
- TftpContext.__init__(self,
- host,
- port,
- timeout,
- )
- # At this point we have no idea if this is a download or an upload. We
- # need to let the start state determine that.
- self.state = TftpStateServerStart(self)
-
- self.root = root
- self.dyn_file_func = dyn_file_func
- self.upload_open = upload_open
-
- def __str__(self):
- return "%s:%s %s" % (self.host, self.port, self.state)
-
- def start(self, buffer):
- """Start the state cycle. Note that the server context receives an
- initial packet in its start method. Also note that the server does not
- loop on cycle(), as it expects the TftpServer object to manage
- that."""
- log.debug("In TftpContextServer.start")
- self.metrics.start_time = time.time()
- log.debug("Set metrics.start_time to %s", self.metrics.start_time)
- # And update our last updated time.
- self.last_update = time.time()
-
- pkt = self.factory.parse(buffer)
- log.debug("TftpContextServer.start() - factory returned a %s", pkt)
-
- # Call handle once with the initial packet. This should put us into
- # the download or the upload state.
- self.state = self.state.handle(pkt,
- self.host,
- self.port)
-
- def end(self):
- """Finish up the context."""
- TftpContext.end(self)
- self.metrics.end_time = time.time()
- log.debug("Set metrics.end_time to %s", self.metrics.end_time)
- self.metrics.compute()
-
-class TftpContextClientUpload(TftpContext):
- """The upload context for the client during an upload.
- Note: If input is a hyphen, then we will use stdin."""
- def __init__(self,
- host,
- port,
- filename,
- input,
- options,
- packethook,
- timeout,
- localip = ""):
- TftpContext.__init__(self,
- host,
- port,
- timeout,
- localip)
- self.file_to_transfer = filename
- self.options = options
- self.packethook = packethook
- # If the input object has a read() function,
- # assume it is file-like.
- if hasattr(input, 'read'):
- self.fileobj = input
- elif input == '-':
- self.fileobj = sys.stdin
- else:
- self.fileobj = open(input, "rb")
-
- log.debug("TftpContextClientUpload.__init__()")
- log.debug("file_to_transfer = %s, options = %s" %
- (self.file_to_transfer, self.options))
-
- def __str__(self):
- return "%s:%s %s" % (self.host, self.port, self.state)
-
- def start(self):
- log.info("Sending tftp upload request to %s" % self.host)
- log.info(" filename -> %s" % self.file_to_transfer)
- log.info(" options -> %s" % self.options)
-
- self.metrics.start_time = time.time()
- log.debug("Set metrics.start_time to %s" % self.metrics.start_time)
-
- # FIXME: put this in a sendWRQ method?
- pkt = TftpPacketWRQ()
- pkt.filename = self.file_to_transfer
- pkt.mode = "octet" # FIXME - shouldn't hardcode this
- pkt.options = self.options
- self.sock.sendto(pkt.encode().buffer, (self.host, self.port))
- self.next_block = 1
- self.last_pkt = pkt
- # FIXME: should we centralize sendto operations so we can refactor all
- # saving of the packet to the last_pkt field?
-
- self.state = TftpStateSentWRQ(self)
-
- while self.state:
- try:
- log.debug("State is %s" % self.state)
- self.cycle()
- except TftpTimeout as err:
- log.error(str(err))
- self.retry_count += 1
- if self.retry_count >= TIMEOUT_RETRIES:
- log.debug("hit max retries, giving up")
- raise
- else:
- log.warning("resending last packet")
- self.state.resendLast()
-
- def end(self):
- """Finish up the context."""
- TftpContext.end(self)
- self.metrics.end_time = time.time()
- log.debug("Set metrics.end_time to %s" % self.metrics.end_time)
- self.metrics.compute()
-
-
-class TftpContextClientDownload(TftpContext):
- """The download context for the client during a download.
- Note: If output is a hyphen, then the output will be sent to stdout."""
- def __init__(self,
- host,
- port,
- filename,
- output,
- options,
- packethook,
- timeout,
- localip = ""):
- TftpContext.__init__(self,
- host,
- port,
- timeout,
- localip)
- # FIXME: should we refactor setting of these params?
- self.file_to_transfer = filename
- self.options = options
- self.packethook = packethook
- self.filelike_fileobj = False
- # If the output object has a write() function,
- # assume it is file-like.
- if hasattr(output, 'write'):
- self.fileobj = output
- self.filelike_fileobj = True
- # If the output filename is -, then use stdout
- elif output == '-':
- self.fileobj = sys.stdout
- self.filelike_fileobj = True
- else:
- self.fileobj = open(output, "wb")
-
- log.debug("TftpContextClientDownload.__init__()")
- log.debug("file_to_transfer = %s, options = %s" %
- (self.file_to_transfer, self.options))
-
- def __str__(self):
- return "%s:%s %s" % (self.host, self.port, self.state)
-
- def start(self):
- """Initiate the download."""
- log.info("Sending tftp download request to %s" % self.host)
- log.info(" filename -> %s" % self.file_to_transfer)
- log.info(" options -> %s" % self.options)
-
- self.metrics.start_time = time.time()
- log.debug("Set metrics.start_time to %s" % self.metrics.start_time)
-
- # FIXME: put this in a sendRRQ method?
- pkt = TftpPacketRRQ()
- pkt.filename = self.file_to_transfer
- pkt.mode = "octet" # FIXME - shouldn't hardcode this
- pkt.options = self.options
- self.sock.sendto(pkt.encode().buffer, (self.host, self.port))
- self.next_block = 1
- self.last_pkt = pkt
-
- self.state = TftpStateSentRRQ(self)
-
- while self.state:
- try:
- log.debug("State is %s" % self.state)
- self.cycle()
- except TftpTimeout as err:
- log.error(str(err))
- self.retry_count += 1
- if self.retry_count >= TIMEOUT_RETRIES:
- log.debug("hit max retries, giving up")
- raise
- else:
- log.warning("resending last packet")
- self.state.resendLast()
- except TftpFileNotFoundError as err:
- # If we received file not found, then we should not save the open
- # output file or we'll be left with a size zero file. Delete it,
- # if it exists.
- log.error("Received File not found error")
- if self.fileobj is not None and not self.filelike_fileobj:
- if os.path.exists(self.fileobj.name):
- log.debug("unlinking output file of %s", self.fileobj.name)
- os.unlink(self.fileobj.name)
-
- raise
-
- def end(self):
- """Finish up the context."""
- TftpContext.end(self, not self.filelike_fileobj)
- self.metrics.end_time = time.time()
- log.debug("Set metrics.end_time to %s" % self.metrics.end_time)
- self.metrics.compute()
diff --git a/tester/rt/tftpy/TftpPacketFactory.py b/tester/rt/tftpy/TftpPacketFactory.py
deleted file mode 100644
index 41f39a9..0000000
--- a/tester/rt/tftpy/TftpPacketFactory.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module implements the TftpPacketFactory class, which can take a binary
-buffer, and return the appropriate TftpPacket object to represent it, via the
-parse() method."""
-
-
-from .TftpShared import *
-from .TftpPacketTypes import *
-import logging
-
-log = logging.getLogger('tftpy.TftpPacketFactory')
-
-class TftpPacketFactory(object):
- """This class generates TftpPacket objects. It is responsible for parsing
- raw buffers off of the wire and returning objects representing them, via
- the parse() method."""
- def __init__(self):
- self.classes = {
- 1: TftpPacketRRQ,
- 2: TftpPacketWRQ,
- 3: TftpPacketDAT,
- 4: TftpPacketACK,
- 5: TftpPacketERR,
- 6: TftpPacketOACK
- }
-
- def parse(self, buffer):
- """This method is used to parse an existing datagram into its
- corresponding TftpPacket object. The buffer is the raw bytes off of
- the network."""
- log.debug("parsing a %d byte packet" % len(buffer))
- (opcode,) = struct.unpack(str("!H"), buffer[:2])
- log.debug("opcode is %d" % opcode)
- packet = self.__create(opcode)
- packet.buffer = buffer
- return packet.decode()
-
- def __create(self, opcode):
- """This method returns the appropriate class object corresponding to
- the passed opcode."""
- tftpassert(opcode in self.classes,
- "Unsupported opcode: %d" % opcode)
-
- packet = self.classes[opcode]()
-
- return packet
diff --git a/tester/rt/tftpy/TftpPacketTypes.py b/tester/rt/tftpy/TftpPacketTypes.py
deleted file mode 100644
index 3d3bdf8..0000000
--- a/tester/rt/tftpy/TftpPacketTypes.py
+++ /dev/null
@@ -1,494 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module implements the packet types of TFTP itself, and the
-corresponding encode and decode methods for them."""
-
-
-import struct
-import sys
-import logging
-from .TftpShared import *
-
-log = logging.getLogger('tftpy.TftpPacketTypes')
-
-class TftpSession(object):
- """This class is the base class for the tftp client and server. Any shared
- code should be in this class."""
- # FIXME: do we need this anymore?
- pass
-
-class TftpPacketWithOptions(object):
- """This class exists to permit some TftpPacket subclasses to share code
- regarding options handling. It does not inherit from TftpPacket, as the
- goal is just to share code here, and not cause diamond inheritance."""
-
- def __init__(self):
- self.options = {}
-
- # Always use unicode strings, except at the encode/decode barrier.
- # Simpler to keep things clear.
- def setoptions(self, options):
- log.debug("in TftpPacketWithOptions.setoptions")
- log.debug("options: %s", options)
- myoptions = {}
- for key in options:
- newkey = key
- if isinstance(key, bytes):
- newkey = newkey.decode('ascii')
- newval = options[key]
- if isinstance(newval, bytes):
- newval = newval.decode('ascii')
- myoptions[newkey] = newval
- log.debug("populated myoptions with %s = %s", newkey, myoptions[newkey])
-
- log.debug("setting options hash to: %s", myoptions)
- self._options = myoptions
-
- def getoptions(self):
- log.debug("in TftpPacketWithOptions.getoptions")
- return self._options
-
- # Set up getter and setter on options to ensure that they are the proper
- # type. They should always be strings, but we don't need to force the
- # client to necessarily enter strings if we can avoid it.
- options = property(getoptions, setoptions)
-
- def decode_options(self, buffer):
- """This method decodes the section of the buffer that contains an
- unknown number of options. It returns a dictionary of option names and
- values."""
- fmt = b"!"
- options = {}
-
- log.debug("decode_options: buffer is: %s", repr(buffer))
- log.debug("size of buffer is %d bytes", len(buffer))
- if len(buffer) == 0:
- log.debug("size of buffer is zero, returning empty hash")
- return {}
-
- # Count the nulls in the buffer. Each one terminates a string.
- log.debug("about to iterate options buffer counting nulls")
- length = 0
- for i in range(len(buffer)):
- if ord(buffer[i:i+1]) == 0:
- log.debug("found a null at length %d", length)
- if length > 0:
- fmt += b"%dsx" % length
- length = -1
- else:
- raise TftpException("Invalid options in buffer")
- length += 1
-
- log.debug("about to unpack, fmt is: %s", fmt)
- mystruct = struct.unpack(fmt, buffer)
-
- tftpassert(len(mystruct) % 2 == 0,
- "packet with odd number of option/value pairs")
-
- for i in range(0, len(mystruct), 2):
- key = mystruct[i].decode('ascii')
- val = mystruct[i+1].decode('ascii')
- log.debug("setting option %s to %s", key, val)
- log.debug("types are %s and %s", type(key), type(val))
- options[key] = val
-
- return options
-
-class TftpPacket(object):
- """This class is the parent class of all tftp packet classes. It is an
- abstract class, providing an interface, and should not be instantiated
- directly."""
- def __init__(self):
- self.opcode = 0
- self.buffer = None
-
- def encode(self):
- """The encode method of a TftpPacket takes keyword arguments specific
- to the type of packet, and packs an appropriate buffer in network-byte
- order suitable for sending over the wire.
-
- This is an abstract method."""
- raise NotImplementedError("Abstract method")
-
- def decode(self):
- """The decode method of a TftpPacket takes a buffer off of the wire in
- network-byte order, and decodes it, populating internal properties as
- appropriate. This can only be done once the first 2-byte opcode has
- already been decoded, but the data section does include the entire
- datagram.
-
- This is an abstract method."""
- raise NotImplementedError("Abstract method")
-
-class TftpPacketInitial(TftpPacket, TftpPacketWithOptions):
- """This class is a common parent class for the RRQ and WRQ packets, as
- they share quite a bit of code."""
- def __init__(self):
- TftpPacket.__init__(self)
- TftpPacketWithOptions.__init__(self)
- self.filename = None
- self.mode = None
-
- def encode(self):
- """Encode the packet's buffer from the instance variables."""
- tftpassert(self.filename, "filename required in initial packet")
- tftpassert(self.mode, "mode required in initial packet")
- # Make sure filename and mode are bytestrings.
- filename = self.filename
- mode = self.mode
- if not isinstance(filename, bytes):
- filename = filename.encode('ascii')
- if not isinstance(self.mode, bytes):
- mode = mode.encode('ascii')
-
- ptype = None
- if self.opcode == 1: ptype = "RRQ"
- else: ptype = "WRQ"
- log.debug("Encoding %s packet, filename = %s, mode = %s",
- ptype, filename, mode)
- for key in self.options:
- log.debug(" Option %s = %s", key, self.options[key])
-
- fmt = b"!H"
- fmt += b"%dsx" % len(filename)
- if mode == b"octet":
- fmt += b"5sx"
- else:
- raise AssertionError("Unsupported mode: %s" % mode)
- # Add options. Note that the options list must be bytes.
- options_list = []
- if len(list(self.options.keys())) > 0:
- log.debug("there are options to encode")
- for key in self.options:
- # Populate the option name
- name = key
- if not isinstance(name, bytes):
- name = name.encode('ascii')
- options_list.append(name)
- fmt += b"%dsx" % len(name)
- # Populate the option value
- value = self.options[key]
- # Work with all strings.
- if isinstance(value, int):
- value = str(value)
- if not isinstance(value, bytes):
- value = value.encode('ascii')
- options_list.append(value)
- fmt += b"%dsx" % len(value)
-
- log.debug("fmt is %s", fmt)
- log.debug("options_list is %s", options_list)
- log.debug("size of struct is %d", struct.calcsize(fmt))
-
- self.buffer = struct.pack(fmt,
- self.opcode,
- filename,
- mode,
- *options_list)
-
- log.debug("buffer is %s", repr(self.buffer))
- return self
-
- def decode(self):
- tftpassert(self.buffer, "Can't decode, buffer is empty")
-
- # FIXME - this shares a lot of code with decode_options
- nulls = 0
- fmt = b""
- nulls = length = tlength = 0
- log.debug("in decode: about to iterate buffer counting nulls")
- subbuf = self.buffer[2:]
- for i in range(len(subbuf)):
- if ord(subbuf[i:i+1]) == 0:
- nulls += 1
- log.debug("found a null at length %d, now have %d", length, nulls)
- fmt += b"%dsx" % length
- length = -1
- # At 2 nulls, we want to mark that position for decoding.
- if nulls == 2:
- break
- length += 1
- tlength += 1
-
- log.debug("hopefully found end of mode at length %d", tlength)
- # length should now be the end of the mode.
- tftpassert(nulls == 2, "malformed packet")
- shortbuf = subbuf[:tlength+1]
- log.debug("about to unpack buffer with fmt: %s", fmt)
- log.debug("unpacking buffer: %s", repr(shortbuf))
- mystruct = struct.unpack(fmt, shortbuf)
-
- tftpassert(len(mystruct) == 2, "malformed packet")
- self.filename = mystruct[0].decode('ascii')
- self.mode = mystruct[1].decode('ascii').lower() # force lc - bug 17
- log.debug("set filename to %s", self.filename)
- log.debug("set mode to %s", self.mode)
-
- self.options = self.decode_options(subbuf[tlength+1:])
- log.debug("options dict is now %s", self.options)
- return self
-
-class TftpPacketRRQ(TftpPacketInitial):
- """
-::
-
- 2 bytes string 1 byte string 1 byte
- -----------------------------------------------
- RRQ/ | 01/02 | Filename | 0 | Mode | 0 |
- WRQ -----------------------------------------------
- """
- def __init__(self):
- TftpPacketInitial.__init__(self)
- self.opcode = 1
-
- def __str__(self):
- s = 'RRQ packet: filename = %s' % self.filename
- s += ' mode = %s' % self.mode
- if self.options:
- s += '\n options = %s' % self.options
- return s
-
-class TftpPacketWRQ(TftpPacketInitial):
- """
-::
-
- 2 bytes string 1 byte string 1 byte
- -----------------------------------------------
- RRQ/ | 01/02 | Filename | 0 | Mode | 0 |
- WRQ -----------------------------------------------
- """
- def __init__(self):
- TftpPacketInitial.__init__(self)
- self.opcode = 2
-
- def __str__(self):
- s = 'WRQ packet: filename = %s' % self.filename
- s += ' mode = %s' % self.mode
- if self.options:
- s += '\n options = %s' % self.options
- return s
-
-class TftpPacketDAT(TftpPacket):
- """
-::
-
- 2 bytes 2 bytes n bytes
- ---------------------------------
- DATA | 03 | Block # | Data |
- ---------------------------------
- """
- def __init__(self):
- TftpPacket.__init__(self)
- self.opcode = 3
- self.blocknumber = 0
- self.data = None
-
- def __str__(self):
- s = 'DAT packet: block %s' % self.blocknumber
- if self.data:
- s += '\n data: %d bytes' % len(self.data)
- return s
-
- def encode(self):
- """Encode the DAT packet. This method populates self.buffer, and
- returns self for easy method chaining."""
- if len(self.data) == 0:
- log.debug("Encoding an empty DAT packet")
- data = self.data
- if not isinstance(self.data, bytes):
- data = self.data.encode('ascii')
- fmt = b"!HH%ds" % len(data)
- self.buffer = struct.pack(fmt,
- self.opcode,
- self.blocknumber,
- data)
- return self
-
- def decode(self):
- """Decode self.buffer into instance variables. It returns self for
- easy method chaining."""
- # We know the first 2 bytes are the opcode. The second two are the
- # block number.
- (self.blocknumber,) = struct.unpack(str("!H"), self.buffer[2:4])
- log.debug("decoding DAT packet, block number %d", self.blocknumber)
- log.debug("should be %d bytes in the packet total", len(self.buffer))
- # Everything else is data.
- self.data = self.buffer[4:]
- log.debug("found %d bytes of data", len(self.data))
- return self
-
-class TftpPacketACK(TftpPacket):
- """
-::
-
- 2 bytes 2 bytes
- -------------------
- ACK | 04 | Block # |
- --------------------
- """
- def __init__(self):
- TftpPacket.__init__(self)
- self.opcode = 4
- self.blocknumber = 0
-
- def __str__(self):
- return 'ACK packet: block %d' % self.blocknumber
-
- def encode(self):
- log.debug("encoding ACK: opcode = %d, block = %d",
- self.opcode, self.blocknumber)
- self.buffer = struct.pack(str("!HH"), self.opcode, self.blocknumber)
- return self
-
- def decode(self):
- if len(self.buffer) > 4:
- log.debug("detected TFTP ACK but request is too large, will truncate")
- log.debug("buffer was: %s", repr(self.buffer))
- self.buffer = self.buffer[0:4]
- self.opcode, self.blocknumber = struct.unpack(str("!HH"), self.buffer)
- log.debug("decoded ACK packet: opcode = %d, block = %d",
- self.opcode, self.blocknumber)
- return self
-
-class TftpPacketERR(TftpPacket):
- """
-::
-
- 2 bytes 2 bytes string 1 byte
- ----------------------------------------
- ERROR | 05 | ErrorCode | ErrMsg | 0 |
- ----------------------------------------
-
- Error Codes
-
- Value Meaning
-
- 0 Not defined, see error message (if any).
- 1 File not found.
- 2 Access violation.
- 3 Disk full or allocation exceeded.
- 4 Illegal TFTP operation.
- 5 Unknown transfer ID.
- 6 File already exists.
- 7 No such user.
- 8 Failed to negotiate options
- """
- def __init__(self):
- TftpPacket.__init__(self)
- self.opcode = 5
- self.errorcode = 0
- # FIXME: We don't encode the errmsg...
- self.errmsg = None
- # FIXME - integrate in TftpErrors references?
- self.errmsgs = {
- 1: b"File not found",
- 2: b"Access violation",
- 3: b"Disk full or allocation exceeded",
- 4: b"Illegal TFTP operation",
- 5: b"Unknown transfer ID",
- 6: b"File already exists",
- 7: b"No such user",
- 8: b"Failed to negotiate options"
- }
-
- def __str__(self):
- s = 'ERR packet: errorcode = %d' % self.errorcode
- s += '\n msg = %s' % self.errmsgs.get(self.errorcode, '')
- return s
-
- def encode(self):
- """Encode the DAT packet based on instance variables, populating
- self.buffer, returning self."""
- fmt = b"!HH%dsx" % len(self.errmsgs[self.errorcode])
- log.debug("encoding ERR packet with fmt %s", fmt)
- self.buffer = struct.pack(fmt,
- self.opcode,
- self.errorcode,
- self.errmsgs[self.errorcode])
- return self
-
- def decode(self):
- "Decode self.buffer, populating instance variables and return self."
- buflen = len(self.buffer)
- tftpassert(buflen >= 4, "malformed ERR packet, too short")
- log.debug("Decoding ERR packet, length %s bytes", buflen)
- if buflen == 4:
- log.debug("Allowing this affront to the RFC of a 4-byte packet")
- fmt = b"!HH"
- log.debug("Decoding ERR packet with fmt: %s", fmt)
- self.opcode, self.errorcode = struct.unpack(fmt,
- self.buffer)
- else:
- log.debug("Good ERR packet > 4 bytes")
- fmt = b"!HH%dsx" % (len(self.buffer) - 5)
- log.debug("Decoding ERR packet with fmt: %s", fmt)
- self.opcode, self.errorcode, self.errmsg = struct.unpack(fmt,
- self.buffer)
- log.error("ERR packet - errorcode: %d, message: %s"
- % (self.errorcode, self.errmsg))
- return self
-
-class TftpPacketOACK(TftpPacket, TftpPacketWithOptions):
- """
-::
-
- +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
- | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
- +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
- """
- def __init__(self):
- TftpPacket.__init__(self)
- TftpPacketWithOptions.__init__(self)
- self.opcode = 6
-
- def __str__(self):
- return 'OACK packet:\n options = %s' % self.options
-
- def encode(self):
- fmt = b"!H" # opcode
- options_list = []
- log.debug("in TftpPacketOACK.encode")
- for key in self.options:
- value = self.options[key]
- if isinstance(value, int):
- value = str(value)
- if not isinstance(key, bytes):
- key = key.encode('ascii')
- if not isinstance(value, bytes):
- value = value.encode('ascii')
- log.debug("looping on option key %s", key)
- log.debug("value is %s", value)
- fmt += b"%dsx" % len(key)
- fmt += b"%dsx" % len(value)
- options_list.append(key)
- options_list.append(value)
- self.buffer = struct.pack(fmt, self.opcode, *options_list)
- return self
-
- def decode(self):
- self.options = self.decode_options(self.buffer[2:])
- return self
-
- def match_options(self, options):
- """This method takes a set of options, and tries to match them with
- its own. It can accept some changes in those options from the server as
- part of a negotiation. Changed or unchanged, it will return a dict of
- the options so that the session can update itself to the negotiated
- options."""
- for name in self.options:
- if name in options:
- if name == 'blksize':
- # We can accept anything between the min and max values.
- size = int(self.options[name])
- if size >= MIN_BLKSIZE and size <= MAX_BLKSIZE:
- log.debug("negotiated blksize of %d bytes", size)
- options['blksize'] = size
- else:
- raise TftpException("blksize %s option outside allowed range" % size)
- elif name == 'tsize':
- size = int(self.options[name])
- if size < 0:
- raise TftpException("Negative file sizes not supported")
- else:
- raise TftpException("Unsupported option: %s" % name)
- return True
diff --git a/tester/rt/tftpy/TftpServer.py b/tester/rt/tftpy/TftpServer.py
deleted file mode 100644
index 8dc6d78..0000000
--- a/tester/rt/tftpy/TftpServer.py
+++ /dev/null
@@ -1,266 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module implements the TFTP Server functionality. Instantiate an
-instance of the server, and then run the listen() method to listen for client
-requests. Logging is performed via a standard logging object set in
-TftpShared."""
-
-
-import socket, os, time
-import select
-import threading
-import logging
-from errno import EINTR
-from .TftpShared import *
-from .TftpPacketTypes import *
-from .TftpPacketFactory import TftpPacketFactory
-from .TftpContexts import TftpContextServer
-
-log = logging.getLogger('tftpy.TftpServer')
-
-class TftpServer(TftpSession):
- """This class implements a tftp server object. Run the listen() method to
- listen for client requests.
-
- tftproot is the path to the tftproot directory to serve files from and/or
- write them to.
-
- dyn_file_func is a callable that takes a requested download
- path that is not present on the file system and must return either a
- file-like object to read from or None if the path should appear as not
- found. This permits the serving of dynamic content.
-
- upload_open is a callable that is triggered on every upload with the
- requested destination path and server context. It must either return a
- file-like object ready for writing or None if the path is invalid."""
-
- def __init__(self,
- tftproot='/tftpboot',
- dyn_file_func=None,
- upload_open=None):
- self.listenip = None
- self.listenport = None
- self.sock = None
- # FIXME: What about multiple roots?
- self.root = os.path.abspath(tftproot)
- self.dyn_file_func = dyn_file_func
- self.upload_open = upload_open
- # A dict of sessions, where each session is keyed by a string like
- # ip:tid for the remote end.
- self.sessions = {}
- # A threading event to help threads synchronize with the server
- # is_running state.
- self.is_running = threading.Event()
-
- self.shutdown_gracefully = False
- self.shutdown_immediately = False
-
- for name in 'dyn_file_func', 'upload_open':
- attr = getattr(self, name)
- if attr and not callable(attr):
- raise TftpException("{} supplied, but it is not callable.".format(name))
- if os.path.exists(self.root):
- log.debug("tftproot %s does exist", self.root)
- if not os.path.isdir(self.root):
- raise TftpException("The tftproot must be a directory.")
- else:
- log.debug("tftproot %s is a directory" % self.root)
- if os.access(self.root, os.R_OK):
- log.debug("tftproot %s is readable" % self.root)
- else:
- raise TftpException("The tftproot must be readable")
- if os.access(self.root, os.W_OK):
- log.debug("tftproot %s is writable" % self.root)
- else:
- log.warning("The tftproot %s is not writable" % self.root)
- else:
- raise TftpException("The tftproot does not exist.")
-
- def __del__(self):
- if self.sock is not None:
- try:
- self.sock.close()
- except:
- pass
-
- def listen(self, listenip="", listenport=DEF_TFTP_PORT,
- timeout=SOCK_TIMEOUT):
- """Start a server listening on the supplied interface and port. This
- defaults to INADDR_ANY (all interfaces) and UDP port 69. You can also
- supply a different socket timeout value, if desired."""
- tftp_factory = TftpPacketFactory()
-
- # Don't use new 2.5 ternary operator yet
- # listenip = listenip if listenip else '0.0.0.0'
- if not listenip: listenip = '0.0.0.0'
- log.info("Server requested on ip %s, port %s" % (listenip, listenport))
- try:
- # FIXME - sockets should be non-blocking
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.sock.bind((listenip, listenport))
- _, self.listenport = self.sock.getsockname()
- except socket.error as err:
- # Reraise it for now.
- raise err
-
- self.is_running.set()
-
- log.info("Starting receive loop...")
- while True:
- log.debug("shutdown_immediately is %s" % self.shutdown_immediately)
- log.debug("shutdown_gracefully is %s" % self.shutdown_gracefully)
- if self.shutdown_immediately:
- log.warning("Shutting down now. Session count: %d" %
- len(self.sessions))
- self.sock.close()
- for key in self.sessions:
- self.sessions[key].end()
- self.sessions = []
- break
-
- elif self.shutdown_gracefully:
- if not self.sessions:
- log.warning("In graceful shutdown mode and all "
- "sessions complete.")
- self.sock.close()
- break
-
- # Build the inputlist array of sockets to select() on.
- inputlist = []
- inputlist.append(self.sock)
- for key in self.sessions:
- inputlist.append(self.sessions[key].sock)
-
- # Block until some socket has input on it.
- log.debug("Performing select on this inputlist: %s", inputlist)
- try:
- readyinput, readyoutput, readyspecial = \
- select.select(inputlist, [], [], SOCK_TIMEOUT)
- except select.error as err:
- if err[0] == EINTR:
- # Interrupted system call
- log.debug("Interrupted syscall, retrying")
- continue
- else:
- raise
-
- deletion_list = []
-
- # Handle the available data, if any. Maybe we timed-out.
- for readysock in readyinput:
- # Is the traffic on the main server socket? ie. new session?
- if readysock == self.sock:
- log.debug("Data ready on our main socket")
- buffer, (raddress, rport) = self.sock.recvfrom(MAX_BLKSIZE)
-
- log.debug("Read %d bytes", len(buffer))
-
- if self.shutdown_gracefully:
- log.warning("Discarding data on main port, "
- "in graceful shutdown mode")
- continue
-
- # Forge a session key based on the client's IP and port,
- # which should safely work through NAT.
- key = "%s:%s" % (raddress, rport)
-
- if not key in self.sessions:
- log.debug("Creating new server context for "
- "session key = %s" % key)
- self.sessions[key] = TftpContextServer(raddress,
- rport,
- timeout,
- self.root,
- self.dyn_file_func,
- self.upload_open)
- try:
- self.sessions[key].start(buffer)
- except TftpException as err:
- deletion_list.append(key)
- log.error("Fatal exception thrown from "
- "session %s: %s" % (key, str(err)))
- else:
- log.warning("received traffic on main socket for "
- "existing session??")
- log.info("Currently handling these sessions:")
- for session_key, session in list(self.sessions.items()):
- log.info(" %s" % session)
-
- else:
- # Must find the owner of this traffic.
- for key in self.sessions:
- if readysock == self.sessions[key].sock:
- log.debug("Matched input to session key %s"
- % key)
- try:
- self.sessions[key].cycle()
- if self.sessions[key].state == None:
- log.info("Successful transfer.")
- deletion_list.append(key)
- except TftpException as err:
- deletion_list.append(key)
- log.error("Fatal exception thrown from "
- "session %s: %s"
- % (key, str(err)))
- # Break out of for loop since we found the correct
- # session.
- break
- else:
- log.error("Can't find the owner for this packet. "
- "Discarding.")
-
- log.debug("Looping on all sessions to check for timeouts")
- now = time.time()
- for key in self.sessions:
- try:
- self.sessions[key].checkTimeout(now)
- except TftpTimeout as err:
- log.error(str(err))
- self.sessions[key].retry_count += 1
- if self.sessions[key].retry_count >= TIMEOUT_RETRIES:
- log.debug("hit max retries on %s, giving up" %
- self.sessions[key])
- deletion_list.append(key)
- else:
- log.debug("resending on session %s" % self.sessions[key])
- self.sessions[key].state.resendLast()
-
- log.debug("Iterating deletion list.")
- for key in deletion_list:
- log.info('')
- log.info("Session %s complete" % key)
- if key in self.sessions:
- log.debug("Gathering up metrics from session before deleting")
- self.sessions[key].end()
- metrics = self.sessions[key].metrics
- if metrics.duration == 0:
- log.info("Duration too short, rate undetermined")
- else:
- log.info("Transferred %d bytes in %.2f seconds"
- % (metrics.bytes, metrics.duration))
- log.info("Average rate: %.2f kbps" % metrics.kbps)
- log.info("%.2f bytes in resent data" % metrics.resent_bytes)
- log.info("%d duplicate packets" % metrics.dupcount)
- log.debug("Deleting session %s" % key)
- del self.sessions[key]
- log.debug("Session list is now %s" % self.sessions)
- else:
- log.warning(
- "Strange, session %s is not on the deletion list" % key)
-
- self.is_running.clear()
-
- log.debug("server returning from while loop")
- self.shutdown_gracefully = self.shutdown_immediately = False
-
- def stop(self, now=False):
- """Stop the server gracefully. Do not take any new transfers,
- but complete the existing ones. If force is True, drop everything
- and stop. Note, immediately will not interrupt the select loop, it
- will happen when the server returns on ready data, or a timeout.
- ie. SOCK_TIMEOUT"""
- if now:
- self.shutdown_immediately = True
- else:
- self.shutdown_gracefully = True
diff --git a/tester/rt/tftpy/TftpShared.py b/tester/rt/tftpy/TftpShared.py
deleted file mode 100644
index 88530c3..0000000
--- a/tester/rt/tftpy/TftpShared.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module holds all objects shared by all other modules in tftpy."""
-
-
-
-MIN_BLKSIZE = 8
-DEF_BLKSIZE = 512
-MAX_BLKSIZE = 65536
-SOCK_TIMEOUT = 5
-MAX_DUPS = 20
-TIMEOUT_RETRIES = 5
-DEF_TFTP_PORT = 69
-
-# A hook for deliberately introducing delay in testing.
-DELAY_BLOCK = 0
-
-def tftpassert(condition, msg):
- """This function is a simple utility that will check the condition
- passed for a false state. If it finds one, it throws a TftpException
- with the message passed. This just makes the code throughout cleaner
- by refactoring."""
- if not condition:
- raise TftpException(msg)
-
-class TftpErrors(object):
- """This class is a convenience for defining the common tftp error codes,
- and making them more readable in the code."""
- NotDefined = 0
- FileNotFound = 1
- AccessViolation = 2
- DiskFull = 3
- IllegalTftpOp = 4
- UnknownTID = 5
- FileAlreadyExists = 6
- NoSuchUser = 7
- FailedNegotiation = 8
-
-class TftpException(Exception):
- """This class is the parent class of all exceptions regarding the handling
- of the TFTP protocol."""
- pass
-
-class TftpTimeout(TftpException):
- """This class represents a timeout error waiting for a response from the
- other end."""
- pass
-
-class TftpFileNotFoundError(TftpException):
- """This class represents an error condition where we received a file
- not found error."""
- pass
diff --git a/tester/rt/tftpy/TftpStates.py b/tester/rt/tftpy/TftpStates.py
deleted file mode 100644
index 42bac1d..0000000
--- a/tester/rt/tftpy/TftpStates.py
+++ /dev/null
@@ -1,611 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""This module implements all state handling during uploads and downloads, the
-main interface to which being the TftpState base class.
-
-The concept is simple. Each context object represents a single upload or
-download, and the state object in the context object represents the current
-state of that transfer. The state object has a handle() method that expects
-the next packet in the transfer, and returns a state object until the transfer
-is complete, at which point it returns None. That is, unless there is a fatal
-error, in which case a TftpException is returned instead."""
-
-
-from .TftpShared import *
-from .TftpPacketTypes import *
-import os
-import logging
-
-log = logging.getLogger('tftpy.TftpStates')
-
-###############################################################################
-# State classes
-###############################################################################
-
-class TftpState(object):
- """The base class for the states."""
-
- def __init__(self, context):
- """Constructor for setting up common instance variables. The involved
- file object is required, since in tftp there's always a file
- involved."""
- self.context = context
-
- def handle(self, pkt, raddress, rport):
- """An abstract method for handling a packet. It is expected to return
- a TftpState object, either itself or a new state."""
- raise NotImplementedError("Abstract method")
-
- def handleOACK(self, pkt):
- """This method handles an OACK from the server, syncing any accepted
- options."""
- if len(pkt.options.keys()) > 0:
- if pkt.match_options(self.context.options):
- log.info("Successful negotiation of options")
- # Set options to OACK options
- self.context.options = pkt.options
- for key in self.context.options:
- log.info(" %s = %s" % (key, self.context.options[key]))
- else:
- log.error("Failed to negotiate options")
- raise TftpException("Failed to negotiate options")
- else:
- raise TftpException("No options found in OACK")
-
- def returnSupportedOptions(self, options):
- """This method takes a requested options list from a client, and
- returns the ones that are supported."""
- # We support the options blksize and tsize right now.
- # FIXME - put this somewhere else?
- accepted_options = {}
- for option in options:
- if option == 'blksize':
- # Make sure it's valid.
- if int(options[option]) > MAX_BLKSIZE:
- log.info("Client requested blksize greater than %d "
- "setting to maximum" % MAX_BLKSIZE)
- accepted_options[option] = MAX_BLKSIZE
- elif int(options[option]) < MIN_BLKSIZE:
- log.info("Client requested blksize less than %d "
- "setting to minimum" % MIN_BLKSIZE)
- accepted_options[option] = MIN_BLKSIZE
- else:
- accepted_options[option] = options[option]
- elif option == 'tsize':
- log.debug("tsize option is set")
- accepted_options['tsize'] = 0
- else:
- log.info("Dropping unsupported option '%s'" % option)
- log.debug("Returning these accepted options: %s", accepted_options)
- return accepted_options
-
- def sendDAT(self):
- """This method sends the next DAT packet based on the data in the
- context. It returns a boolean indicating whether the transfer is
- finished."""
- finished = False
- blocknumber = self.context.next_block
- # Test hook
- if DELAY_BLOCK and DELAY_BLOCK == blocknumber:
- import time
- log.debug("Deliberately delaying 10 seconds...")
- time.sleep(10)
- dat = None
- blksize = self.context.getBlocksize()
- buffer = self.context.fileobj.read(blksize)
- log.debug("Read %d bytes into buffer", len(buffer))
- if len(buffer) < blksize:
- log.info("Reached EOF on file %s"
- % self.context.file_to_transfer)
- finished = True
- dat = TftpPacketDAT()
- dat.data = buffer
- dat.blocknumber = blocknumber
- self.context.metrics.bytes += len(dat.data)
- log.debug("Sending DAT packet %d", dat.blocknumber)
- self.context.sock.sendto(dat.encode().buffer,
- (self.context.host, self.context.tidport))
- if self.context.packethook:
- self.context.packethook(dat)
- self.context.last_pkt = dat
- return finished
-
- def sendACK(self, blocknumber=None):
- """This method sends an ack packet to the block number specified. If
- none is specified, it defaults to the next_block property in the
- parent context."""
- log.debug("In sendACK, passed blocknumber is %s", blocknumber)
- if blocknumber is None:
- blocknumber = self.context.next_block
- log.info("Sending ack to block %d" % blocknumber)
- ackpkt = TftpPacketACK()
- ackpkt.blocknumber = blocknumber
- self.context.sock.sendto(ackpkt.encode().buffer,
- (self.context.host,
- self.context.tidport))
- self.context.last_pkt = ackpkt
-
- def sendError(self, errorcode):
- """This method uses the socket passed, and uses the errorcode to
- compose and send an error packet."""
- log.debug("In sendError, being asked to send error %d", errorcode)
- errpkt = TftpPacketERR()
- errpkt.errorcode = errorcode
- if self.context.tidport == None:
- log.debug("Error packet received outside session. Discarding")
- else:
- self.context.sock.sendto(errpkt.encode().buffer,
- (self.context.host,
- self.context.tidport))
- self.context.last_pkt = errpkt
-
- def sendOACK(self):
- """This method sends an OACK packet with the options from the current
- context."""
- log.debug("In sendOACK with options %s", self.context.options)
- pkt = TftpPacketOACK()
- pkt.options = self.context.options
- self.context.sock.sendto(pkt.encode().buffer,
- (self.context.host,
- self.context.tidport))
- self.context.last_pkt = pkt
-
- def resendLast(self):
- "Resend the last sent packet due to a timeout."
- log.warning("Resending packet %s on sessions %s"
- % (self.context.last_pkt, self))
- self.context.metrics.resent_bytes += len(self.context.last_pkt.buffer)
- self.context.metrics.add_dup(self.context.last_pkt)
- sendto_port = self.context.tidport
- if not sendto_port:
- # If the tidport wasn't set, then the remote end hasn't even
- # started talking to us yet. That's not good. Maybe it's not
- # there.
- sendto_port = self.context.port
- self.context.sock.sendto(self.context.last_pkt.encode().buffer,
- (self.context.host, sendto_port))
- if self.context.packethook:
- self.context.packethook(self.context.last_pkt)
-
- def handleDat(self, pkt):
- """This method handles a DAT packet during a client download, or a
- server upload."""
- log.info("Handling DAT packet - block %d" % pkt.blocknumber)
- log.debug("Expecting block %s", self.context.next_block)
- if pkt.blocknumber == self.context.next_block:
- log.debug("Good, received block %d in sequence", pkt.blocknumber)
-
- self.sendACK()
- self.context.next_block += 1
-
- log.debug("Writing %d bytes to output file", len(pkt.data))
- self.context.fileobj.write(pkt.data)
- self.context.metrics.bytes += len(pkt.data)
- # Check for end-of-file, any less than full data packet.
- if len(pkt.data) < self.context.getBlocksize():
- log.info("End of file detected")
- return None
-
- elif pkt.blocknumber < self.context.next_block:
- if pkt.blocknumber == 0:
- log.warning("There is no block zero!")
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("There is no block zero!")
- log.warning("Dropping duplicate block %d" % pkt.blocknumber)
- self.context.metrics.add_dup(pkt)
- log.debug("ACKing block %d again, just in case", pkt.blocknumber)
- self.sendACK(pkt.blocknumber)
-
- else:
- # FIXME: should we be more tolerant and just discard instead?
- msg = "Whoa! Received future block %d but expected %d" \
- % (pkt.blocknumber, self.context.next_block)
- log.error(msg)
- raise TftpException(msg)
-
- # Default is to ack
- return TftpStateExpectDAT(self.context)
-
-class TftpServerState(TftpState):
- """The base class for server states."""
-
- def __init__(self, context):
- TftpState.__init__(self, context)
-
- # This variable is used to store the absolute path to the file being
- # managed.
- self.full_path = None
-
- def serverInitial(self, pkt, raddress, rport):
- """This method performs initial setup for a server context transfer,
- put here to refactor code out of the TftpStateServerRecvRRQ and
- TftpStateServerRecvWRQ classes, since their initial setup is
- identical. The method returns a boolean, sendoack, to indicate whether
- it is required to send an OACK to the client."""
- options = pkt.options
- sendoack = False
- if not self.context.tidport:
- self.context.tidport = rport
- log.info("Setting tidport to %s" % rport)
-
- log.debug("Setting default options, blksize")
- self.context.options = { 'blksize': DEF_BLKSIZE }
-
- if options:
- log.debug("Options requested: %s", options)
- supported_options = self.returnSupportedOptions(options)
- self.context.options.update(supported_options)
- sendoack = True
-
- # FIXME - only octet mode is supported at this time.
- if pkt.mode != 'octet':
- #self.sendError(TftpErrors.IllegalTftpOp)
- #raise TftpException("Only octet transfers are supported at this time.")
- log.warning("Received non-octet mode request. I'll reply with binary data.")
-
- # test host/port of client end
- if self.context.host != raddress or self.context.port != rport:
- self.sendError(TftpErrors.UnknownTID)
- log.error("Expected traffic from %s:%s but received it "
- "from %s:%s instead."
- % (self.context.host,
- self.context.port,
- raddress,
- rport))
- # FIXME: increment an error count?
- # Return same state, we're still waiting for valid traffic.
- return self
-
- log.debug("Requested filename is %s", pkt.filename)
-
- # Build the filename on this server and ensure it is contained
- # in the specified root directory.
- #
- # Filenames that begin with server root are accepted. It's
- # assumed the client and server are tightly connected and this
- # provides backwards compatibility.
- #
- # Filenames otherwise are relative to the server root. If they
- # begin with a '/' strip it off as otherwise os.path.join will
- # treat it as absolute (regardless of whether it is ntpath or
- # posixpath module
- if pkt.filename.startswith(self.context.root):
- full_path = pkt.filename
- else:
- full_path = os.path.join(self.context.root, pkt.filename.lstrip('/'))
-
- # Use abspath to eliminate any remaining relative elements
- # (e.g. '..') and ensure that is still within the server's
- # root directory
- self.full_path = os.path.abspath(full_path)
- log.debug("full_path is %s", full_path)
- if self.full_path.startswith(self.context.root):
- log.info("requested file is in the server root - good")
- else:
- log.warning("requested file is not within the server root - bad")
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("bad file path")
-
- self.context.file_to_transfer = pkt.filename
-
- return sendoack
-
-
-class TftpStateServerRecvRRQ(TftpServerState):
- """This class represents the state of the TFTP server when it has just
- received an RRQ packet."""
- def handle(self, pkt, raddress, rport):
- "Handle an initial RRQ packet as a server."
- log.debug("In TftpStateServerRecvRRQ.handle")
- sendoack = self.serverInitial(pkt, raddress, rport)
- path = self.full_path
- log.info("Opening file %s for reading" % path)
- if os.path.exists(path):
- # Note: Open in binary mode for win32 portability, since win32
- # blows.
- self.context.fileobj = open(path, "rb")
- elif self.context.dyn_file_func:
- log.debug("No such file %s but using dyn_file_func", path)
- self.context.fileobj = \
- self.context.dyn_file_func(self.context.file_to_transfer, raddress=raddress, rport=rport)
-
- if self.context.fileobj is None:
- log.debug("dyn_file_func returned 'None', treating as "
- "FileNotFound")
- self.sendError(TftpErrors.FileNotFound)
- raise TftpException("File not found: %s" % path)
- else:
- log.warn("File not found: %s", path)
- self.sendError(TftpErrors.FileNotFound)
- raise TftpException("File not found: {}".format(path))
-
- # Options negotiation.
- if sendoack and 'tsize' in self.context.options:
- # getting the file size for the tsize option. As we handle
- # file-like objects and not only real files, we use this seeking
- # method instead of asking the OS
- self.context.fileobj.seek(0, os.SEEK_END)
- tsize = str(self.context.fileobj.tell())
- self.context.fileobj.seek(0, 0)
- self.context.options['tsize'] = tsize
-
- if sendoack:
- # Note, next_block is 0 here since that's the proper
- # acknowledgement to an OACK.
- # FIXME: perhaps we do need a TftpStateExpectOACK class...
- self.sendOACK()
- # Note, self.context.next_block is already 0.
- else:
- self.context.next_block = 1
- log.debug("No requested options, starting send...")
- self.context.pending_complete = self.sendDAT()
- # Note, we expect an ack regardless of whether we sent a DAT or an
- # OACK.
- return TftpStateExpectACK(self.context)
-
- # Note, we don't have to check any other states in this method, that's
- # up to the caller.
-
-class TftpStateServerRecvWRQ(TftpServerState):
- """This class represents the state of the TFTP server when it has just
- received a WRQ packet."""
- def make_subdirs(self):
- """The purpose of this method is to, if necessary, create all of the
- subdirectories leading up to the file to the written."""
- # Pull off everything below the root.
- subpath = self.full_path[len(self.context.root):]
- log.debug("make_subdirs: subpath is %s", subpath)
- # Split on directory separators, but drop the last one, as it should
- # be the filename.
- dirs = subpath.split(os.sep)[:-1]
- log.debug("dirs is %s", dirs)
- current = self.context.root
- for dir in dirs:
- if dir:
- current = os.path.join(current, dir)
- if os.path.isdir(current):
- log.debug("%s is already an existing directory", current)
- else:
- os.mkdir(current, 0o700)
-
- def handle(self, pkt, raddress, rport):
- "Handle an initial WRQ packet as a server."
- log.debug("In TftpStateServerRecvWRQ.handle")
- sendoack = self.serverInitial(pkt, raddress, rport)
- path = self.full_path
- if self.context.upload_open:
- f = self.context.upload_open(path, self.context)
- if f is None:
- self.sendError(TftpErrors.AccessViolation)
- raise TftpException("Dynamic path %s not permitted" % path)
- else:
- self.context.fileobj = f
- else:
- log.info("Opening file %s for writing" % path)
- if os.path.exists(path):
- # FIXME: correct behavior?
- log.warning("File %s exists already, overwriting..." % (
- self.context.file_to_transfer))
- # FIXME: I think we should upload to a temp file and not overwrite
- # the existing file until the file is successfully uploaded.
- self.make_subdirs()
- self.context.fileobj = open(path, "wb")
-
- # Options negotiation.
- if sendoack:
- log.debug("Sending OACK to client")
- self.sendOACK()
- else:
- log.debug("No requested options, expecting transfer to begin...")
- self.sendACK()
- # Whether we're sending an oack or not, we're expecting a DAT for
- # block 1
- self.context.next_block = 1
- # We may have sent an OACK, but we're expecting a DAT as the response
- # to either the OACK or an ACK, so lets unconditionally use the
- # TftpStateExpectDAT state.
- return TftpStateExpectDAT(self.context)
-
- # Note, we don't have to check any other states in this method, that's
- # up to the caller.
-
-class TftpStateServerStart(TftpState):
- """The start state for the server. This is a transitory state since at
- this point we don't know if we're handling an upload or a download. We
- will commit to one of them once we interpret the initial packet."""
- def handle(self, pkt, raddress, rport):
- """Handle a packet we just received."""
- log.debug("In TftpStateServerStart.handle")
- if isinstance(pkt, TftpPacketRRQ):
- log.debug("Handling an RRQ packet")
- return TftpStateServerRecvRRQ(self.context).handle(pkt,
- raddress,
- rport)
- elif isinstance(pkt, TftpPacketWRQ):
- log.debug("Handling a WRQ packet")
- return TftpStateServerRecvWRQ(self.context).handle(pkt,
- raddress,
- rport)
- else:
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Invalid packet to begin up/download: %s" % pkt)
-
-class TftpStateExpectACK(TftpState):
- """This class represents the state of the transfer when a DAT was just
- sent, and we are waiting for an ACK from the server. This class is the
- same one used by the client during the upload, and the server during the
- download."""
- def handle(self, pkt, raddress, rport):
- "Handle a packet, hopefully an ACK since we just sent a DAT."
- if isinstance(pkt, TftpPacketACK):
- log.debug("Received ACK for packet %d" % pkt.blocknumber)
- # Is this an ack to the one we just sent?
- if self.context.next_block == pkt.blocknumber:
- if self.context.pending_complete:
- log.info("Received ACK to final DAT, we're done.")
- return None
- else:
- log.debug("Good ACK, sending next DAT")
- self.context.next_block += 1
- log.debug("Incremented next_block to %d",
- self.context.next_block)
- self.context.pending_complete = self.sendDAT()
-
- elif pkt.blocknumber < self.context.next_block:
- log.warning("Received duplicate ACK for block %d"
- % pkt.blocknumber)
- self.context.metrics.add_dup(pkt)
-
- else:
- log.warning("Oooh, time warp. Received ACK to packet we "
- "didn't send yet. Discarding.")
- self.context.metrics.errors += 1
- return self
- elif isinstance(pkt, TftpPacketERR):
- log.error("Received ERR packet from peer: %s" % str(pkt))
- raise TftpException("Received ERR packet from peer: %s" % str(pkt))
- else:
- log.warning("Discarding unsupported packet: %s" % str(pkt))
- return self
-
-class TftpStateExpectDAT(TftpState):
- """Just sent an ACK packet. Waiting for DAT."""
- def handle(self, pkt, raddress, rport):
- """Handle the packet in response to an ACK, which should be a DAT."""
- if isinstance(pkt, TftpPacketDAT):
- return self.handleDat(pkt)
-
- # Every other packet type is a problem.
- elif isinstance(pkt, TftpPacketACK):
- # Umm, we ACK, you don't.
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received ACK from peer when expecting DAT")
-
- elif isinstance(pkt, TftpPacketWRQ):
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received WRQ from peer when expecting DAT")
-
- elif isinstance(pkt, TftpPacketERR):
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received ERR from peer: " + str(pkt))
-
- else:
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received unknown packet type from peer: " + str(pkt))
-
-class TftpStateSentWRQ(TftpState):
- """Just sent an WRQ packet for an upload."""
- def handle(self, pkt, raddress, rport):
- """Handle a packet we just received."""
- if not self.context.tidport:
- self.context.tidport = rport
- log.debug("Set remote port for session to %s", rport)
-
- # If we're going to successfully transfer the file, then we should see
- # either an OACK for accepted options, or an ACK to ignore options.
- if isinstance(pkt, TftpPacketOACK):
- log.info("Received OACK from server")
- try:
- self.handleOACK(pkt)
- except TftpException:
- log.error("Failed to negotiate options")
- self.sendError(TftpErrors.FailedNegotiation)
- raise
- else:
- log.debug("Sending first DAT packet")
- self.context.pending_complete = self.sendDAT()
- log.debug("Changing state to TftpStateExpectACK")
- return TftpStateExpectACK(self.context)
-
- elif isinstance(pkt, TftpPacketACK):
- log.info("Received ACK from server")
- log.debug("Apparently the server ignored our options")
- # The block number should be zero.
- if pkt.blocknumber == 0:
- log.debug("Ack blocknumber is zero as expected")
- log.debug("Sending first DAT packet")
- self.context.pending_complete = self.sendDAT()
- log.debug("Changing state to TftpStateExpectACK")
- return TftpStateExpectACK(self.context)
- else:
- log.warning("Discarding ACK to block %s" % pkt.blocknumber)
- log.debug("Still waiting for valid response from server")
- return self
-
- elif isinstance(pkt, TftpPacketERR):
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received ERR from server: %s" % pkt)
-
- elif isinstance(pkt, TftpPacketRRQ):
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received RRQ from server while in upload")
-
- elif isinstance(pkt, TftpPacketDAT):
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received DAT from server while in upload")
-
- else:
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received unknown packet type from server: %s" % pkt)
-
- # By default, no state change.
- return self
-
-class TftpStateSentRRQ(TftpState):
- """Just sent an RRQ packet."""
- def handle(self, pkt, raddress, rport):
- """Handle the packet in response to an RRQ to the server."""
- if not self.context.tidport:
- self.context.tidport = rport
- log.info("Set remote port for session to %s" % rport)
-
- # Now check the packet type and dispatch it properly.
- if isinstance(pkt, TftpPacketOACK):
- log.info("Received OACK from server")
- try:
- self.handleOACK(pkt)
- except TftpException as err:
- log.error("Failed to negotiate options: %s" % str(err))
- self.sendError(TftpErrors.FailedNegotiation)
- raise
- else:
- log.debug("Sending ACK to OACK")
-
- self.sendACK(blocknumber=0)
-
- log.debug("Changing state to TftpStateExpectDAT")
- return TftpStateExpectDAT(self.context)
-
- elif isinstance(pkt, TftpPacketDAT):
- # If there are any options set, then the server didn't honour any
- # of them.
- log.info("Received DAT from server")
- if self.context.options:
- log.info("Server ignored options, falling back to defaults")
- self.context.options = { 'blksize': DEF_BLKSIZE }
- return self.handleDat(pkt)
-
- # Every other packet type is a problem.
- elif isinstance(pkt, TftpPacketACK):
- # Umm, we ACK, the server doesn't.
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received ACK from server while in download")
-
- elif isinstance(pkt, TftpPacketWRQ):
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received WRQ from server while in download")
-
- elif isinstance(pkt, TftpPacketERR):
- self.sendError(TftpErrors.IllegalTftpOp)
- log.debug("Received ERR packet: %s", pkt)
- if pkt.errorcode == TftpErrors.FileNotFound:
- raise TftpFileNotFoundError("File not found")
- else:
- raise TftpException("Received ERR from server: {}".format(pkt))
-
- else:
- self.sendError(TftpErrors.IllegalTftpOp)
- raise TftpException("Received unknown packet type from server: %s" % pkt)
-
- # By default, no state change.
- return self
diff --git a/tester/rt/tftpy/__init__.py b/tester/rt/tftpy/__init__.py
deleted file mode 100644
index 71b8e3d..0000000
--- a/tester/rt/tftpy/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# vim: ts=4 sw=4 et ai:
-# -*- coding: utf8 -*-
-"""
-This library implements the tftp protocol, based on rfc 1350.
-http://www.faqs.org/rfcs/rfc1350.html
-At the moment it implements only a client class, but will include a server,
-with support for variable block sizes.
-
-As a client of tftpy, this is the only module that you should need to import
-directly. The TftpClient and TftpServer classes can be reached through it.
-"""
-
-
-import sys
-
-# Make sure that this is at least Python 2.7
-required_version = (2, 7)
-if sys.version_info < required_version:
- raise ImportError("Requires at least Python 2.7")
-
-from .TftpShared import *
-from . import TftpPacketTypes
-from . import TftpPacketFactory
-from .TftpClient import TftpClient
-from .TftpServer import TftpServer
-from . import TftpContexts
-from . import TftpStates
diff --git a/tester/rtems-bsp-builder b/tester/rtems-bsp-builder
index 4901ff0..4f47ea6 100755
--- a/tester/rtems-bsp-builder
+++ b/tester/rtems-bsp-builder
@@ -1,7 +1,7 @@
-#! /bin/sh
+#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2018 Chris Johns (chrisj@rtems.org)
+# Copyright 2016, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,15 +28,18 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
-set -e
-base=$(dirname $(dirname $0))
-cmd=tester/rt/cmd-bsp-builder.py
-PYTHON_WRAPPER=rtemstoolkit/python-wrapper.sh
-if test -f ${base}/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/${cmd}
- . ${base}/${PYTHON_WRAPPER}
-elif test -f ${base}/share/rtems/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/share/rtems/${cmd}
- . ${base}/share/rtems/${PYTHON_WRAPPER}
-fi
-echo "error: RTEMS Toolkit python wrapper not found, plrease report"
+
+from __future__ import print_function
+
+import sys, os
+
+base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+rtems = os.path.join(base, 'share', 'rtems')
+sys.path = sys.path[0:1] + [rtems, base] + sys.path[1:]
+
+try:
+ import tester.rt.check
+ tester.rt.check.run(sys.argv)
+except ImportError:
+ print("Incorrect RTEMS Tools installation", file = sys.stderr)
+ sys.exit(1)
diff --git a/tester/rtems-run b/tester/rtems-run
index cf5f263..8c8b87d 100755
--- a/tester/rtems-run
+++ b/tester/rtems-run
@@ -1,7 +1,7 @@
-#! /bin/sh
+#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2018 Chris Johns (chrisj@rtems.org)
+# Copyright 2017, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,15 +28,18 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
-set -e
-base=$(dirname $(dirname $0))
-cmd=tester/rt/cmd-run.py
-PYTHON_WRAPPER=rtemstoolkit/python-wrapper.sh
-if test -f ${base}/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/${cmd}
- . ${base}/${PYTHON_WRAPPER}
-elif test -f ${base}/share/rtems/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/share/rtems/${cmd}
- . ${base}/share/rtems/${PYTHON_WRAPPER}
-fi
-echo "error: RTEMS Toolkit python wrapper not found, plrease report"
+
+from __future__ import print_function
+
+import sys, os
+
+base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+rtems = os.path.join(base, 'share', 'rtems')
+sys.path = sys.path[0:1] + [rtems, base] + sys.path[1:]
+
+try:
+ import tester.rt.run
+ tester.rt.run.run(sys.argv)
+except ImportError:
+ print("Incorrect RTEMS Tools installation", file = sys.stderr)
+ sys.exit(1)
diff --git a/tester/rtems-test b/tester/rtems-test
index 3eb5701..ed7e0fb 100755
--- a/tester/rtems-test
+++ b/tester/rtems-test
@@ -1,7 +1,7 @@
-#! /bin/sh
+#! /usr/bin/env python
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2018 Chris Johns (chrisj@rtems.org)
+# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,15 +28,18 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
-set -e
-base=$(dirname $(dirname $0))
-cmd=tester/rt/cmd-test.py
-PYTHON_WRAPPER=rtemstoolkit/python-wrapper.sh
-if test -f ${base}/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/${cmd}
- . ${base}/${PYTHON_WRAPPER}
-elif test -f ${base}/share/rtems/${PYTHON_WRAPPER}; then
- PYTHON_CMD=${base}/share/rtems/${cmd}
- . ${base}/share/rtems/${PYTHON_WRAPPER}
-fi
-echo "error: RTEMS Toolkit python wrapper not found, plrease report"
+
+from __future__ import print_function
+
+import sys, os
+
+base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+rtems = os.path.join(base, 'share', 'rtems')
+sys.path = sys.path[0:1] + [rtems, base] + sys.path[1:]
+
+try:
+ import tester.rt.test
+ tester.rt.test.run(sys.argv)
+except ImportError:
+ print("Incorrect RTEMS Tools installation", file = sys.stderr)
+ sys.exit(1)
diff --git a/tester/rt/cmd-bsp-builder.py b/tester/rtems-tftp-server
index c94aeaf..b255121 100755
--- a/tester/rt/cmd-bsp-builder.py
+++ b/tester/rtems-tftp-server
@@ -1,25 +1,23 @@
#! /usr/bin/env python
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2016 Chris Johns (chrisj@rtems.org)
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
+# SPDX-License-Identifier: BSD-2-Clause
+'''A command line standalone TFTP Server. This is useful when testing
+and setting up a TFTP target.'''
+
+# Copyright (C) 2020 Chris Johns (chrisj@rtems.org)
#
# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
@@ -27,19 +25,21 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
+
+# pylint: disable=invalid-name
from __future__ import print_function
-import sys, os
+import os
+import sys
base = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
-rtems = os.path.dirname(base)
-sys.path = [rtems] + sys.path
+rtems = os.path.join(base, 'share', 'rtems')
+sys.path = sys.path[0:1] + [rtems, base] + sys.path[1:]
try:
- import check
- check.run(sys.argv[1:])
+ import tester.rt.tftpserver
+ tester.rt.tftpserver.run(sys.argv)
except ImportError:
- print("Incorrect RTEMS Tools installation", file = sys.stderr)
+ print("Incorrect RTEMS Tools installation", file=sys.stderr)
sys.exit(1)
diff --git a/tester/rtems/testing/bsps/arm1136jfs.ini b/tester/rtems/testing/bsps/a53_ilp32_qemu.ini
index ea8d9f1..3beba06 100644
--- a/tester/rtems/testing/bsps/arm1136jfs.ini
+++ b/tester/rtems/testing/bsps/a53_ilp32_qemu.ini
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
+# Copyright 2020 Kinsey Moore(kinsey.moore@oarcorp.com)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -29,13 +29,10 @@
#
#
-# The arm1136jfs BSP
+# The AArch64 Cortex-A53 ILP32 BSP.
#
-[arm1136jfs]
-bsp = arm1136jfs
-arch = arm
-tester = %{_rtscripts}/gdb.cfg
-gdb_script = bsp_gdb_script
-bsp_gdb_script = target sim
- load
- run
+[a53_ilp32_qemu]
+bsp = a53_ilp32_qemu
+arch = aarch64
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_opts = %{qemu_opts_base} -serial mon:stdio -machine virt,gic-version=3 -cpu cortex-a53 -m 4096
diff --git a/tester/rtems/testing/bsps/arm1136jfs-run.ini b/tester/rtems/testing/bsps/a53_lp64_qemu.ini
index 1066088..1b89284 100644
--- a/tester/rtems/testing/bsps/arm1136jfs-run.ini
+++ b/tester/rtems/testing/bsps/a53_lp64_qemu.ini
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
+# Copyright 2020 Kinsey Moore(kinsey.moore@oarcorp.com)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -29,11 +29,10 @@
#
#
-# The arm1136jfs BSP
+# The AArch64 Cortex-A53 LP64 BSP.
#
-[arm1136jfs-run]
-bsp = arm1136jfs
-arch = arm
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
-bsp_run_opts = -a -nouartrx
+[a53_lp64_qemu]
+bsp = a53_lp64_qemu
+arch = aarch64
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_opts = %{qemu_opts_base} -serial mon:stdio -machine virt,gic-version=3 -cpu cortex-a53 -m 4096
diff --git a/tester/rtems/testing/bsps/arm7tdmi.ini b/tester/rtems/testing/bsps/arm7tdmi.ini
deleted file mode 100644
index e9b1261..0000000
--- a/tester/rtems/testing/bsps/arm7tdmi.ini
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-#
-# The arm7tdmi BSP
-#
-[arm7tdmi]
-bsp = arm7tdmi
-arch = arm
-tester = %{_rtscripts}/gdb.cfg
-gdb_script = bsp_gdb_script
-bsp_gdb_script = target sim
- load
- run
diff --git a/tester/rtems/testing/bsps/arm920.ini b/tester/rtems/testing/bsps/arm920.ini
deleted file mode 100644
index 0e376c4..0000000
--- a/tester/rtems/testing/bsps/arm920.ini
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-#
-# The arm920 BSP
-#
-[arm920]
-bsp = arm920
-arch = arm
-tester = %{_rtscripts}/gdb.cfg
-gdb_script = bsp_gdb_script
-bsp_gdb_script = target sim
- load
- run
diff --git a/tester/rtems/testing/bsps/armcortexa9-run.ini b/tester/rtems/testing/bsps/armcortexa9-run.ini
deleted file mode 100644
index aa9c07f..0000000
--- a/tester/rtems/testing/bsps/armcortexa9-run.ini
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-#
-# The armcortexa9 BSP
-#
-[armcortexa9-run]
-bsp = armcortexa9
-arch = arm
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
-bsp_run_opts = -a -nouartrx
diff --git a/tester/rtems/testing/bsps/armcortexa9.ini b/tester/rtems/testing/bsps/armcortexa9.ini
deleted file mode 100644
index 1a21ea6..0000000
--- a/tester/rtems/testing/bsps/armcortexa9.ini
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
-# All rights reserved.
-#
-# This file is part of the RTEMS Tools package in 'rtems-tools'.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-#
-# The armcortexa9 BSP
-#
-[armcortexa9]
-bsp = armcortexa9
-arch = arm
-tester = %{_rtscripts}/gdb.cfg
-gdb_script = bsp_gdb_script
-bsp_gdb_script = target sim
- load
- run
diff --git a/tester/rtems/testing/bsps/generic_or1k.ini b/tester/rtems/testing/bsps/generic_or1k.ini
index 153d167..9bd139f 100644
--- a/tester/rtems/testing/bsps/generic_or1k.ini
+++ b/tester/rtems/testing/bsps/generic_or1k.ini
@@ -35,4 +35,4 @@
bsp = generic_or1k
arch = or32
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} -m 32M
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} %{qemu_opts_serial} -m 32M
diff --git a/tester/rtems/testing/bsps/erc32-run.ini b/tester/rtems/testing/bsps/gr740-sis.ini
index 3dc5f60..b71048c 100644
--- a/tester/rtems/testing/bsps/erc32-run.ini
+++ b/tester/rtems/testing/bsps/gr740-sis.ini
@@ -1,7 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
-# All rights reserved.
+# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
@@ -29,10 +28,11 @@
#
#
-# The erc32 BSP
+# The sparc/gr740 BSP
#
-[erc32-run]
-bsp = erc32
+[gr740-sis]
+bsp = gr740
arch = sparc
tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
+bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
+bsp_run_opts = -gr740 -nouartrx -r -tlim 200 s -m 4
diff --git a/tester/rtems/testing/bsps/leon3-qemu-cov.ini b/tester/rtems/testing/bsps/leon3-qemu-cov.ini
index f1cfe55..3b183e6 100644
--- a/tester/rtems/testing/bsps/leon3-qemu-cov.ini
+++ b/tester/rtems/testing/bsps/leon3-qemu-cov.ini
@@ -36,6 +36,6 @@ bsp = leon3-qemu
arch = sparc
target = sparc-rtems5
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = %{qemu_opts_base} -M leon3_generic
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_serial} -M leon3_generic
bsp_qemu_cov_opts = -exec-trace %{test_executable}.cov
bsp_covoar_cmd = -S %{bsp_symbol_path} -E %{cov_explanations}
diff --git a/tester/rtems/testing/bsps/leon3-qemu.ini b/tester/rtems/testing/bsps/leon3-qemu.ini
index 9e8854c..957b1e4 100644
--- a/tester/rtems/testing/bsps/leon3-qemu.ini
+++ b/tester/rtems/testing/bsps/leon3-qemu.ini
@@ -35,4 +35,4 @@
bsp = leon3-qemu
arch = sparc
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = %{qemu_opts_base} -M leon3_generic
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_serial} -M leon3_generic
diff --git a/tester/rtems/testing/bsps/arm7tdmi-run.ini b/tester/rtems/testing/bsps/pc-qemu.ini
index 7f98465..dd70946 100644
--- a/tester/rtems/testing/bsps/arm7tdmi-run.ini
+++ b/tester/rtems/testing/bsps/pc-qemu.ini
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
+# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -29,11 +29,11 @@
#
#
-# The arm7tdmi BSP
+# The pc QEMU BSP
#
-[arm7tdmi-run]
-bsp = arm7tdmi
-arch = arm
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
-bsp_run_opts = -a -nouartrx
+[pc-qemu]
+bsp = pc686
+arch = i386
+qemu_use_serial_console = yes
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_serial} -append --console=/dev/com1
diff --git a/tester/rtems/testing/bsps/psim-device-tree b/tester/rtems/testing/bsps/psim-device-tree
index d0b5f7c..f2feca4 100644
--- a/tester/rtems/testing/bsps/psim-device-tree
+++ b/tester/rtems/testing/bsps/psim-device-tree
@@ -4,24 +4,24 @@
/#address-cells 1
/openprom/init/register/pvr 0xfffe0000
/openprom/options/oea-memory-size 0x10000000
-##### EEPROM @ 0x0c000000 for 512K
-/eeprom@0x0c000000/reg 0x0c000000 0x80000
-/eeprom@0x0c000000/nr-sectors 8
-/eeprom@0x0c000000/sector-size 0x10000
-/eeprom@0x0c000000/byte-write-delay 1000
-/eeprom@0x0c000000/sector-start-delay 100
-/eeprom@0x0c000000/erase-delay 1000
-/eeprom@0x0c000000/manufacture-code 0x01
-/eeprom@0x0c000000/device-code 0xa4
+##### EEPROM @ 0xfc000000 for 512K
+/eeprom@0xfc000000/reg 0xfc000000 0x80000
+/eeprom@0xfc000000/nr-sectors 8
+/eeprom@0xfc000000/sector-size 0x10000
+/eeprom@0xfc000000/byte-write-delay 1000
+/eeprom@0xfc000000/sector-start-delay 100
+/eeprom@0xfc000000/erase-delay 1000
+/eeprom@0xfc000000/manufacture-code 0x01
+/eeprom@0xfc000000/device-code 0xa4
-##### NVRAM/RTC NVRAM Portion is 0x0c080000 for 512K
-##### NVRAM/RTC RTC Portion is 0x0c100000 for 12
-/nvram@0x0c080000/reg 0x0c080000 524300
-/nvram@0x0c080000/timezone -3600
+##### NVRAM/RTC NVRAM Portion is 0xfc080000 for 512K
+##### NVRAM/RTC RTC Portion is 0xfc100000 for 12
+/nvram@0xfc080000/reg 0xfc080000 524300
+/nvram@0xfc080000/timezone -3600
-##### OPENPIC @ 0x0c130000 - 0x0c170000 (512K)
-/opic@0x0c130000/reg 0x0c130000 0 0x0c130000 0x40000
-/opic@0x0c130000/interrupt-ranges 0 0 0 16
-/opic@0x0c130000/device_type open-pic
+##### OPENPIC @ 0xfc130000 - 0xfc170000 (512K)
+/opic@0xfc130000/reg 0xfc130000 0 0xfc130000 0x40000
+/opic@0xfc130000/interrupt-ranges 0 0 0 16
+/opic@0xfc130000/device_type open-pic
## interupt out -> CPU's interrupt pin
-/opic@0x0c130000 > intr0 int /cpus/cpu@0
+/opic@0xfc130000 > intr0 int /cpus/cpu@0
diff --git a/tester/rtems/testing/bsps/raspberrypi2.ini b/tester/rtems/testing/bsps/raspberrypi2.ini
new file mode 100644
index 0000000..e8043ac
--- /dev/null
+++ b/tester/rtems/testing/bsps/raspberrypi2.ini
@@ -0,0 +1,43 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2010-2017 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# The BeagleBone Black board connected via TFTP. The console is connected to a
+# telnet tty device.
+#
+[raspberrypi2]
+bsp = raspberrypi2
+arch = arm
+jobs = 1
+tester = %{_rtscripts}/tftp.cfg
+test_restarts = 3
+target_reset_regex = BOOTP broadcast 6.*|^ERROR: can.t get kernel image
+target_start_regex = ^U-Boot .*
+requires = bsp_tty_dev, target_on_command, target_off_command, target_reset_command
diff --git a/tester/rtems/testing/bsps/arm920-run.ini b/tester/rtems/testing/bsps/rv64imafdc_medany.ini
index c3652c7..30abd50 100644
--- a/tester/rtems/testing/bsps/arm920-run.ini
+++ b/tester/rtems/testing/bsps/rv64imafdc_medany.ini
@@ -1,6 +1,7 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
+# Copyright 2020 Hesham Almatary
+# Copyright 2018 embedded brains GmbH
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,12 +29,8 @@
# POSSIBILITY OF SUCH DAMAGE.
#
-#
-# The arm920 BSP
-#
-[arm920-run]
-bsp = arm920
-arch = arm
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
-bsp_run_opts = -a -nouartrx
+[rv64imafdc_medany]
+bsp = rv64imafdc_medany
+arch = riscv64
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 64M
diff --git a/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu.ini b/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu.ini
index 6fba113..a7bb952 100644
--- a/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu.ini
+++ b/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu.ini
@@ -35,4 +35,4 @@
bsp = xilinx_zynq_a9_qemu
arch = arm
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} -M xilinx-zynq-a9 -m 256M
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} %{qemu_opts_serial} -M xilinx-zynq-a9 -m 256M
diff --git a/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu_smp.ini b/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu_smp.ini
index f49d381..4366ffc 100644
--- a/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu_smp.ini
+++ b/tester/rtems/testing/bsps/xilinx_zynq_a9_qemu_smp.ini
@@ -36,4 +36,4 @@ bsp = xilinx_zynq_a9_qemu
arch = arm
jobs = half
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} -M xilinx-zynq-a9 -m 256M -smp cpus=2
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} %{qemu_opts_serial} -M xilinx-zynq-a9 -m 256M -smp cpus=2
diff --git a/tester/rtems/testing/bsps/xilinx_zynq_zc706_qemu.ini b/tester/rtems/testing/bsps/xilinx_zynq_zc706_qemu.ini
index 7abc34e..dc635ae 100644
--- a/tester/rtems/testing/bsps/xilinx_zynq_zc706_qemu.ini
+++ b/tester/rtems/testing/bsps/xilinx_zynq_zc706_qemu.ini
@@ -35,4 +35,4 @@
bsp = xilinx_zynq_zc706
arch = arm
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} -M xilinx-zynq-a9 -m 1024M
+bsp_qemu_opts = %{qemu_opts_base} %{qemu_opts_no_net} %{qemu_opts_serial} -M xilinx-zynq-a9 -m 1024M
diff --git a/tester/rtems/testing/bsps/xilinx_zynqmp_ilp32.ini b/tester/rtems/testing/bsps/xilinx_zynqmp_ilp32.ini
new file mode 100644
index 0000000..5ff0e86
--- /dev/null
+++ b/tester/rtems/testing/bsps/xilinx_zynqmp_ilp32.ini
@@ -0,0 +1,38 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2020 Kinsey Moore(kinsey.moore@oarcorp.com)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# The AArch64 Xilinx ZynqMP (Ultrascale+ MPSOC) ILP32 BSP.
+#
+[xilinx_zynqmp_ilp32]
+bsp = xilinx_zynqmp_ilp32
+arch = aarch64
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_opts = %{qemu_opts_base} -serial null -serial mon:stdio -machine xlnx-zcu102 -m 4096
diff --git a/tester/rtems/testing/bsps/arm1136js.ini b/tester/rtems/testing/bsps/xilinx_zynqmp_lp64.ini
index 2f86ca2..8db82b6 100644
--- a/tester/rtems/testing/bsps/arm1136js.ini
+++ b/tester/rtems/testing/bsps/xilinx_zynqmp_lp64.ini
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2015 On-Line Applications Research Corporation (OAR).
+# Copyright 2020 Kinsey Moore(kinsey.moore@oarcorp.com)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -29,13 +29,10 @@
#
#
-# The arm1136js BSP
+# The AArch64 Xilinx ZynqMP (Ultrascale+ MPSOC) LP64 BSP.
#
-[arm1136js]
-bsp = arm1136js
-arch = arm
-arm1136js = %{_rtscripts}/gdb.cfg
-gdb_script = bsp_gdb_script
-bsp_gdb_script = target sim
- load
- run
+[xilinx_zynqmp_lp64]
+bsp = xilinx_zynqmp_lp64
+arch = aarch64
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_opts = %{qemu_opts_base} -serial null -serial mon:stdio -machine xlnx-zcu102 -m 4096
diff --git a/tester/rtems/testing/qemu.cfg b/tester/rtems/testing/qemu.cfg
index b23be4b..efaef8d 100644
--- a/tester/rtems/testing/qemu.cfg
+++ b/tester/rtems/testing/qemu.cfg
@@ -58,10 +58,11 @@
#
# Qemu common option patterns.
#
+%define qemu_opts_base -no-reboot -nographic
%if %{defined qemu_use_serial_console}
- %define qemu_opts_base -no-reboot -monitor none -serial stdio -nographic
+ %define qemu_opts_serial -monitor none -serial stdio
%else
- %define qemu_opts_base -no-reboot -serial null -serial mon:stdio -nographic
+ %define qemu_opts_serial -serial null -serial mon:stdio
%endif
%define qemu_opts_no_net -net none
diff --git a/tester/rtems/testing/testing.mc b/tester/rtems/testing/testing.mc
index 662b352..d03ea6d 100644
--- a/tester/rtems/testing/testing.mc
+++ b/tester/rtems/testing/testing.mc
@@ -51,7 +51,8 @@ _rtbase: none, none, '%{_rtdir}'
_rtscripts: none, none, '%{_rtbase}/rtems/testing'
# Defaults
-timeout: none, none, '180'
+timeout: none, none, '180' # seconds
+max_test_period: none, none, '300' # seconds
# Tests detected as invalid that are valid
invalid_tests: none, none, '''minimum.exe'''
diff --git a/tester/rtems/version.cfg b/tester/rtems/version.cfg
index 2cd9073..950850f 100644
--- a/tester/rtems/version.cfg
+++ b/tester/rtems/version.cfg
@@ -32,4 +32,4 @@
# RTEMS Version
#
-%define rtems_version 5
+%define rtems_version 6
diff --git a/tester/wscript b/tester/wscript
index e923347..674c4fb 100644
--- a/tester/wscript
+++ b/tester/wscript
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2013-2016 Chris Johns (chrisj@rtems.org)
+# Copyright 2013, 2020 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -51,15 +51,14 @@ def build(bld):
# Install the tester code.
#
bld(features = 'py',
- source = ['rt/__init__.py',
+ source = ['__init__.py',
+ 'rt/__init__.py',
'rt/bsps.py',
'rt/check.py',
- 'rt/cmd-bsp-builder.py',
- 'rt/cmd-run.py',
- 'rt/cmd-test.py',
'rt/config.py',
'rt/console.py',
'rt/coverage.py',
+ 'rt/exe.py',
'rt/gdb.py',
'rt/options.py',
'rt/report.py',
@@ -67,7 +66,8 @@ def build(bld):
'rt/stty.py',
'rt/telnet.py',
'rt/test.py',
- 'rt/tftp.py'],
+ 'rt/tftp.py',
+ 'rt/tftpserver.py'],
install_from = '.',
install_path = '${PREFIX}/share/rtems/tester')
bld(features = 'py',
@@ -76,21 +76,11 @@ def build(bld):
'rt/pygdb/spark.py'],
install_from = '.',
install_path = '${PREFIX}/share/rtems/tester')
- bld(features = 'py',
- source = ['rt/tftpy/__init__.py',
- 'rt/tftpy/TftpClient.py',
- 'rt/tftpy/TftpContexts.py',
- 'rt/tftpy/TftpPacketFactory.py',
- 'rt/tftpy/TftpPacketTypes.py',
- 'rt/tftpy/TftpServer.py',
- 'rt/tftpy/TftpShared.py',
- 'rt/tftpy/TftpStates.py'],
- install_from = '.',
- install_path = '${PREFIX}/share/rtems/tester')
bld.install_files('${PREFIX}/bin',
['rtems-run',
'rtems-test',
- 'rtems-bsp-builder'],
+ 'rtems-bsp-builder',
+ 'rtems-tftp-server'],
chmod = 0o755)
#
diff --git a/trace/record/client.h b/trace/record/client.h
new file mode 100644
index 0000000..62ffdb5
--- /dev/null
+++ b/trace/record/client.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2018, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RTEMS_TOOLS_TRACE_RECORD_CLIENT_H_
+#define RTEMS_TOOLS_TRACE_RECORD_CLIENT_H_
+
+#include <rtems/recordclient.h>
+#include <rtems/recorddata.h>
+
+#include <sys/types.h>
+
+#include <cerrno>
+#include <csignal>
+#include <cstring>
+#include <iostream>
+#include <list>
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+class ErrnoException : public std::runtime_error {
+ public:
+ ErrnoException(std::string msg)
+ : std::runtime_error(msg + ": " + std::strerror(errno)) {
+ // Nothing to do
+ }
+};
+
+class FileDescriptor {
+ public:
+ FileDescriptor() = default;
+
+ FileDescriptor(const FileDescriptor&) = delete;
+
+ FileDescriptor& operator=(const FileDescriptor&) = delete;
+
+ void Open(const char* file);
+
+ void Connect(const char* host, uint16_t port);
+
+ ssize_t Read(void* buf, size_t n) { return (*reader_)(fd_, buf, n); }
+
+ void Destroy();
+
+ private:
+ int fd_ = -1;
+ ssize_t (*reader_)(int fd, void* buf, size_t n) = nullptr;
+};
+
+class ConfigFile {
+ public:
+ static const std::string kNoError;
+
+ ConfigFile() = default;
+
+ ConfigFile(const ConfigFile&) = delete;
+
+ ConfigFile& operator=(const ConfigFile&) = delete;
+
+ typedef std::string (*Parser)(void* arg, const char* name, const char* value);
+
+ void AddParser(const char* section, Parser parser, void* arg);
+
+ void Parse(const char* file);
+
+ private:
+ std::map<std::string, std::pair<Parser, void*> > parser_;
+
+ std::string error_;
+
+ static int INIHandler(void* user,
+ const char* section,
+ const char* name,
+ const char* value);
+};
+
+class Filter {
+ public:
+ Filter() = default;
+
+ Filter(const Filter&) = default;
+
+ Filter& operator=(const Filter&) = default;
+
+ virtual ~Filter() = default;
+
+ virtual bool Run(void** buf, size_t* n) = 0;
+};
+
+class Base64Filter : public Filter {
+ public:
+ Base64Filter() = default;
+
+ Base64Filter(const Base64Filter&) = default;
+
+ Base64Filter& operator=(const Base64Filter&) = default;
+
+ virtual ~Base64Filter() = default;
+
+ virtual bool Run(void** buf, size_t* n);
+
+ private:
+ int digits_ = 0;
+ bool seen_end_ = false;
+ int val_[4];
+
+ bool DecodeChar(int c, char **target);
+};
+
+#ifdef HAVE_ZLIB_H
+class ZlibFilter : public Filter {
+ public:
+ ZlibFilter();
+
+ ZlibFilter(const ZlibFilter&) = default;
+
+ ZlibFilter& operator=(const ZlibFilter&) = default;
+
+ virtual ~ZlibFilter();
+
+ virtual bool Run(void** buf, size_t* n);
+
+ private:
+ z_stream stream_;
+ std::vector<Bytef> buffer_;
+};
+#endif
+
+class Client {
+ public:
+ Client() = default;
+
+ Client(const Client&) = delete;
+
+ Client& operator=(const Client&) = delete;
+
+ void Open(const char* file) { input_.Open(file); }
+
+ void Connect(const char* host, uint16_t port) { input_.Connect(host, port); }
+
+ void Run();
+
+ void RequestStop() { stop_ = 1; }
+
+ void AddFilter(Filter* filter) { filters_.push_back(filter); }
+
+ void Destroy();
+
+ void set_limit(uint64_t limit) { limit_ = limit; }
+
+ protected:
+ void Initialize(rtems_record_client_handler handler) {
+ rtems_record_client_init(&base_, handler, this);
+ }
+
+ size_t data_size() const { return base_.data_size; };
+
+ private:
+ rtems_record_client_context base_;
+ std::list<Filter*> filters_;
+ FileDescriptor input_;
+ sig_atomic_t stop_ = 0;
+ uint64_t limit_ = 0;
+
+ void Flush();
+};
+
+#endif // RTEMS_TOOLS_TRACE_RECORD_CLIENT_H_
diff --git a/trace/record/inih/LICENSE.txt b/trace/record/inih/LICENSE.txt
new file mode 100644
index 0000000..cb7ee2d
--- /dev/null
+++ b/trace/record/inih/LICENSE.txt
@@ -0,0 +1,27 @@
+
+The "inih" library is distributed under the New BSD license:
+
+Copyright (c) 2009, Ben Hoyt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Ben Hoyt nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/trace/record/inih/ini.c b/trace/record/inih/ini.c
new file mode 100644
index 0000000..d19d613
--- /dev/null
+++ b/trace/record/inih/ini.c
@@ -0,0 +1,284 @@
+/* inih -- simple .INI file parser
+
+SPDX-License-Identifier: BSD-3-Clause
+
+Copyright (C) 2009-2020, Ben Hoyt
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "ini.h"
+
+#if !INI_USE_STACK
+#include <stdlib.h>
+#endif
+
+#define MAX_SECTION 50
+#define MAX_NAME 50
+
+/* Used by ini_parse_string() to keep track of string parsing state. */
+typedef struct {
+ const char* ptr;
+ size_t num_left;
+} ini_parse_string_ctx;
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+static char* rstrip(char* s)
+{
+ char* p = s + strlen(s);
+ while (p > s && isspace((unsigned char)(*--p)))
+ *p = '\0';
+ return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+static char* lskip(const char* s)
+{
+ while (*s && isspace((unsigned char)(*s)))
+ s++;
+ return (char*)s;
+}
+
+/* Return pointer to first char (of chars) or inline comment in given string,
+ or pointer to null at end of string if neither found. Inline comment must
+ be prefixed by a whitespace character to register as a comment. */
+static char* find_chars_or_comment(const char* s, const char* chars)
+{
+#if INI_ALLOW_INLINE_COMMENTS
+ int was_space = 0;
+ while (*s && (!chars || !strchr(chars, *s)) &&
+ !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
+ was_space = isspace((unsigned char)(*s));
+ s++;
+ }
+#else
+ while (*s && (!chars || !strchr(chars, *s))) {
+ s++;
+ }
+#endif
+ return (char*)s;
+}
+
+/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
+static char* strncpy0(char* dest, const char* src, size_t size)
+{
+ strncpy(dest, src, size - 1);
+ dest[size - 1] = '\0';
+ return dest;
+}
+
+/* See documentation in header file. */
+int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
+ void* user)
+{
+ /* Uses a fair bit of stack (use heap instead if you need to) */
+#if INI_USE_STACK
+ char line[INI_MAX_LINE];
+ int max_line = INI_MAX_LINE;
+#else
+ char* line;
+ size_t max_line = INI_INITIAL_ALLOC;
+#endif
+#if INI_ALLOW_REALLOC && !INI_USE_STACK
+ char* new_line;
+ size_t offset;
+#endif
+ char section[MAX_SECTION] = "";
+ char prev_name[MAX_NAME] = "";
+
+ char* start;
+ char* end;
+ char* name;
+ char* value;
+ int lineno = 0;
+ int error = 0;
+
+#if !INI_USE_STACK
+ line = (char*)malloc(INI_INITIAL_ALLOC);
+ if (!line) {
+ return -2;
+ }
+#endif
+
+#if INI_HANDLER_LINENO
+#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
+#else
+#define HANDLER(u, s, n, v) handler(u, s, n, v)
+#endif
+
+ /* Scan through stream line by line */
+ while (reader(line, (int)max_line, stream) != NULL) {
+#if INI_ALLOW_REALLOC && !INI_USE_STACK
+ offset = strlen(line);
+ while (offset == max_line - 1 && line[offset - 1] != '\n') {
+ max_line *= 2;
+ if (max_line > INI_MAX_LINE)
+ max_line = INI_MAX_LINE;
+ new_line = realloc(line, max_line);
+ if (!new_line) {
+ free(line);
+ return -2;
+ }
+ line = new_line;
+ if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
+ break;
+ if (max_line >= INI_MAX_LINE)
+ break;
+ offset += strlen(line + offset);
+ }
+#endif
+
+ lineno++;
+
+ start = line;
+#if INI_ALLOW_BOM
+ if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
+ (unsigned char)start[1] == 0xBB &&
+ (unsigned char)start[2] == 0xBF) {
+ start += 3;
+ }
+#endif
+ start = lskip(rstrip(start));
+
+ if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
+ /* Start-of-line comment */
+ }
+#if INI_ALLOW_MULTILINE
+ else if (*prev_name && *start && start > line) {
+ /* Non-blank line with leading whitespace, treat as continuation
+ of previous name's value (as per Python configparser). */
+ if (!HANDLER(user, section, prev_name, start) && !error)
+ error = lineno;
+ }
+#endif
+ else if (*start == '[') {
+ /* A "[section]" line */
+ end = find_chars_or_comment(start + 1, "]");
+ if (*end == ']') {
+ *end = '\0';
+ strncpy0(section, start + 1, sizeof(section));
+ *prev_name = '\0';
+#if INI_CALL_HANDLER_ON_NEW_SECTION
+ if (!HANDLER(user, section, NULL, NULL) && !error)
+ error = lineno;
+#endif
+ }
+ else if (!error) {
+ /* No ']' found on section line */
+ error = lineno;
+ }
+ }
+ else if (*start) {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_chars_or_comment(start, "=:");
+ if (*end == '=' || *end == ':') {
+ *end = '\0';
+ name = rstrip(start);
+ value = end + 1;
+#if INI_ALLOW_INLINE_COMMENTS
+ end = find_chars_or_comment(value, NULL);
+ if (*end)
+ *end = '\0';
+#endif
+ value = lskip(value);
+ rstrip(value);
+
+ /* Valid name[=:]value pair found, call handler */
+ strncpy0(prev_name, name, sizeof(prev_name));
+ if (!HANDLER(user, section, name, value) && !error)
+ error = lineno;
+ }
+ else if (!error) {
+ /* No '=' or ':' found on name[=:]value line */
+#if INI_ALLOW_NO_VALUE
+ *end = '\0';
+ name = rstrip(start);
+ if (!HANDLER(user, section, name, NULL) && !error)
+ error = lineno;
+#else
+ error = lineno;
+#endif
+ }
+ }
+
+#if INI_STOP_ON_FIRST_ERROR
+ if (error)
+ break;
+#endif
+ }
+
+#if !INI_USE_STACK
+ free(line);
+#endif
+
+ return error;
+}
+
+/* See documentation in header file. */
+int ini_parse_file(FILE* file, ini_handler handler, void* user)
+{
+ return ini_parse_stream((ini_reader)fgets, file, handler, user);
+}
+
+/* See documentation in header file. */
+int ini_parse(const char* filename, ini_handler handler, void* user)
+{
+ FILE* file;
+ int error;
+
+ file = fopen(filename, "r");
+ if (!file)
+ return -1;
+ error = ini_parse_file(file, handler, user);
+ fclose(file);
+ return error;
+}
+
+/* An ini_reader function to read the next line from a string buffer. This
+ is the fgets() equivalent used by ini_parse_string(). */
+static char* ini_reader_string(char* str, int num, void* stream) {
+ ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
+ const char* ctx_ptr = ctx->ptr;
+ size_t ctx_num_left = ctx->num_left;
+ char* strp = str;
+ char c;
+
+ if (ctx_num_left == 0 || num < 2)
+ return NULL;
+
+ while (num > 1 && ctx_num_left != 0) {
+ c = *ctx_ptr++;
+ ctx_num_left--;
+ *strp++ = c;
+ if (c == '\n')
+ break;
+ num--;
+ }
+
+ *strp = '\0';
+ ctx->ptr = ctx_ptr;
+ ctx->num_left = ctx_num_left;
+ return str;
+}
+
+/* See documentation in header file. */
+int ini_parse_string(const char* string, ini_handler handler, void* user) {
+ ini_parse_string_ctx ctx;
+
+ ctx.ptr = string;
+ ctx.num_left = strlen(string);
+ return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
+ user);
+}
diff --git a/trace/record/inih/ini.h b/trace/record/inih/ini.h
new file mode 100644
index 0000000..441f560
--- /dev/null
+++ b/trace/record/inih/ini.h
@@ -0,0 +1,148 @@
+/* inih -- simple .INI file parser
+
+SPDX-License-Identifier: BSD-3-Clause
+
+Copyright (C) 2009-2020, Ben Hoyt
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#ifndef __INI_H__
+#define __INI_H__
+
+/* Make this header file easier to include in C++ code */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/* Nonzero if ini_handler callback should accept lineno parameter. */
+#ifndef INI_HANDLER_LINENO
+#define INI_HANDLER_LINENO 0
+#endif
+
+/* Typedef for prototype of handler function. */
+#if INI_HANDLER_LINENO
+typedef int (*ini_handler)(void* user, const char* section,
+ const char* name, const char* value,
+ int lineno);
+#else
+typedef int (*ini_handler)(void* user, const char* section,
+ const char* name, const char* value);
+#endif
+
+/* Typedef for prototype of fgets-style reader function. */
+typedef char* (*ini_reader)(char* str, int num, void* stream);
+
+/* Parse given INI-style file. May have [section]s, name=value pairs
+ (whitespace stripped), and comments starting with ';' (semicolon). Section
+ is "" if name=value pair parsed before any section heading. name:value
+ pairs are also supported as a concession to Python's configparser.
+
+ For each name=value pair parsed, call handler function with given user
+ pointer as well as section, name, and value (data only valid for duration
+ of handler call). Handler should return nonzero on success, zero on error.
+
+ Returns 0 on success, line number of first error on parse error (doesn't
+ stop on first error), -1 on file open error, or -2 on memory allocation
+ error (only when INI_USE_STACK is zero).
+*/
+int ini_parse(const char* filename, ini_handler handler, void* user);
+
+/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
+ close the file when it's finished -- the caller must do that. */
+int ini_parse_file(FILE* file, ini_handler handler, void* user);
+
+/* Same as ini_parse(), but takes an ini_reader function pointer instead of
+ filename. Used for implementing custom or string-based I/O (see also
+ ini_parse_string). */
+int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
+ void* user);
+
+/* Same as ini_parse(), but takes a zero-terminated string with the INI data
+instead of a file. Useful for parsing INI data from a network socket or
+already in memory. */
+int ini_parse_string(const char* string, ini_handler handler, void* user);
+
+/* Nonzero to allow multi-line value parsing, in the style of Python's
+ configparser. If allowed, ini_parse() will call the handler with the same
+ name for each subsequent line parsed. */
+#ifndef INI_ALLOW_MULTILINE
+#define INI_ALLOW_MULTILINE 1
+#endif
+
+/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
+ the file. See https://github.com/benhoyt/inih/issues/21 */
+#ifndef INI_ALLOW_BOM
+#define INI_ALLOW_BOM 1
+#endif
+
+/* Chars that begin a start-of-line comment. Per Python configparser, allow
+ both ; and # comments at the start of a line by default. */
+#ifndef INI_START_COMMENT_PREFIXES
+#define INI_START_COMMENT_PREFIXES ";#"
+#endif
+
+/* Nonzero to allow inline comments (with valid inline comment characters
+ specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
+ Python 3.2+ configparser behaviour. */
+#ifndef INI_ALLOW_INLINE_COMMENTS
+#define INI_ALLOW_INLINE_COMMENTS 1
+#endif
+#ifndef INI_INLINE_COMMENT_PREFIXES
+#define INI_INLINE_COMMENT_PREFIXES ";"
+#endif
+
+/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
+#ifndef INI_USE_STACK
+#define INI_USE_STACK 1
+#endif
+
+/* Maximum line length for any line in INI file (stack or heap). Note that
+ this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
+#ifndef INI_MAX_LINE
+#define INI_MAX_LINE 200
+#endif
+
+/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
+ fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
+ zero. */
+#ifndef INI_ALLOW_REALLOC
+#define INI_ALLOW_REALLOC 0
+#endif
+
+/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
+ is zero. */
+#ifndef INI_INITIAL_ALLOC
+#define INI_INITIAL_ALLOC 200
+#endif
+
+/* Stop parsing on first error (default is to keep parsing). */
+#ifndef INI_STOP_ON_FIRST_ERROR
+#define INI_STOP_ON_FIRST_ERROR 0
+#endif
+
+/* Nonzero to call the handler at the start of each new section (with
+ name and value NULL). Default is to only call the handler on
+ each name=value pair. */
+#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
+#define INI_CALL_HANDLER_ON_NEW_SECTION 0
+#endif
+
+/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
+ call the handler with value NULL in this case. Default is to treat
+ no-value lines as an error. */
+#ifndef INI_ALLOW_NO_VALUE
+#define INI_ALLOW_NO_VALUE 0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INI_H__ */
diff --git a/trace/record/record-client-base.cc b/trace/record/record-client-base.cc
new file mode 100644
index 0000000..fa75f53
--- /dev/null
+++ b/trace/record/record-client-base.cc
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2018, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "client.h"
+
+#ifdef _WIN32
+#include <io.h>
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+
+#include <ini.h>
+
+static ssize_t ReadFile(int fd, void* buf, size_t n) {
+ return ::read(fd, buf, n);
+}
+
+static ssize_t ReadSocket(int fd, void* buf, size_t n) {
+ // This cast is necessary for Windows
+ return ::recv(fd, static_cast<char*>(buf), n, 0);
+}
+
+void FileDescriptor::Open(const char* file) {
+ assert(fd_ == -1);
+
+ int oflag = O_RDONLY;
+#ifdef _WIN32
+ oflag |= O_BINARY;
+#endif
+ fd_ = ::open(file, oflag);
+ if (fd_ < 0) {
+ throw ErrnoException(std::string("cannot open file '") + file + "'");
+ }
+
+ reader_ = ReadFile;
+}
+
+void FileDescriptor::Connect(const char* host, uint16_t port) {
+ assert(fd_ == -1);
+
+ fd_ = ::socket(PF_INET, SOCK_STREAM, 0);
+ if (fd_ < 0) {
+ throw ErrnoException("cannot open socket");
+ }
+
+ struct sockaddr_in in_addr;
+ std::memset(&in_addr, 0, sizeof(in_addr));
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons(port);
+ in_addr.sin_addr.s_addr = inet_addr(host);
+
+ int rv = ::connect(fd_, (struct sockaddr*)&in_addr, sizeof(in_addr));
+ if (rv != 0) {
+ throw ErrnoException(std::string("cannot connect to ") + host + " port " +
+ std::to_string(port));
+ }
+
+ reader_ = ReadSocket;
+}
+
+void FileDescriptor::Destroy() {
+ if (fd_ != -1) {
+ int rv = ::close(fd_);
+ if (rv != 0) {
+ std::cerr << "close failed: " << strerror(errno) << std::endl;
+ }
+ }
+}
+
+const std::string ConfigFile::kNoError;
+
+void ConfigFile::AddParser(const char* section, Parser parser, void* arg) {
+ parser_[section] = std::make_pair(parser, arg);
+}
+
+void ConfigFile::Parse(const char* file) {
+ int status = ini_parse(file, INIHandler, this);
+ if (status < 0) {
+ throw ErrnoException(std::string("cannot parse configuration file '") +
+ file + "'");
+ } else if (status > 0) {
+ throw std::runtime_error(
+ std::string("invalid line ") + std::to_string(status) +
+ " in configuration file '" + file + "': " + error_);
+ }
+}
+
+int ConfigFile::INIHandler(void* user,
+ const char* section,
+ const char* name,
+ const char* value) {
+ ConfigFile* self = static_cast<ConfigFile*>(user);
+ auto it = self->parser_.find(section);
+ if (it != self->parser_.end()) {
+ std::string error = (*it->second.first)(it->second.second, name, value);
+ if (error == kNoError) {
+ return 1;
+ }
+
+ self->error_ = error;
+ } else {
+ self->error_ = std::string("unknown section: ") + section;
+ }
+
+ return 0;
+}
+
+void Client::Flush() {
+ while (true) {
+ void* p = nullptr;
+ size_t n = 0;
+ for (auto filter : filters_) {
+ if (!filter->Run(&p, &n)) {
+ break;
+ }
+ }
+
+ if (n > 0) {
+ rtems_record_client_run(&base_, p, n);
+ } else {
+ break;
+ }
+ }
+}
+
+void Client::Run() {
+ uint64_t todo = UINT64_MAX;
+
+ if (limit_ != 0) {
+ todo = limit_;
+ }
+
+ while (stop_ == 0 && todo > 0) {
+ long buf[8192];
+ size_t m = std::min(static_cast<uint64_t>(sizeof(buf)), todo);
+ ssize_t n = input_.Read(buf, m);
+ if (n <= 0) {
+ break;
+ }
+
+ void* p = &buf[0];
+ size_t k = static_cast<size_t>(n);
+ for (auto filter : filters_) {
+ if (!filter->Run(&p, &k)) {
+ std::cerr << "error: input filter failure" << std::endl;
+ return;
+ }
+ }
+
+ rtems_record_client_run(&base_, p, k);
+ todo -= static_cast<size_t>(n);
+ }
+
+ Flush();
+}
+
+void Client::Destroy() {
+ input_.Destroy();
+ rtems_record_client_destroy(&base_);
+}
diff --git a/trace/record/record-client.c b/trace/record/record-client.c
new file mode 100644
index 0000000..e1365fe
--- /dev/null
+++ b/trace/record/record-client.c
@@ -0,0 +1,729 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018, 2019 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD. It may be used for utility programs.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/recordclient.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#define TIME_MASK ( ( UINT32_C( 1 ) << RTEMS_RECORD_TIME_BITS ) - 1 )
+
+static rtems_record_client_status visit(
+ rtems_record_client_context *ctx,
+ uint32_t time_event,
+ uint64_t data
+);
+
+static rtems_record_client_status consume_error(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ (void) buf;
+ (void) n;
+
+ return ctx->status;
+}
+
+static rtems_record_client_status error(
+ rtems_record_client_context *ctx,
+ rtems_record_client_status status
+)
+{
+ ctx->status = status;
+ ctx->consume = consume_error;
+
+ return status;
+}
+
+static void set_to_bt_scaler(
+ rtems_record_client_context *ctx,
+ uint32_t frequency
+)
+{
+ uint64_t bin_per_s;
+
+ bin_per_s = UINT64_C( 1 ) << 32;
+ ctx->to_bt_scaler = ( ( bin_per_s << 31 ) + frequency - 1 ) / frequency;
+}
+
+static rtems_record_client_status call_handler(
+ const rtems_record_client_context *ctx,
+ uint64_t bt,
+ rtems_record_event event,
+ uint64_t data
+)
+{
+ return ( *ctx->handler )(
+ bt,
+ ctx->cpu,
+ event,
+ data,
+ ctx->handler_arg
+ );
+}
+
+static void signal_overflow(
+ rtems_record_client_per_cpu *per_cpu,
+ uint32_t data
+)
+{
+ per_cpu->hold_back = true;
+ per_cpu->item_index = 0;
+ per_cpu->overflow += data;
+}
+
+static void resolve_hold_back(
+ rtems_record_client_context *ctx,
+ rtems_record_client_per_cpu *per_cpu
+)
+{
+ if ( per_cpu->hold_back ) {
+ uint32_t last_tail;
+ uint32_t last_head;
+ uint32_t last_capacity;
+ uint32_t new_head;
+ uint32_t new_content;
+ uint32_t begin_index;
+ uint32_t index;
+ uint32_t first;
+ uint32_t last;
+ uint32_t delta;
+ uint64_t uptime;
+
+ last_head = per_cpu->head[ per_cpu->tail_head_index ];
+ last_tail = per_cpu->tail[ per_cpu->tail_head_index ];
+ new_head = per_cpu->head[ per_cpu->tail_head_index ^ 1 ];
+ last_capacity = ( last_tail - last_head - 1 ) & ( ctx->count - 1 );
+ new_content = new_head - last_head;
+
+ if ( new_content > last_capacity ) {
+ begin_index = new_content - last_capacity;
+ per_cpu->overflow += begin_index;
+ } else {
+ begin_index = 0;
+ }
+
+ if ( begin_index >= per_cpu->item_index ) {
+ per_cpu->item_index = 0;
+ return;
+ }
+
+ per_cpu->hold_back = false;
+
+ first = RTEMS_RECORD_GET_TIME( per_cpu->items[ begin_index ].event );
+ last = first;
+ delta = 0;
+ uptime = 0;
+
+ for ( index = begin_index; index < per_cpu->item_index; ++index ) {
+ const rtems_record_item_64 *item;
+ rtems_record_event event;
+ uint32_t time;
+
+ item = &per_cpu->items[ index ];
+ event = RTEMS_RECORD_GET_EVENT( item->event );
+ time = RTEMS_RECORD_GET_TIME( item->event );
+ delta += ( time - last ) & TIME_MASK;
+ last = time;
+
+ if (
+ event == RTEMS_RECORD_UPTIME_LOW
+ && index + 1 < per_cpu->item_index
+ && RTEMS_RECORD_GET_EVENT( ( item + 1 )->event )
+ == RTEMS_RECORD_UPTIME_HIGH
+ ) {
+ uptime = (uint32_t) item->data;
+ uptime += ( item + 1 )->data << 32;
+ break;
+ }
+ }
+
+ per_cpu->uptime.bt = uptime - ( ( delta * ctx->to_bt_scaler ) >> 31 );
+ per_cpu->uptime.time_at_bt = first;
+ per_cpu->uptime.time_last = first;
+ per_cpu->uptime.time_accumulated = 0;
+
+ if ( per_cpu->overflow > 0 ) {
+ call_handler(
+ ctx,
+ per_cpu->uptime.bt,
+ RTEMS_RECORD_PER_CPU_OVERFLOW,
+ per_cpu->overflow
+ );
+ per_cpu->overflow = 0;
+ }
+
+ for ( index = begin_index; index < per_cpu->item_index; ++index ) {
+ const rtems_record_item_64 *item;
+
+ item = &per_cpu->items[ index ];
+ visit( ctx, item->event, item->data );
+ }
+ }
+}
+
+static void process_per_cpu_head(
+ rtems_record_client_context *ctx,
+ rtems_record_client_per_cpu *per_cpu,
+ uint64_t data
+)
+{
+ uint32_t last_tail;
+ uint32_t last_head;
+ uint32_t last_capacity;
+ uint32_t new_tail;
+ uint32_t new_head;
+ uint32_t new_content;
+ uint32_t content;
+
+ new_tail = per_cpu->tail[ per_cpu->tail_head_index ];
+ new_head = (uint32_t) data;
+ content = new_head - new_tail;
+
+ per_cpu->head[ per_cpu->tail_head_index ] = new_head;
+ per_cpu->tail_head_index ^= 1;
+
+ if ( content >= ctx->count ) {
+ /*
+ * This is a complete ring buffer overflow, the server will detect this
+ * also. It sets the tail to the head plus one and sends us all the
+ * content. This reduces the ring buffer capacity to zero. So, during
+ * transfer, new events will overwrite items in transfer. This is handled
+ * by resolve_hold_back().
+ */
+ per_cpu->tail[ per_cpu->tail_head_index ^ 1 ] = new_head + 1;
+ signal_overflow( per_cpu, content - ctx->count + 1 );
+ return;
+ }
+
+ last_tail = per_cpu->tail[ per_cpu->tail_head_index ];
+ last_head = per_cpu->head[ per_cpu->tail_head_index ];
+
+ if ( last_tail == last_head ) {
+ if ( per_cpu->uptime.bt == 0 ) {
+ /*
+ * This is a special case during initial ramp up. We hold back the items
+ * to deduce the uptime of the first item via resolve_hold_back().
+ */
+ per_cpu->hold_back = true;
+ } else {
+ resolve_hold_back( ctx, per_cpu );
+ }
+
+ return;
+ }
+
+ last_capacity = ( last_tail - last_head - 1 ) & ( ctx->count - 1 );
+ new_content = new_head - last_head;
+
+ if ( new_content <= last_capacity || per_cpu->hold_back ) {
+ resolve_hold_back( ctx, per_cpu );
+ return;
+ }
+
+ signal_overflow( per_cpu, new_content - last_capacity );
+}
+
+static rtems_record_client_status process_per_cpu_count(
+ rtems_record_client_context *ctx,
+ uint64_t data
+)
+{
+ size_t per_cpu_items;
+ rtems_record_item_64 *items;
+ uint32_t cpu;
+
+ if ( ctx->count != 0 ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_DOUBLE_PER_CPU_COUNT );
+ }
+
+ if ( ctx->cpu_count == 0 ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_NO_CPU_MAX );
+ }
+
+ ctx->count = (uint32_t) data;
+
+ /*
+ * The ring buffer capacity plus two items for RTEMS_RECORD_PROCESSOR and
+ * RTEMS_RECORD_PER_CPU_TAIL.
+ */
+ per_cpu_items = ctx->count + 1;
+
+ items = malloc( per_cpu_items * ctx->cpu_count * sizeof( *items ) );
+
+ if ( items == NULL ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_NO_MEMORY );
+ }
+
+ for ( cpu = 0; cpu < ctx->cpu_count; ++cpu ) {
+ ctx->per_cpu[ cpu ].items = items;
+ items += per_cpu_items;
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status hold_back(
+ rtems_record_client_context *ctx,
+ rtems_record_client_per_cpu *per_cpu,
+ uint32_t time_event,
+ rtems_record_event event,
+ uint64_t data
+)
+{
+ if ( event != RTEMS_RECORD_PER_CPU_HEAD ) {
+ uint32_t item_index;
+
+ item_index = per_cpu->item_index;
+
+ if ( item_index <= ctx->count ) {
+ per_cpu->items[ item_index ].event = time_event;
+ per_cpu->items[ item_index ].data = data;
+ per_cpu->item_index = item_index + 1;
+ } else {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_PER_CPU_ITEMS_OVERFLOW );
+ }
+ } else {
+ return call_handler( ctx, 0, RTEMS_RECORD_GET_EVENT( time_event ), data );
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static uint64_t time_bt(
+ const rtems_record_client_context *ctx,
+ rtems_record_client_per_cpu *per_cpu,
+ uint32_t time
+)
+{
+ uint64_t bt;
+
+ if ( time != 0 ) {
+ uint32_t delta;
+
+ delta = ( time - per_cpu->uptime.time_last ) & TIME_MASK;
+ per_cpu->uptime.time_last = time;
+ per_cpu->uptime.time_accumulated += delta;
+ bt = ( per_cpu->uptime.time_accumulated * ctx->to_bt_scaler ) >> 31;
+ bt += per_cpu->uptime.bt;
+ } else {
+ bt = 0;
+ }
+
+ return bt;
+}
+
+static rtems_record_client_status visit(
+ rtems_record_client_context *ctx,
+ uint32_t time_event,
+ uint64_t data
+)
+{
+ rtems_record_client_per_cpu *per_cpu;
+ uint32_t time;
+ rtems_record_event event;
+ rtems_record_client_status status;
+
+ per_cpu = &ctx->per_cpu[ ctx->cpu ];
+ time = RTEMS_RECORD_GET_TIME( time_event );
+ event = RTEMS_RECORD_GET_EVENT( time_event );
+
+ switch ( event ) {
+ case RTEMS_RECORD_PROCESSOR:
+ if ( data >= ctx->cpu_count ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU );
+ }
+
+ ctx->cpu = (uint32_t) data;
+ per_cpu = &ctx->per_cpu[ ctx->cpu ];
+ break;
+ case RTEMS_RECORD_UPTIME_LOW:
+ per_cpu->uptime.bt = (uint32_t) data;
+ per_cpu->uptime.time_at_bt = time;
+ per_cpu->uptime.time_last = time;
+ per_cpu->uptime.time_accumulated = 0;
+ time = 0;
+ break;
+ case RTEMS_RECORD_UPTIME_HIGH:
+ per_cpu->uptime.bt += data << 32;
+ time = 0;
+ break;
+ case RTEMS_RECORD_PER_CPU_TAIL:
+ per_cpu->tail[ per_cpu->tail_head_index ] = (uint32_t) data;
+ break;
+ case RTEMS_RECORD_PER_CPU_HEAD:
+ process_per_cpu_head( ctx, per_cpu, data );
+ break;
+ case RTEMS_RECORD_PROCESSOR_MAXIMUM:
+ if ( data >= RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU_MAX );
+ }
+
+ if ( ctx->cpu_count != 0 ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_DOUBLE_CPU_MAX );
+ }
+
+ ctx->cpu_count = (uint32_t) data + 1;
+ break;
+ case RTEMS_RECORD_PER_CPU_COUNT:
+ status = process_per_cpu_count( ctx, data );
+
+ if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+ return status;
+ }
+
+ break;
+ case RTEMS_RECORD_FREQUENCY:
+ set_to_bt_scaler( ctx, (uint32_t) data );
+ break;
+ case RTEMS_RECORD_VERSION:
+ if ( data != RTEMS_RECORD_THE_VERSION ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_VERSION );
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ if ( per_cpu->hold_back ) {
+ return hold_back( ctx, per_cpu, time_event, event, data );
+ }
+
+ return call_handler( ctx, time_bt( ctx, per_cpu, time ), event, data );
+}
+
+static rtems_record_client_status consume_32(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ while ( n > 0 ) {
+ size_t m;
+ char *pos;
+
+ m = ctx->todo < n ? ctx->todo : n;
+ pos = ctx->pos;
+ pos = memcpy( pos, buf, m );
+ n -= m;
+ buf = (char *) buf + m;
+
+ if ( m == ctx->todo ) {
+ rtems_record_client_status status;
+
+ ctx->todo = sizeof( ctx->item.format_32 );
+ ctx->pos = &ctx->item.format_32;
+
+ status = visit(
+ ctx,
+ ctx->item.format_32.event,
+ ctx->item.format_32.data
+ );
+
+ if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+ return status;
+ }
+ } else {
+ ctx->todo -= m;
+ ctx->pos = pos + m;
+ }
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_64(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ while ( n > 0 ) {
+ size_t m;
+ char *pos;
+
+ m = ctx->todo < n ? ctx->todo : n;
+ pos = ctx->pos;
+ pos = memcpy( pos, buf, m );
+ n -= m;
+ buf = (char *) buf + m;
+
+ if ( m == ctx->todo ) {
+ rtems_record_client_status status;
+
+ ctx->todo = sizeof( ctx->item.format_64 );
+ ctx->pos = &ctx->item.format_64;
+
+ status = visit(
+ ctx,
+ ctx->item.format_64.event,
+ ctx->item.format_64.data
+ );
+
+ if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+ return status;
+ }
+ } else {
+ ctx->todo -= m;
+ ctx->pos = pos + m;
+ }
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_swap_32(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ while ( n > 0 ) {
+ size_t m;
+ char *pos;
+
+ m = ctx->todo < n ? ctx->todo : n;
+ pos = ctx->pos;
+ pos = memcpy( pos, buf, m );
+ n -= m;
+ buf = (char *) buf + m;
+
+ if ( m == ctx->todo ) {
+ rtems_record_client_status status;
+
+ ctx->todo = sizeof( ctx->item.format_32 );
+ ctx->pos = &ctx->item.format_32;
+
+ status = visit(
+ ctx,
+ __builtin_bswap32( ctx->item.format_32.event ),
+ __builtin_bswap32( ctx->item.format_32.data )
+ );
+
+ if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+ return status;
+ }
+ } else {
+ ctx->todo -= m;
+ ctx->pos = pos + m;
+ }
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_swap_64(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ while ( n > 0 ) {
+ size_t m;
+ char *pos;
+
+ m = ctx->todo < n ? ctx->todo : n;
+ pos = ctx->pos;
+ pos = memcpy( pos, buf, m );
+ n -= m;
+ buf = (char *) buf + m;
+
+ if ( m == ctx->todo ) {
+ rtems_record_client_status status;
+
+ ctx->todo = sizeof( ctx->item.format_64 );
+ ctx->pos = &ctx->item.format_64;
+
+ status = visit(
+ ctx,
+ __builtin_bswap32( ctx->item.format_64.event ),
+ __builtin_bswap64( ctx->item.format_64.data )
+ );
+
+ if ( status != RTEMS_RECORD_CLIENT_SUCCESS ) {
+ return status;
+ }
+ } else {
+ ctx->todo -= m;
+ ctx->pos = pos + m;
+ }
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+static rtems_record_client_status consume_init(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ while ( n > 0 ) {
+ size_t m;
+ char *pos;
+
+ m = ctx->todo < n ? ctx->todo : n;
+ pos = ctx->pos;
+ pos = memcpy( pos, buf, m );
+ n -= m;
+ buf = (char *) buf + m;
+
+ if ( m == ctx->todo ) {
+ uint32_t magic;
+
+ magic = ctx->header[ 1 ];
+
+ switch ( ctx->header[ 0 ] ) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ case RTEMS_RECORD_FORMAT_LE_32:
+ ctx->todo = sizeof( ctx->item.format_32 );
+ ctx->pos = &ctx->item.format_32;
+ ctx->consume = consume_32;
+ ctx->data_size = 4;
+ break;
+ case RTEMS_RECORD_FORMAT_LE_64:
+ ctx->todo = sizeof( ctx->item.format_64 );
+ ctx->pos = &ctx->item.format_64;
+ ctx->consume = consume_64;
+ ctx->data_size = 8;
+ break;
+ case RTEMS_RECORD_FORMAT_BE_32:
+ ctx->todo = sizeof( ctx->item.format_32 );
+ ctx->pos = &ctx->item.format_32;
+ ctx->consume = consume_swap_32;
+ ctx->data_size = 4;
+ magic = __builtin_bswap32( magic );
+ break;
+ case RTEMS_RECORD_FORMAT_BE_64:
+ ctx->todo = sizeof( ctx->item.format_64 );
+ ctx->pos = &ctx->item.format_64;
+ ctx->consume = consume_swap_64;
+ ctx->data_size = 8;
+ magic = __builtin_bswap32( magic );
+ break;
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ case RTEMS_RECORD_FORMAT_LE_32:
+ ctx->todo = sizeof( ctx->item.format_32 );
+ ctx->pos = &ctx->item.format_32;
+ ctx->consume = consume_swap_32;
+ ctx->data_size = 4;
+ magic = __builtin_bswap32( magic );
+ break;
+ case RTEMS_RECORD_FORMAT_LE_64:
+ ctx->todo = sizeof( ctx->item.format_64 );
+ ctx->pos = &ctx->item.format_64;
+ ctx->consume = consume_swap_64;
+ ctx->data_size = 8;
+ magic = __builtin_bswap32( magic );
+ break;
+ case RTEMS_RECORD_FORMAT_BE_32:
+ ctx->todo = sizeof( ctx->item.format_32 );
+ ctx->pos = &ctx->item.format_32;
+ ctx->consume = consume_32;
+ ctx->data_size = 4;
+ break;
+ case RTEMS_RECORD_FORMAT_BE_64:
+ ctx->todo = sizeof( ctx->item.format_64 );
+ ctx->pos = &ctx->item.format_64;
+ ctx->consume = consume_64;
+ ctx->data_size = 8;
+ break;
+#else
+#error "unexpected __BYTE_ORDER__"
+#endif
+ default:
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_UNKNOWN_FORMAT );
+ }
+
+ if ( magic != RTEMS_RECORD_MAGIC ) {
+ return error( ctx, RTEMS_RECORD_CLIENT_ERROR_INVALID_MAGIC );
+ }
+
+ return rtems_record_client_run( ctx, buf, n );
+ } else {
+ ctx->todo -= m;
+ ctx->pos = pos + m;
+ }
+ }
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+void rtems_record_client_init(
+ rtems_record_client_context *ctx,
+ rtems_record_client_handler handler,
+ void *arg
+)
+{
+ ctx = memset( ctx, 0, sizeof( *ctx ) );
+ ctx->to_bt_scaler = UINT64_C( 1 ) << 31;
+ ctx->handler = handler;
+ ctx->handler_arg = arg;
+ ctx->todo = sizeof( ctx->header );
+ ctx->pos = &ctx->header;
+ ctx->consume = consume_init;
+}
+
+rtems_record_client_status rtems_record_client_run(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+)
+{
+ return ( *ctx->consume )( ctx, buf, n );
+}
+
+void rtems_record_client_destroy(
+ rtems_record_client_context *ctx
+)
+{
+ uint32_t cpu;
+
+ for ( cpu = 0; cpu < ctx->cpu_count; ++cpu ) {
+ rtems_record_client_per_cpu *per_cpu;
+
+ ctx->cpu = cpu;
+ per_cpu = &ctx->per_cpu[ cpu ];
+ per_cpu->head[ per_cpu->tail_head_index ^ 1 ] =
+ per_cpu->head[ per_cpu->tail_head_index ];
+ resolve_hold_back( ctx, per_cpu );
+ }
+
+ free( ctx->per_cpu[ 0 ].items );
+}
diff --git a/trace/record/record-filter-base64.cc b/trace/record/record-filter-base64.cc
new file mode 100644
index 0000000..12f4bab
--- /dev/null
+++ b/trace/record/record-filter-base64.cc
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: ISC */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc.
+ * ("ISC") Copyright (C) 1998-2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "client.h"
+
+#include <cstring>
+
+static const char base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+bool Base64Filter::DecodeChar(int c, char **target) {
+ char* s;
+
+ if (seen_end_)
+ return false;
+ if ((s = std::strchr(const_cast<char*>(base64), c)) == NULL)
+ return false;
+ val_[digits_++] = s - base64;
+ if (digits_ == 4) {
+ int n;
+ unsigned char buf[3];
+ if (val_[0] == 64 || val_[1] == 64)
+ return false;
+ if (val_[2] == 64 && val_[3] != 64)
+ return false;
+ /*
+ * Check that bits that should be zero are.
+ */
+ if (val_[2] == 64 && (val_[1] & 0xf) != 0)
+ return false;
+ /*
+ * We don't need to test for val_[2] != 64 as
+ * the bottom two bits of 64 are zero.
+ */
+ if (val_[3] == 64 && (val_[2] & 0x3) != 0)
+ return false;
+ n = (val_[2] == 64) ? 1 : (val_[3] == 64) ? 2 : 3;
+ if (n != 3) {
+ seen_end_ = true;
+ if (val_[2] == 64)
+ val_[2] = 0;
+ if (val_[3] == 64)
+ val_[3] = 0;
+ }
+ buf[0] = (val_[0] << 2) | (val_[1] >> 4);
+ buf[1] = (val_[1] << 4) | (val_[2] >> 2);
+ buf[2] = (val_[2] << 6) | (val_[3]);
+ char *out = *target;
+ for (int i = 0; i < n; ++i) {
+ out[i] = buf[i];
+ }
+ *target = out + n;
+ digits_ = 0;
+ }
+ return true;
+}
+
+bool Base64Filter::Run(void** buf, size_t* n) {
+ char* begin = static_cast<char*>(*buf);
+ const char* in = begin;
+ const char* end = in + *n;
+
+ if (begin == end) {
+ return digits_ == 0;
+ }
+
+ char* target = begin;
+ while (in != end) {
+ int c = *in;
+ ++in;
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+ continue;
+ }
+
+ if (!DecodeChar(c, &target)) {
+ return false;
+ };
+ }
+
+ *n = static_cast<size_t>(target - begin);
+ return true;
+}
diff --git a/trace/record/record-filter-zlib.cc b/trace/record/record-filter-zlib.cc
new file mode 100644
index 0000000..87b2cfe
--- /dev/null
+++ b/trace/record/record-filter-zlib.cc
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ZLIB_H
+
+#include "client.h"
+
+ZlibFilter::ZlibFilter() : buffer_(65536)
+{
+ inflateInit(&stream_);
+}
+
+ZlibFilter::~ZlibFilter()
+{
+ inflateEnd(&stream_);
+}
+
+bool ZlibFilter::Run(void** buf, size_t* n) {
+ stream_.next_in = static_cast<unsigned char*>(*buf);
+ stream_.avail_in = *n;
+ stream_.next_out = &buffer_[0];
+ size_t avail_out = 0;
+ size_t buffer_size = buffer_.size();
+ size_t chunk_size = buffer_size;
+ stream_.avail_out = buffer_size;
+
+ while (true) {
+ int err = inflate(&stream_, Z_NO_FLUSH);
+ if (err != Z_OK && err != Z_STREAM_END) {
+ return false;
+ }
+
+ if (stream_.avail_in > 0) {
+ chunk_size = buffer_size;
+ buffer_size *= 2;
+ buffer_.resize(buffer_size);
+ stream_.next_out = reinterpret_cast<Bytef*>(&buffer_[chunk_size]);
+ stream_.avail_out = chunk_size;
+ avail_out += chunk_size;
+ continue;
+ }
+
+ *buf = &buffer_[0];
+ *n = avail_out + chunk_size - stream_.avail_out;
+ return true;
+ }
+}
+
+#endif // HAVE_ZLIB_H
diff --git a/trace/record/record-main-lttng.cc b/trace/record/record-main-lttng.cc
new file mode 100644
index 0000000..7cfa48c
--- /dev/null
+++ b/trace/record/record-main-lttng.cc
@@ -0,0 +1,899 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/*
+ * Copyright (c) 2019 Ravindra Kumar Meena <rmeena840@gmail.com>
+ * Copyright (C) 2018, 2020 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "client.h"
+
+#include <getopt.h>
+
+#include <cassert>
+#include <cerrno>
+#include <cinttypes>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
+#include <llvm/DebugInfo/Symbolize/Symbolize.h>
+#include <llvm/Support/Path.h>
+#endif
+
+#define CTF_MAGIC 0xC1FC1FC1
+#define TASK_RUNNING 0x0000
+#define TASK_IDLE 0x0402
+#define UUID_SIZE 16
+#define THREAD_NAME_SIZE 16
+#define THREAD_API_COUNT 3
+#define THREAD_ID_COUNT 0x10000
+#define BITS_PER_CHAR 8
+#define COMPACT_HEADER_ID 31
+
+static const uint8_t kEmptyThreadName[THREAD_API_COUNT] = "";
+
+static const uint8_t kUUID[] = {0x6a, 0x77, 0x15, 0xd0, 0xb5, 0x02, 0x4c, 0x65,
+ 0x86, 0x78, 0x67, 0x77, 0xac, 0x7f, 0x75, 0x5a};
+
+struct ClientItem {
+ uint64_t ns;
+ uint32_t cpu;
+ rtems_record_event event;
+ uint64_t data;
+};
+
+struct PacketHeader {
+ uint32_t ctf_magic;
+ uint8_t uuid[UUID_SIZE];
+ uint32_t stream_id;
+ uint64_t stream_instance_id;
+} __attribute__((__packed__));
+
+struct PacketContext {
+ PacketHeader header;
+ uint64_t timestamp_begin;
+ uint64_t timestamp_end;
+ uint64_t content_size;
+ uint64_t packet_size;
+ uint64_t packet_seq_num;
+ uint64_t events_discarded;
+ uint32_t cpu_id;
+} __attribute__((__packed__));
+
+static const size_t kPacketContextBits = sizeof(PacketContext) * BITS_PER_CHAR;
+
+struct EventHeaderCompact {
+ uint8_t id;
+ uint32_t event_id;
+ uint64_t ns;
+} __attribute__((__packed__));
+
+struct EventRecordItem {
+ EventHeaderCompact header;
+ uint64_t data;
+} __attribute__((__packed__));
+
+static const size_t kEventRecordItemBits =
+ sizeof(EventRecordItem) * BITS_PER_CHAR;
+
+struct EventSchedSwitch {
+ EventHeaderCompact header;
+ uint8_t prev_comm[THREAD_NAME_SIZE];
+ int32_t prev_tid;
+ int32_t prev_prio;
+ int64_t prev_state;
+ uint8_t next_comm[THREAD_NAME_SIZE];
+ int32_t next_tid;
+ int32_t next_prio;
+} __attribute__((__packed__));
+
+static const size_t kEventSchedSwitchBits =
+ sizeof(EventSchedSwitch) * BITS_PER_CHAR;
+
+struct EventIRQHandlerEntry {
+ EventHeaderCompact header;
+ int32_t irq;
+ uint8_t name[1];
+} __attribute__((__packed__));
+
+static const size_t kEventIRQHandlerEntryBits =
+ sizeof(EventIRQHandlerEntry) * BITS_PER_CHAR;
+
+struct EventIRQHandlerExit {
+ EventHeaderCompact header;
+ int32_t irq;
+ int32_t ret;
+} __attribute__((__packed__));
+
+static const size_t kEventIRQHandlerExitBits =
+ sizeof(EventIRQHandlerExit) * BITS_PER_CHAR;
+
+struct PerCPUContext {
+ FILE* event_stream;
+ uint64_t timestamp_begin;
+ uint64_t timestamp_end;
+ uint64_t size_in_bits;
+ uint32_t thread_id;
+ uint64_t thread_ns;
+ size_t thread_name_index;
+ EventRecordItem record_item;
+ EventSchedSwitch sched_switch;
+ EventIRQHandlerEntry irq_handler_entry;
+ EventIRQHandlerExit irq_handler_exit;
+};
+
+class LTTNGClient : public Client {
+ public:
+ LTTNGClient();
+
+ void ParseConfigFile(const char* config_file);
+
+ void GenerateMetadata();
+
+ void OpenExecutable(const char* elf_file);
+
+ void Destroy() {
+ Client::Destroy();
+ CloseStreamFiles();
+ }
+
+ private:
+ PerCPUContext per_cpu_[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT];
+
+ /*
+ * @brief Thread names indexed by API and object index.
+ *
+ * The API indices are 0 for Internal API, 1 for Classic API and 2 for
+ * POSIX API.
+ */
+ uint8_t thread_names_[THREAD_API_COUNT][THREAD_ID_COUNT][THREAD_NAME_SIZE];
+
+ PacketContext pkt_ctx_;
+
+ size_t cpu_count_ = 0;
+
+#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
+ llvm::symbolize::LLVMSymbolizer symbolizer_;
+#endif
+
+ std::string elf_file_;
+
+ bool resolve_address_ = false;
+
+ typedef std::map<uint64_t, std::vector<char>> AddressToLineMap;
+
+ AddressToLineMap address_to_line_;
+
+ std::vector<std::string> event_to_name_;
+
+ static rtems_record_client_status HandlerCaller(uint64_t bt,
+ uint32_t cpu,
+ rtems_record_event event,
+ uint64_t data,
+ void* arg) {
+ LTTNGClient& self = *static_cast<LTTNGClient*>(arg);
+ return self.Handler(bt, cpu, event, data);
+ }
+
+ rtems_record_client_status Handler(uint64_t bt,
+ uint32_t cpu,
+ rtems_record_event event,
+ uint64_t data);
+
+ void CopyThreadName(const ClientItem& item,
+ size_t api_index,
+ uint8_t* dst) const;
+
+ void WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item);
+
+ void WriteSchedSwitch(PerCPUContext* pcpu, const ClientItem& item);
+
+ void WriteIRQHandlerEntry(PerCPUContext* pcpu, const ClientItem& item);
+
+ void WriteIRQHandlerExit(PerCPUContext* pcpu, const ClientItem& item);
+
+ void AddThreadName(PerCPUContext* pcpu, const ClientItem& item);
+
+ void PrintItem(const ClientItem& item);
+
+ static std::string EventNameParser(void* arg,
+ const char* name,
+ const char* value);
+
+ void OpenStreamFiles(uint64_t data);
+
+ void CloseStreamFiles();
+
+ AddressToLineMap::iterator AddAddressAsHexNumber(const ClientItem& item);
+
+ AddressToLineMap::iterator ResolveAddress(const ClientItem& item);
+};
+
+LTTNGClient::LTTNGClient() : event_to_name_(RTEMS_RECORD_LAST + 1) {
+ Initialize(LTTNGClient::HandlerCaller);
+
+ std::memset(&pkt_ctx_, 0, sizeof(pkt_ctx_));
+ std::memcpy(pkt_ctx_.header.uuid, kUUID, sizeof(pkt_ctx_.header.uuid));
+ pkt_ctx_.header.ctf_magic = CTF_MAGIC;
+
+ for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) {
+ PerCPUContext& pcpu = per_cpu_[i];
+ pcpu.sched_switch.header.id = COMPACT_HEADER_ID;
+ pcpu.sched_switch.header.event_id = 1024;
+ pcpu.irq_handler_entry.header.id = COMPACT_HEADER_ID;
+ pcpu.irq_handler_entry.header.event_id = 1025;
+ pcpu.irq_handler_entry.name[0] = '\0';
+ pcpu.irq_handler_exit.header.id = COMPACT_HEADER_ID;
+ pcpu.irq_handler_exit.header.event_id = 1026;
+ pcpu.irq_handler_exit.ret = 1;
+ pcpu.record_item.header.id = COMPACT_HEADER_ID;
+ }
+
+ for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
+ event_to_name_[i] =
+ rtems_record_event_text(static_cast<rtems_record_event>(i));
+ }
+}
+
+static uint32_t GetAPIIndexOfID(uint32_t id) {
+ return ((id >> 24) & 0x7) - 1;
+}
+
+static uint32_t GetObjIndexOfID(uint32_t id) {
+ return id & (THREAD_ID_COUNT - 1);
+}
+
+static bool IsIdleTaskByAPIIndex(uint32_t api_index) {
+ return api_index == 0;
+}
+
+static const uint8_t kCPUPostfix[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT][2] = {
+ {'0', '\0'}, {'1', '\0'}, {'2', '\0'}, {'3', '\0'}, {'4', '\0'},
+ {'5', '\0'}, {'6', '\0'}, {'7', '\0'}, {'8', '\0'}, {'9', '\0'},
+ {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
+ {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
+ {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
+ {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
+ {'3', '0'}, {'3', '1'}};
+
+void LTTNGClient::OpenExecutable(const char* elf_file) {
+ elf_file_ = elf_file;
+ resolve_address_ = true;
+}
+
+void LTTNGClient::CopyThreadName(const ClientItem& item,
+ size_t api_index,
+ uint8_t* dst) const {
+ const uint8_t* name;
+
+ if (api_index < THREAD_API_COUNT) {
+ name = thread_names_[api_index][GetObjIndexOfID(item.data)];
+ } else {
+ name = kEmptyThreadName;
+ }
+
+ std::memcpy(dst, name, THREAD_NAME_SIZE);
+
+ if (IsIdleTaskByAPIIndex(api_index)) {
+ /*
+ * In Linux, the idle threads are bound to a specific CPU (swapper/n). In
+ * RTEMS, the idle threads can move around, so mimic this Linux behaviour.
+ */
+ dst[4] = '/';
+ dst[5] = kCPUPostfix[item.cpu][0];
+ dst[6] = kCPUPostfix[item.cpu][1];
+ }
+}
+
+static bool IsCodeEvent(rtems_record_event event) {
+ switch (event) {
+ default:
+ return false;
+ case RTEMS_RECORD_CALLER:
+ case RTEMS_RECORD_FUNCTION_ENTRY:
+ case RTEMS_RECORD_FUNCTION_EXIT:
+ case RTEMS_RECORD_ISR_DISABLE:
+ case RTEMS_RECORD_ISR_ENABLE:
+ case RTEMS_RECORD_LINE:
+ case RTEMS_RECORD_THREAD_DISPATCH_DISABLE:
+ case RTEMS_RECORD_THREAD_DISPATCH_ENABLE:
+ return true;
+ }
+}
+
+LTTNGClient::AddressToLineMap::iterator LTTNGClient::AddAddressAsHexNumber(
+ const ClientItem& item) {
+ char hex[19];
+ int n = std::snprintf(hex, sizeof(hex), "0x%" PRIx64, item.data);
+ assert(static_cast<size_t>(n) < sizeof(hex));
+ std::vector<char> code(hex, hex + n + 1);
+ return address_to_line_.emplace(item.data, std::move(code)).first;
+}
+
+LTTNGClient::AddressToLineMap::iterator LTTNGClient::ResolveAddress(
+ const ClientItem& item) {
+#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
+ if (resolve_address_) {
+ auto res_or_err = symbolizer_.symbolizeCode(
+ elf_file_,
+#if LLVM_VERSION_MAJOR >= 9
+ {item.data, llvm::object::SectionedAddress::UndefSection});
+#else
+ item.data);
+#endif
+
+ if (res_or_err) {
+ auto info = res_or_err.get();
+ std::string fn = info.FunctionName;
+ std::string str;
+
+ if (fn != "<invalid>") {
+ str += fn;
+ str += " at ";
+ }
+
+ str += llvm::sys::path::filename(info.FileName);
+ str += ":";
+ str += std::to_string(info.Line);
+ std::vector<char> code(str.begin(), str.end());
+ code.push_back('\0');
+ return address_to_line_.emplace(item.data, std::move(code)).first;
+ }
+ }
+#endif
+
+ return AddAddressAsHexNumber(item);
+}
+
+void LTTNGClient::WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item) {
+ if (IsCodeEvent(item.event)) {
+ EventHeaderCompact header;
+ header.id = COMPACT_HEADER_ID;
+ header.event_id = item.event;
+ header.ns = item.ns;
+
+ auto it = address_to_line_.find(item.data);
+ if (it == address_to_line_.end()) {
+ it = ResolveAddress(item);
+ }
+
+ pcpu->size_in_bits += (sizeof(header) + it->second.size()) * BITS_PER_CHAR;
+
+ std::fwrite(&header, sizeof(header), 1, pcpu->event_stream);
+ std::fwrite(&(*it->second.begin()), it->second.size(), 1,
+ pcpu->event_stream);
+ } else {
+ pcpu->size_in_bits += kEventRecordItemBits;
+
+ EventRecordItem& ri = pcpu->record_item;
+ ri.header.ns = item.ns;
+ ri.header.event_id = item.event;
+ ri.data = item.data;
+
+ std::fwrite(&ri, sizeof(ri), 1, pcpu->event_stream);
+ }
+}
+
+void LTTNGClient::WriteSchedSwitch(PerCPUContext* pcpu,
+ const ClientItem& item) {
+ pcpu->size_in_bits += kEventSchedSwitchBits;
+
+ EventSchedSwitch& ss = pcpu->sched_switch;
+ ss.header.ns = item.ns;
+
+ uint32_t api_index = GetAPIIndexOfID(item.data);
+ ss.next_tid = IsIdleTaskByAPIIndex(api_index) ? 0 : item.data;
+
+ CopyThreadName(item, api_index, ss.next_comm);
+ std::fwrite(&ss, sizeof(ss), 1, pcpu->event_stream);
+}
+
+void LTTNGClient::WriteIRQHandlerEntry(PerCPUContext* pcpu,
+ const ClientItem& item) {
+ pcpu->size_in_bits += kEventIRQHandlerEntryBits;
+
+ EventIRQHandlerEntry& ih = pcpu->irq_handler_entry;
+ ih.header.ns = item.ns;
+ ih.irq = static_cast<int32_t>(item.data);
+ std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
+}
+
+void LTTNGClient::WriteIRQHandlerExit(PerCPUContext* pcpu,
+ const ClientItem& item) {
+ pcpu->size_in_bits += kEventIRQHandlerExitBits;
+
+ EventIRQHandlerExit& ih = pcpu->irq_handler_exit;
+ ih.header.ns = item.ns;
+ ih.irq = static_cast<int32_t>(item.data);
+ std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
+}
+
+void LTTNGClient::AddThreadName(PerCPUContext* pcpu, const ClientItem& item) {
+ if (pcpu->thread_name_index >= THREAD_NAME_SIZE) {
+ return;
+ }
+
+ uint32_t api_index = GetAPIIndexOfID(pcpu->thread_id);
+ if (api_index >= THREAD_API_COUNT) {
+ return;
+ }
+
+ uint32_t obj_index = GetObjIndexOfID(pcpu->thread_id);
+ uint64_t name = item.data;
+ size_t i;
+ for (i = pcpu->thread_name_index; i < pcpu->thread_name_index + data_size();
+ ++i) {
+ thread_names_[api_index][obj_index][i] = static_cast<uint8_t>(name);
+ name >>= BITS_PER_CHAR;
+ }
+
+ pcpu->thread_name_index = i;
+}
+
+void LTTNGClient::PrintItem(const ClientItem& item) {
+ PerCPUContext& pcpu = per_cpu_[item.cpu];
+ if (pcpu.timestamp_begin == 0) {
+ pcpu.timestamp_begin = item.ns;
+ }
+
+ pcpu.timestamp_end = item.ns;
+
+ EventSchedSwitch& ss = pcpu.sched_switch;
+ switch (item.event) {
+ case RTEMS_RECORD_THREAD_SWITCH_OUT: {
+ uint32_t api_index = GetAPIIndexOfID(item.data);
+ ss.header.ns = item.ns;
+
+ if (IsIdleTaskByAPIIndex(api_index)) {
+ ss.prev_tid = 0;
+ ss.prev_state = TASK_IDLE;
+ } else {
+ ss.prev_tid = item.data;
+ ss.prev_state = TASK_RUNNING;
+ }
+
+ CopyThreadName(item, api_index, ss.prev_comm);
+ break;
+ }
+ case RTEMS_RECORD_THREAD_SWITCH_IN:
+ if (item.ns == ss.header.ns) {
+ WriteSchedSwitch(&pcpu, item);
+ }
+ break;
+ case RTEMS_RECORD_THREAD_ID:
+ pcpu.thread_id = item.data;
+ pcpu.thread_ns = item.ns;
+ pcpu.thread_name_index = 0;
+ break;
+ case RTEMS_RECORD_INTERRUPT_ENTRY:
+ WriteIRQHandlerEntry(&pcpu, item);
+ break;
+ case RTEMS_RECORD_INTERRUPT_EXIT:
+ WriteIRQHandlerExit(&pcpu, item);
+ break;
+ case RTEMS_RECORD_THREAD_NAME:
+ AddThreadName(&pcpu, item);
+ break;
+ case RTEMS_RECORD_PROCESSOR_MAXIMUM:
+ OpenStreamFiles(item.data);
+ break;
+ default:
+ if (item.ns != 0) {
+ WriteRecordItem(&pcpu, item);
+ }
+ break;
+ }
+}
+
+rtems_record_client_status LTTNGClient::Handler(uint64_t bt,
+ uint32_t cpu,
+ rtems_record_event event,
+ uint64_t data) {
+ ClientItem item;
+
+ item.ns = rtems_record_client_bintime_to_nanoseconds(bt);
+ item.cpu = cpu;
+ item.event = event;
+ item.data = data;
+
+ PrintItem(item);
+
+ return RTEMS_RECORD_CLIENT_SUCCESS;
+}
+
+std::string LTTNGClient::EventNameParser(void* arg,
+ const char* name,
+ const char* value) {
+ LTTNGClient* self = static_cast<LTTNGClient*>(arg);
+ errno = 0;
+ char* end;
+ long event = std::strtol(name, &end, 0);
+ if (errno == 0 && *end == '\0' && event >= 0 && event <= RTEMS_RECORD_LAST) {
+ self->event_to_name_[event] = value;
+ return ConfigFile::kNoError;
+ }
+
+ return std::string("invalid event: ") + name;
+}
+
+void LTTNGClient::ParseConfigFile(const char* config_file) {
+ if (config_file != nullptr) {
+ ConfigFile file;
+ file.AddParser("EventNames", EventNameParser, this);
+ file.Parse(config_file);
+ }
+}
+
+void LTTNGClient::OpenStreamFiles(uint64_t data) {
+ // Assertions are ensured by C record client
+ assert(cpu_count_ == 0 && data < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT);
+ cpu_count_ = static_cast<size_t>(data) + 1;
+
+ for (size_t i = 0; i < cpu_count_; ++i) {
+ std::string filename("stream_");
+ filename += std::to_string(i);
+ FILE* f = std::fopen(filename.c_str(), "wb");
+ if (f == NULL) {
+ throw ErrnoException("cannot create file '" + filename + "'");
+ }
+ per_cpu_[i].event_stream = f;
+ std::fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, f);
+ }
+}
+
+void LTTNGClient::CloseStreamFiles() {
+ for (size_t i = 0; i < cpu_count_; ++i) {
+ PerCPUContext* pcpu = &per_cpu_[i];
+ std::fseek(pcpu->event_stream, 0, SEEK_SET);
+
+ pkt_ctx_.header.stream_instance_id = i;
+ pkt_ctx_.timestamp_begin = pcpu->timestamp_begin;
+ pkt_ctx_.timestamp_end = pcpu->timestamp_end;
+ pkt_ctx_.content_size = pcpu->size_in_bits + kPacketContextBits;
+ pkt_ctx_.packet_size = pkt_ctx_.content_size;
+ pkt_ctx_.cpu_id = i;
+
+ std::fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, pcpu->event_stream);
+ std::fclose(pcpu->event_stream);
+ }
+}
+
+static const char kMetadata[] =
+ "/* CTF 1.8 */\n"
+ "\n"
+ "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
+ "typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n"
+ "typealias integer { size = 32; align = 8; signed = true; } := int32_t;\n"
+ "typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n"
+ "typealias integer { size = 64; align = 8; signed = true; } := int64_t;\n"
+ "typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n"
+ "\n"
+ "typealias integer {\n"
+ "\tsize = 64; align = 8; signed = false; base = 16;\n"
+ "} := xint64_t;\n"
+ "\n"
+ "typealias integer {\n"
+ "\tsize = 8; align = 8; signed = false; encoding = UTF8; base = 10;\n"
+ "} := utf8_t;\n"
+ "\n"
+ "typealias integer {\n"
+ "\tsize = 27; align = 1; signed = false;\n"
+ "\tmap = clock.monotonic.value;\n"
+ "} := uint27_clock_monotonic_t;\n"
+ "\n"
+ "typealias integer {\n"
+ "\tsize = 64; align = 8; signed = false;\n"
+ "\tmap = clock.monotonic.value;\n"
+ "} := uint64_clock_monotonic_t;\n"
+ "\n"
+ "trace {\n"
+ "\tmajor = 1;\n"
+ "\tminor = 8;\n"
+ "\tuuid = \"6a7715d0-b502-4c65-8678-6777ac7f755a\";\n"
+ "\tbyte_order = le;\n"
+ "\tpacket.header := struct {\n"
+ "\t\tuint32_t magic;\n"
+ "\t\tuint8_t uuid[16];\n"
+ "\t\tuint32_t stream_id;\n"
+ "\t\tuint64_t stream_instance_id;\n"
+ "\t};\n"
+ "};\n"
+ "\n"
+ "env {\n"
+ "\thostname = \"RTEMS\";\n"
+ "\tdomain = \"kernel\";\n"
+ "\tsysname = \"Linux\";\n"
+ "\tkernel_release = \"5\";\n"
+ "\tkernel_version = \"0\";\n"
+ "\ttracer_name = \"lttng-modules\";\n"
+ "\ttracer_major = 2;\n"
+ "\ttracer_minor = 11;\n"
+ "\ttracer_patchlevel = 0;\n"
+ "};\n"
+ "\n"
+ "clock {\n"
+ "\tname = \"monotonic\";\n"
+ "\tuuid = \"234d669d-7651-4bc1-a7fd-af581ecc6232\";\n"
+ "\tdescription = \"Monotonic Clock\";\n"
+ "\tfreq = 1000000000;\n"
+ "\toffset = 0;\n"
+ "};\n"
+ "\n"
+ "struct packet_context {\n"
+ "\tuint64_clock_monotonic_t timestamp_begin;\n"
+ "\tuint64_clock_monotonic_t timestamp_end;\n"
+ "\tuint64_t content_size;\n"
+ "\tuint64_t packet_size;\n"
+ "\tuint64_t packet_seq_num;\n"
+ "\tuint64_t events_discarded;\n"
+ "\tuint32_t cpu_id;\n"
+ "};\n"
+ "\n"
+ "struct event_header_compact {\n"
+ "\tenum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
+ "\tvariant <id> {\n"
+ "\t\tstruct {\n"
+ "\t\t\tuint27_clock_monotonic_t timestamp;\n"
+ "\t\t} compact;\n"
+ "\t\tstruct {\n"
+ "\t\t\tuint32_t id;\n"
+ "\t\t\tuint64_clock_monotonic_t timestamp;\n"
+ "\t\t} extended;\n"
+ "\t} v;\n"
+ "} align(8);\n"
+ "\n"
+ "stream {\n"
+ "\tid = 0;\n"
+ "\tevent.header := struct event_header_compact;\n"
+ "\tpacket.context := struct packet_context;\n"
+ "};\n"
+ "\n"
+ "event {\n"
+ "\tname = sched_switch;\n"
+ "\tid = 1024;\n"
+ "\tstream_id = 0;\n"
+ "\tfields := struct {\n"
+ "\t\tutf8_t _prev_comm[16];\n"
+ "\t\tint32_t _prev_tid;\n"
+ "\t\tint32_t _prev_prio;\n"
+ "\t\tint64_t _prev_state;\n"
+ "\t\tutf8_t _next_comm[16];\n"
+ "\t\tint32_t _next_tid;\n"
+ "\t\tint32_t _next_prio;\n"
+ "\t};\n"
+ "};\n"
+ "\n"
+ "event {\n"
+ "\tname = irq_handler_entry;\n"
+ "\tid = 1025;\n"
+ "\tstream_id = 0;\n"
+ "\tfields := struct {\n"
+ "\t\tint32_t _irq;\n"
+ "\t\tstring _name;\n"
+ "\t};\n"
+ "};\n"
+ "\n"
+ "event {\n"
+ "\tname = irq_handler_exit;\n"
+ "\tid = 1026;\n"
+ "\tstream_id = 0;\n"
+ "\tfields := struct {\n"
+ "\t\tint32_t _irq;\n"
+ "\t\tint32_t _ret;\n"
+ "\t};\n"
+ "};\n";
+
+void LTTNGClient::GenerateMetadata() {
+ FILE* f = std::fopen("metadata", "w");
+ if (f == NULL) {
+ throw ErrnoException("cannot create file 'metadata'");
+ }
+
+ std::fwrite(kMetadata, sizeof(kMetadata) - 1, 1, f);
+
+ for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
+ if (IsCodeEvent(static_cast<rtems_record_event>(i))) {
+ std::fprintf(f,
+ "\n"
+ "event {\n"
+ "\tname = \"%s\";\n"
+ "\tid = %i;\n"
+ "\tstream_id = 0;\n"
+ "\tfields := struct {\n"
+ "\t\tstring _code;\n"
+ "\t};\n"
+ "};\n",
+ event_to_name_[i].c_str(), i);
+ } else {
+ std::fprintf(f,
+ "\n"
+ "event {\n"
+ "\tname = \"%s\";\n"
+ "\tid = %i;\n"
+ "\tstream_id = 0;\n"
+ "\tfields := struct {\n"
+ "\t\txint64_t _data;\n"
+ "\t};\n"
+ "};\n",
+ event_to_name_[i].c_str(), i);
+ }
+ }
+
+ std::fclose(f);
+}
+
+static LTTNGClient client;
+
+static void SignalHandler(int s) {
+ client.RequestStop();
+ std::signal(s, SIG_DFL);
+}
+
+static const struct option kLongOpts[] = {
+ {"elf", 1, NULL, 'e'}, {"help", 0, NULL, 'h'},
+ {"host", 1, NULL, 'H'}, {"port", 1, NULL, 'p'},
+ {"limit", 1, NULL, 'l'}, {"base64", 0, NULL, 'b'},
+ {"zlib", 0, NULL, 'z'}, {"config", 1, NULL, 'c'},
+ {"defaults", 0, NULL, 'd'}, {NULL, 0, NULL, 0}};
+
+static void Usage(char** argv) {
+ std::cout << argv[0] << " [OPTION]... [INPUT-FILE]" << std::endl
+ << std::endl
+ << "Mandatory arguments to long options are mandatory for short "
+ "options too."
+ << std::endl
+ << " -h, --help print this help text" << std::endl
+ << " -H, --host=HOST the host IPv4 address of the "
+ "record server"
+ << std::endl
+ << " -p, --port=PORT the TCP port of the record server"
+ << std::endl
+ << " -l, --limit=LIMIT limit in bytes to process"
+ << std::endl
+ << " -b, --base64 input is base64 encoded"
+ << std::endl
+ << " -z, --zlib input is zlib compressed"
+ << std::endl
+ << " -e, --elf=ELF the ELF executable file"
+ << std::endl
+ << " -c, --config=CONFIG an INI-style configuration file"
+ << std::endl
+ << " -d, --defaults print default values for "
+ "configuration file"
+ << std::endl
+ << " INPUT-FILE the input file" << std::endl;
+}
+
+static void PrintDefaults() {
+ std::cout << "[EventNames]" << std::endl;
+
+ for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
+ std::cout << i << " = "
+ << rtems_record_event_text(static_cast<rtems_record_event>(i))
+ << std::endl;
+ }
+}
+
+int main(int argc, char** argv) {
+ const char* host = "127.0.0.1";
+ uint16_t port = 1234;
+ bool is_base64_encoded = false;
+ bool is_zlib_compressed = false;
+ const char* elf_file = nullptr;
+ const char* input_file = nullptr;
+ const char* config_file = nullptr;
+ int opt;
+ int longindex;
+
+ while ((opt = getopt_long(argc, argv, "hH:p:l:bze:c:d", &kLongOpts[0],
+ &longindex)) != -1) {
+ switch (opt) {
+ case 'h':
+ Usage(argv);
+ return 0;
+ case 'H':
+ host = optarg;
+ break;
+ case 'p':
+ port = (uint16_t)strtoul(optarg, NULL, 0);
+ break;
+ case 'l':
+ client.set_limit(strtoull(optarg, NULL, 0));
+ break;
+ case 'b':
+ is_base64_encoded = true;
+ break;
+ case 'z':
+ is_zlib_compressed = true;
+ break;
+ case 'e':
+ elf_file = optarg;
+ break;
+ case 'c':
+ config_file = optarg;
+ break;
+ case 'd':
+ PrintDefaults();
+ return 0;
+ default:
+ return 1;
+ }
+ }
+
+ if (optind == argc - 1) {
+ input_file = argv[optind];
+ ++optind;
+ }
+
+ if (optind != argc) {
+ std::cerr << argv[0] << ": unrecognized options:";
+ for (int i = optind; i < argc; ++i) {
+ std::cerr << " '" << argv[i] << "'";
+ }
+ std::cerr << std::endl;
+ return 1;
+ }
+
+ try {
+ if (is_base64_encoded) {
+ client.AddFilter(new Base64Filter());
+ }
+
+ if (is_zlib_compressed) {
+#ifdef HAVE_ZLIB_H
+ client.AddFilter(new ZlibFilter());
+#endif
+ }
+
+ client.ParseConfigFile(config_file);
+ client.GenerateMetadata();
+
+ if (elf_file != nullptr) {
+ client.OpenExecutable(elf_file);
+ }
+
+ if (input_file != nullptr) {
+ client.Open(input_file);
+ } else {
+ client.Connect(host, port);
+ }
+
+ std::signal(SIGINT, SignalHandler);
+ client.Run();
+ client.Destroy();
+ } catch (std::exception& e) {
+ std::cerr << argv[0] << ": " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/trace/record/record-text.c b/trace/record/record-text.c
new file mode 100644
index 0000000..7bdc2a6
--- /dev/null
+++ b/trace/record/record-text.c
@@ -0,0 +1,1071 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018, 2019 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD. It may be used for utility programs.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/recorddata.h>
+
+#include <stddef.h>
+
+static const char * const event_text[] = {
+ [ RTEMS_RECORD_EMPTY ] = "EMPTY",
+ [ RTEMS_RECORD_VERSION ] = "VERSION",
+ [ RTEMS_RECORD_ACCEPT_ENTRY ] = "ACCEPT_ENTRY",
+ [ RTEMS_RECORD_ACCEPT_EXIT ] = "ACCEPT_EXIT",
+ [ RTEMS_RECORD_ADDRESS ] = "ADDRESS",
+ [ RTEMS_RECORD_ALIGNED_ALLOC_ENTRY ] = "ALIGNED_ALLOC_ENTRY",
+ [ RTEMS_RECORD_ALIGNED_ALLOC_EXIT ] = "ALIGNED_ALLOC_EXIT",
+ [ RTEMS_RECORD_ARCH ] = "ARCH",
+ [ RTEMS_RECORD_ARG_0 ] = "ARG_0",
+ [ RTEMS_RECORD_ARG_1 ] = "ARG_1",
+ [ RTEMS_RECORD_ARG_2 ] = "ARG_2",
+ [ RTEMS_RECORD_ARG_3 ] = "ARG_3",
+ [ RTEMS_RECORD_ARG_4 ] = "ARG_4",
+ [ RTEMS_RECORD_ARG_5 ] = "ARG_5",
+ [ RTEMS_RECORD_ARG_6 ] = "ARG_6",
+ [ RTEMS_RECORD_ARG_7 ] = "ARG_7",
+ [ RTEMS_RECORD_ARG_8 ] = "ARG_8",
+ [ RTEMS_RECORD_ARG_9 ] = "ARG_9",
+ [ RTEMS_RECORD_BIND_ENTRY ] = "BIND_ENTRY",
+ [ RTEMS_RECORD_BIND_EXIT ] = "BIND_EXIT",
+ [ RTEMS_RECORD_BSP ] = "BSP",
+ [ RTEMS_RECORD_BUFFER ] = "BUFFER",
+ [ RTEMS_RECORD_CALLER ] = "CALLER",
+ [ RTEMS_RECORD_CALLOC_ENTRY ] = "CALLOC_ENTRY",
+ [ RTEMS_RECORD_CALLOC_EXIT ] = "CALLOC_EXIT",
+ [ RTEMS_RECORD_CHOWN_ENTRY ] = "CHOWN_ENTRY",
+ [ RTEMS_RECORD_CHOWN_EXIT ] = "CHOWN_EXIT",
+ [ RTEMS_RECORD_CLOSE_ENTRY ] = "CLOSE_ENTRY",
+ [ RTEMS_RECORD_CLOSE_EXIT ] = "CLOSE_EXIT",
+ [ RTEMS_RECORD_CONNECT_ENTRY ] = "CONNECT_ENTRY",
+ [ RTEMS_RECORD_CONNECT_EXIT ] = "CONNECT_EXIT",
+ [ RTEMS_RECORD_ETHER_INPUT ] = "ETHER_INPUT",
+ [ RTEMS_RECORD_ETHER_OUTPUT ] = "ETHER_OUTPUT",
+ [ RTEMS_RECORD_ERRNO ] = "ERRNO",
+ [ RTEMS_RECORD_FATAL_CODE ] = "FATAL_CODE",
+ [ RTEMS_RECORD_FATAL_SOURCE ] = "FATAL_SOURCE",
+ [ RTEMS_RECORD_FCHMOD_ENTRY ] = "FCHMOD_ENTRY",
+ [ RTEMS_RECORD_FCHMOD_EXIT ] = "FCHMOD_EXIT",
+ [ RTEMS_RECORD_FCNTL_ENTRY ] = "FCNTL_ENTRY",
+ [ RTEMS_RECORD_FCNTL_EXIT ] = "FCNTL_EXIT",
+ [ RTEMS_RECORD_FDATASYNC_ENTRY ] = "FDATASYNC_ENTRY",
+ [ RTEMS_RECORD_FDATASYNC_EXIT ] = "FDATASYNC_EXIT",
+ [ RTEMS_RECORD_FREE_ENTRY ] = "FREE_ENTRY",
+ [ RTEMS_RECORD_FREE_EXIT ] = "FREE_EXIT",
+ [ RTEMS_RECORD_FREQUENCY ] = "FREQUENCY",
+ [ RTEMS_RECORD_FSTAT_ENTRY ] = "FSTAT_ENTRY",
+ [ RTEMS_RECORD_FSTAT_EXIT ] = "FSTAT_EXIT",
+ [ RTEMS_RECORD_FSYNC_ENTRY ] = "FSYNC_ENTRY",
+ [ RTEMS_RECORD_FSYNC_EXIT ] = "FSYNC_EXIT",
+ [ RTEMS_RECORD_FTRUNCATE_ENTRY ] = "FTRUNCATE_ENTRY",
+ [ RTEMS_RECORD_FTRUNCATE_EXIT ] = "FTRUNCATE_EXIT",
+ [ RTEMS_RECORD_FUNCTION_ENTRY ] = "FUNCTION_ENTRY",
+ [ RTEMS_RECORD_FUNCTION_EXIT ] = "FUNCTION_EXIT",
+ [ RTEMS_RECORD_GETSOCKOPT_ENTRY ] = "GETSOCKOPT_ENTRY",
+ [ RTEMS_RECORD_GETSOCKOPT_EXIT ] = "GETSOCKOPT_EXIT",
+ [ RTEMS_RECORD_HEAP_ALLOC ] = "HEAP_ALLOC",
+ [ RTEMS_RECORD_HEAP_FREE ] = "HEAP_FREE",
+ [ RTEMS_RECORD_HEAP_SIZE ] = "HEAP_SIZE",
+ [ RTEMS_RECORD_HEAP_USAGE ] = "HEAP_USAGE",
+ [ RTEMS_RECORD_INTERRUPT_ENTRY ] = "INTERRUPT_ENTRY",
+ [ RTEMS_RECORD_INTERRUPT_EXIT ] = "INTERRUPT_EXIT",
+ [ RTEMS_RECORD_INTERRUPT_INSTALL ] = "INTERRUPT_INSTALL",
+ [ RTEMS_RECORD_INTERRUPT_REMOVE ] = "INTERRUPT_REMOVE",
+ [ RTEMS_RECORD_INTERRUPT_SERVER_ENTRY ] = "INTERRUPT_SERVER_ENTRY",
+ [ RTEMS_RECORD_INTERRUPT_SERVER_EXIT ] = "INTERRUPT_SERVER_EXIT",
+ [ RTEMS_RECORD_INTERRUPT_SERVER_INSTALL ] = "INTERRUPT_SERVER_INSTALL",
+ [ RTEMS_RECORD_INTERRUPT_SERVER_MOVE ] = "INTERRUPT_SERVER_MOVE",
+ [ RTEMS_RECORD_INTERRUPT_SERVER_REMOVE ] = "INTERRUPT_SERVER_REMOVE",
+ [ RTEMS_RECORD_INTERRUPT_SERVER_TRIGGER ] = "INTERRUPT_SERVER_TRIGGER",
+ [ RTEMS_RECORD_IOCTL_ENTRY ] = "IOCTL_ENTRY",
+ [ RTEMS_RECORD_IOCTL_EXIT ] = "IOCTL_EXIT",
+ [ RTEMS_RECORD_IP6_INPUT ] = "IP6_INPUT",
+ [ RTEMS_RECORD_IP6_OUTPUT ] = "IP6_OUTPUT",
+ [ RTEMS_RECORD_IP_INPUT ] = "IP_INPUT",
+ [ RTEMS_RECORD_IP_OUTPUT ] = "IP_OUTPUT",
+ [ RTEMS_RECORD_ISR_DISABLE ] = "ISR_DISABLE",
+ [ RTEMS_RECORD_ISR_ENABLE ] = "ISR_ENABLE",
+ [ RTEMS_RECORD_ISR_LOCK_ACQUIRE_ENTRY ] = "ISR_LOCK_ACQUIRE_ENTRY",
+ [ RTEMS_RECORD_ISR_LOCK_ACQUIRE_EXIT ] = "ISR_LOCK_ACQUIRE_EXIT",
+ [ RTEMS_RECORD_ISR_LOCK_ADDRESS ] = "ISR_LOCK_ADDRESS",
+ [ RTEMS_RECORD_ISR_LOCK_DESTROY ] = "ISR_LOCK_DESTROY",
+ [ RTEMS_RECORD_ISR_LOCK_INITIALIZE ] = "ISR_LOCK_INITIALIZE",
+ [ RTEMS_RECORD_ISR_LOCK_NAME ] = "ISR_LOCK_NAME",
+ [ RTEMS_RECORD_ISR_LOCK_RELEASE ] = "ISR_LOCK_RELEASE",
+ [ RTEMS_RECORD_KEVENT_ENTRY ] = "KEVENT_ENTRY",
+ [ RTEMS_RECORD_KEVENT_EXIT ] = "KEVENT_EXIT",
+ [ RTEMS_RECORD_KQUEUE_ENTRY ] = "KQUEUE_ENTRY",
+ [ RTEMS_RECORD_KQUEUE_EXIT ] = "KQUEUE_EXIT",
+ [ RTEMS_RECORD_LENGTH ] = "LENGTH",
+ [ RTEMS_RECORD_LINE ] = "LINE",
+ [ RTEMS_RECORD_LINK_ENTRY ] = "LINK_ENTRY",
+ [ RTEMS_RECORD_LINK_EXIT ] = "LINK_EXIT",
+ [ RTEMS_RECORD_LISTEN_ENTRY ] = "LISTEN_ENTRY",
+ [ RTEMS_RECORD_LISTEN_EXIT ] = "LISTEN_EXIT",
+ [ RTEMS_RECORD_LSEEK_ENTRY ] = "LSEEK_ENTRY",
+ [ RTEMS_RECORD_LSEEK_EXIT ] = "LSEEK_EXIT",
+ [ RTEMS_RECORD_MALLOC_ENTRY ] = "MALLOC_ENTRY",
+ [ RTEMS_RECORD_MALLOC_EXIT ] = "MALLOC_EXIT",
+ [ RTEMS_RECORD_MEMORY ] = "MEMORY",
+ [ RTEMS_RECORD_MKNOD_ENTRY ] = "MKNOD_ENTRY",
+ [ RTEMS_RECORD_MKNOD_EXIT ] = "MKNOD_EXIT",
+ [ RTEMS_RECORD_MMAP_ENTRY ] = "MMAP_ENTRY",
+ [ RTEMS_RECORD_MMAP_EXIT ] = "MMAP_EXIT",
+ [ RTEMS_RECORD_MOUNT_ENTRY ] = "MOUNT_ENTRY",
+ [ RTEMS_RECORD_MOUNT_EXIT ] = "MOUNT_EXIT",
+ [ RTEMS_RECORD_MULTILIB ] = "MULTILIB",
+ [ RTEMS_RECORD_OPEN_ENTRY ] = "OPEN_ENTRY",
+ [ RTEMS_RECORD_OPEN_EXIT ] = "OPEN_EXIT",
+ [ RTEMS_RECORD_PAGE_ALLOC ] = "PAGE_ALLOC",
+ [ RTEMS_RECORD_PAGE_FREE ] = "PAGE_FREE",
+ [ RTEMS_RECORD_PER_CPU_COUNT ] = "PER_CPU_COUNT",
+ [ RTEMS_RECORD_PER_CPU_HEAD ] = "PER_CPU_HEAD",
+ [ RTEMS_RECORD_PER_CPU_OVERFLOW ] = "PER_CPU_OVERFLOW",
+ [ RTEMS_RECORD_PER_CPU_TAIL ] = "PER_CPU_TAIL",
+ [ RTEMS_RECORD_POLL_ENTRY ] = "POLL_ENTRY",
+ [ RTEMS_RECORD_POLL_EXIT ] = "POLL_EXIT",
+ [ RTEMS_RECORD_POSIX_MEMALIGN_ENTRY ] = "POSIX_MEMALIGN_ENTRY",
+ [ RTEMS_RECORD_POSIX_MEMALIGN_EXIT ] = "POSIX_MEMALIGN_EXIT",
+ [ RTEMS_RECORD_PROCESSOR ] = "PROCESSOR",
+ [ RTEMS_RECORD_PROCESSOR_MAXIMUM ] = "PROCESSOR_MAXIMUM",
+ [ RTEMS_RECORD_READ_ENTRY ] = "READ_ENTRY",
+ [ RTEMS_RECORD_READ_EXIT ] = "READ_EXIT",
+ [ RTEMS_RECORD_READLINK_ENTRY ] = "READLINK_ENTRY",
+ [ RTEMS_RECORD_READLINK_EXIT ] = "READLINK_EXIT",
+ [ RTEMS_RECORD_READV_ENTRY ] = "READV_ENTRY",
+ [ RTEMS_RECORD_READV_EXIT ] = "READV_EXIT",
+ [ RTEMS_RECORD_REALLOC_ENTRY ] = "REALLOC_ENTRY",
+ [ RTEMS_RECORD_REALLOC_EXIT ] = "REALLOC_EXIT",
+ [ RTEMS_RECORD_RECV_ENTRY ] = "RECV_ENTRY",
+ [ RTEMS_RECORD_RECV_EXIT ] = "RECV_EXIT",
+ [ RTEMS_RECORD_RECVFROM_ENTRY ] = "RECVFROM_ENTRY",
+ [ RTEMS_RECORD_RECVFROM_EXIT ] = "RECVFROM_EXIT",
+ [ RTEMS_RECORD_RECVMSG_ENTRY ] = "RECVMSG_ENTRY",
+ [ RTEMS_RECORD_RECVMSG_EXIT ] = "RECVMSG_EXIT",
+ [ RTEMS_RECORD_REGISTERS ] = "REGISTERS",
+ [ RTEMS_RECORD_RENAME_ENTRY ] = "RENAME_ENTRY",
+ [ RTEMS_RECORD_RENAME_EXIT ] = "RENAME_EXIT",
+ [ RTEMS_RECORD_RETURN_0 ] = "RETURN_0",
+ [ RTEMS_RECORD_RETURN_1 ] = "RETURN_1",
+ [ RTEMS_RECORD_RETURN_2 ] = "RETURN_2",
+ [ RTEMS_RECORD_RETURN_3 ] = "RETURN_3",
+ [ RTEMS_RECORD_RETURN_4 ] = "RETURN_4",
+ [ RTEMS_RECORD_RETURN_5 ] = "RETURN_5",
+ [ RTEMS_RECORD_RETURN_6 ] = "RETURN_6",
+ [ RTEMS_RECORD_RETURN_7 ] = "RETURN_7",
+ [ RTEMS_RECORD_RETURN_8 ] = "RETURN_8",
+ [ RTEMS_RECORD_RETURN_9 ] = "RETURN_9",
+ [ RTEMS_RECORD_RTEMS_BARRIER_CREATE ] = "RTEMS_BARRIER_CREATE",
+ [ RTEMS_RECORD_RTEMS_BARRIER_DELETE ] = "RTEMS_BARRIER_DELETE",
+ [ RTEMS_RECORD_RTEMS_BARRIER_RELEASE ] = "RTEMS_BARRIER_RELEASE",
+ [ RTEMS_RECORD_RTEMS_BARRIER_WAIT ] = "RTEMS_BARRIER_WAIT",
+ [ RTEMS_RECORD_RTEMS_CALLOC_ENTRY ] = "RTEMS_CALLOC_ENTRY",
+ [ RTEMS_RECORD_RTEMS_CALLOC_EXIT ] = "RTEMS_CALLOC_EXIT",
+ [ RTEMS_RECORD_RTEMS_EVENT_RECEIVE ] = "RTEMS_EVENT_RECEIVE",
+ [ RTEMS_RECORD_RTEMS_EVENT_SEND ] = "RTEMS_EVENT_SEND",
+ [ RTEMS_RECORD_RTEMS_EVENT_SYSTEM_RECEIVE ] = "RTEMS_EVENT_SYSTEM_RECEIVE",
+ [ RTEMS_RECORD_RTEMS_EVENT_SYSTEM_SEND ] = "RTEMS_EVENT_SYSTEM_SEND",
+ [ RTEMS_RECORD_RTEMS_MALLOC_ENTRY ] = "RTEMS_MALLOC_ENTRY",
+ [ RTEMS_RECORD_RTEMS_MALLOC_EXIT ] = "RTEMS_MALLOC_EXIT",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_BROADCAST ] = "RTEMS_MESSAGE_QUEUE_BROADCAST",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_CREATE ] = "RTEMS_MESSAGE_QUEUE_CREATE",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_DELETE ] = "RTEMS_MESSAGE_QUEUE_DELETE",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_FLUSH ] = "RTEMS_MESSAGE_QUEUE_FLUSH",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_RECEIVE ] = "RTEMS_MESSAGE_QUEUE_RECEIVE",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_SEND ] = "RTEMS_MESSAGE_QUEUE_SEND",
+ [ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_URGENT ] = "RTEMS_MESSAGE_QUEUE_URGENT",
+ [ RTEMS_RECORD_RTEMS_PARTITION_CREATE ] = "RTEMS_PARTITION_CREATE",
+ [ RTEMS_RECORD_RTEMS_PARTITION_DELETE ] = "RTEMS_PARTITION_DELETE",
+ [ RTEMS_RECORD_RTEMS_PARTITION_GET_BUFFER ] = "RTEMS_PARTITION_GET_BUFFER",
+ [ RTEMS_RECORD_RTEMS_PARTITION_RETURN_BUFFER ] = "RTEMS_PARTITION_RETURN_BUFFER",
+ [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CANCEL ] = "RTEMS_RATE_MONOTONIC_CANCEL",
+ [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CREATE ] = "RTEMS_RATE_MONOTONIC_CREATE",
+ [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_DELETE ] = "RTEMS_RATE_MONOTONIC_DELETE",
+ [ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_PERIOD ] = "RTEMS_RATE_MONOTONIC_PERIOD",
+ [ RTEMS_RECORD_RTEMS_SEMAPHORE_CREATE ] = "RTEMS_SEMAPHORE_CREATE",
+ [ RTEMS_RECORD_RTEMS_SEMAPHORE_DELETE ] = "RTEMS_SEMAPHORE_DELETE",
+ [ RTEMS_RECORD_RTEMS_SEMAPHORE_FLUSH ] = "RTEMS_SEMAPHORE_FLUSH",
+ [ RTEMS_RECORD_RTEMS_SEMAPHORE_OBTAIN ] = "RTEMS_SEMAPHORE_OBTAIN",
+ [ RTEMS_RECORD_RTEMS_SEMAPHORE_RELEASE ] = "RTEMS_SEMAPHORE_RELEASE",
+ [ RTEMS_RECORD_RTEMS_TIMER_CANCEL ] = "RTEMS_TIMER_CANCEL",
+ [ RTEMS_RECORD_RTEMS_TIMER_CREATE ] = "RTEMS_TIMER_CREATE",
+ [ RTEMS_RECORD_RTEMS_TIMER_DELETE ] = "RTEMS_TIMER_DELETE",
+ [ RTEMS_RECORD_RTEMS_TIMER_FIRE_AFTER ] = "RTEMS_TIMER_FIRE_AFTER",
+ [ RTEMS_RECORD_RTEMS_TIMER_FIRE_WHEN ] = "RTEMS_TIMER_FIRE_WHEN",
+ [ RTEMS_RECORD_RTEMS_TIMER_RESET ] = "RTEMS_TIMER_RESET",
+ [ RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_AFTER ] = "RTEMS_TIMER_SERVER_FIRE_AFTER",
+ [ RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_WHEN ] = "RTEMS_TIMER_SERVER_FIRE_WHEN",
+ [ RTEMS_RECORD_SBWAIT_ENTRY ] = "SBWAIT_ENTRY",
+ [ RTEMS_RECORD_SBWAIT_EXIT ] = "SBWAIT_EXIT",
+ [ RTEMS_RECORD_SBWAKEUP_ENTRY ] = "SBWAKEUP_ENTRY",
+ [ RTEMS_RECORD_SBWAKEUP_EXIT ] = "SBWAKEUP_EXIT",
+ [ RTEMS_RECORD_SCHEDULER_ADD_PROCESSOR ] = "SCHEDULER_ADD_PROCESSOR",
+ [ RTEMS_RECORD_SCHEDULER_ASK_FOR_HELP ] = "SCHEDULER_ASK_FOR_HELP",
+ [ RTEMS_RECORD_SCHEDULER_BLOCK ] = "SCHEDULER_BLOCK",
+ [ RTEMS_RECORD_SCHEDULER_CANCEL_JOB ] = "SCHEDULER_CANCEL_JOB",
+ [ RTEMS_RECORD_SCHEDULER_ID ] = "SCHEDULER_ID",
+ [ RTEMS_RECORD_SCHEDULER_MAP_PRIORITY ] = "SCHEDULER_MAP_PRIORITY",
+ [ RTEMS_RECORD_SCHEDULER_NAME ] = "SCHEDULER_NAME",
+ [ RTEMS_RECORD_SCHEDULER_PIN ] = "SCHEDULER_PIN",
+ [ RTEMS_RECORD_SCHEDULER_RECONSIDER_HELP_REQUEST ] = "SCHEDULER_RECONSIDER_HELP_REQUEST",
+ [ RTEMS_RECORD_SCHEDULER_RELEASE_JOB ] = "SCHEDULER_RELEASE_JOB",
+ [ RTEMS_RECORD_SCHEDULER_REMOVE_PROCESSOR ] = "SCHEDULER_REMOVE_PROCESSOR",
+ [ RTEMS_RECORD_SCHEDULER_SCHEDULE ] = "SCHEDULER_SCHEDULE",
+ [ RTEMS_RECORD_SCHEDULER_SET_AFFINITY ] = "SCHEDULER_SET_AFFINITY",
+ [ RTEMS_RECORD_SCHEDULER_TICK ] = "SCHEDULER_TICK",
+ [ RTEMS_RECORD_SCHEDULER_UNBLOCK ] = "SCHEDULER_UNBLOCK",
+ [ RTEMS_RECORD_SCHEDULER_UNMAP_PRIORITY ] = "SCHEDULER_UNMAP_PRIORITY",
+ [ RTEMS_RECORD_SCHEDULER_UNPIN ] = "SCHEDULER_UNPIN",
+ [ RTEMS_RECORD_SCHEDULER_UPDATE_PRIORITY ] = "SCHEDULER_UPDATE_PRIORITY",
+ [ RTEMS_RECORD_SCHEDULER_WITHDRAW_NODE ] = "SCHEDULER_WITHDRAW_NODE",
+ [ RTEMS_RECORD_SCHEDULER_YIELD ] = "SCHEDULER_YIELD",
+ [ RTEMS_RECORD_SELECT_ENTRY ] = "SELECT_ENTRY",
+ [ RTEMS_RECORD_SELECT_EXIT ] = "SELECT_EXIT",
+ [ RTEMS_RECORD_SEND_ENTRY ] = "SEND_ENTRY",
+ [ RTEMS_RECORD_SEND_EXIT ] = "SEND_EXIT",
+ [ RTEMS_RECORD_SENDMSG_ENTRY ] = "SENDMSG_ENTRY",
+ [ RTEMS_RECORD_SENDMSG_EXIT ] = "SENDMSG_EXIT",
+ [ RTEMS_RECORD_SENDTO_ENTRY ] = "SENDTO_ENTRY",
+ [ RTEMS_RECORD_SENDTO_EXIT ] = "SENDTO_EXIT",
+ [ RTEMS_RECORD_SETSOCKOPT_ENTRY ] = "SETSOCKOPT_ENTRY",
+ [ RTEMS_RECORD_SETSOCKOPT_EXIT ] = "SETSOCKOPT_EXIT",
+ [ RTEMS_RECORD_SHUTDOWN_ENTRY ] = "SHUTDOWN_ENTRY",
+ [ RTEMS_RECORD_SHUTDOWN_EXIT ] = "SHUTDOWN_EXIT",
+ [ RTEMS_RECORD_SOABORT_ENTRY ] = "SOABORT_ENTRY",
+ [ RTEMS_RECORD_SOABORT_EXIT ] = "SOABORT_EXIT",
+ [ RTEMS_RECORD_SOACCEPT_ENTRY ] = "SOACCEPT_ENTRY",
+ [ RTEMS_RECORD_SOACCEPT_EXIT ] = "SOACCEPT_EXIT",
+ [ RTEMS_RECORD_SOALLOC_ENTRY ] = "SOALLOC_ENTRY",
+ [ RTEMS_RECORD_SOALLOC_EXIT ] = "SOALLOC_EXIT",
+ [ RTEMS_RECORD_SOBINDAT_ENTRY ] = "SOBINDAT_ENTRY",
+ [ RTEMS_RECORD_SOBINDAT_EXIT ] = "SOBINDAT_EXIT",
+ [ RTEMS_RECORD_SOBIND_ENTRY ] = "SOBIND_ENTRY",
+ [ RTEMS_RECORD_SOBIND_EXIT ] = "SOBIND_EXIT",
+ [ RTEMS_RECORD_SOCKET_ENTRY ] = "SOCKET_ENTRY",
+ [ RTEMS_RECORD_SOCKET_EXIT ] = "SOCKET_EXIT",
+ [ RTEMS_RECORD_SOCLOSE_ENTRY ] = "SOCLOSE_ENTRY",
+ [ RTEMS_RECORD_SOCLOSE_EXIT ] = "SOCLOSE_EXIT",
+ [ RTEMS_RECORD_SOCONNECT2_ENTRY ] = "SOCONNECT2_ENTRY",
+ [ RTEMS_RECORD_SOCONNECT2_EXIT ] = "SOCONNECT2_EXIT",
+ [ RTEMS_RECORD_SOCONNECTAT_ENTRY ] = "SOCONNECTAT_ENTRY",
+ [ RTEMS_RECORD_SOCONNECTAT_EXIT ] = "SOCONNECTAT_EXIT",
+ [ RTEMS_RECORD_SOCREATE_ENTRY ] = "SOCREATE_ENTRY",
+ [ RTEMS_RECORD_SOCREATE_EXIT ] = "SOCREATE_EXIT",
+ [ RTEMS_RECORD_SODEALLOC_ENTRY ] = "SODEALLOC_ENTRY",
+ [ RTEMS_RECORD_SODEALLOC_EXIT ] = "SODEALLOC_EXIT",
+ [ RTEMS_RECORD_SODISCONNECT_ENTRY ] = "SODISCONNECT_ENTRY",
+ [ RTEMS_RECORD_SODISCONNECT_EXIT ] = "SODISCONNECT_EXIT",
+ [ RTEMS_RECORD_SOFREE_ENTRY ] = "SOFREE_ENTRY",
+ [ RTEMS_RECORD_SOFREE_EXIT ] = "SOFREE_EXIT",
+ [ RTEMS_RECORD_SOLISTEN_ENTRY ] = "SOLISTEN_ENTRY",
+ [ RTEMS_RECORD_SOLISTEN_EXIT ] = "SOLISTEN_EXIT",
+ [ RTEMS_RECORD_SONEWCONN_ENTRY ] = "SONEWCONN_ENTRY",
+ [ RTEMS_RECORD_SONEWCONN_EXIT ] = "SONEWCONN_EXIT",
+ [ RTEMS_RECORD_SORECEIVE_ENTRY ] = "SORECEIVE_ENTRY",
+ [ RTEMS_RECORD_SORECEIVE_EXIT ] = "SORECEIVE_EXIT",
+ [ RTEMS_RECORD_SORFLUSH_ENTRY ] = "SORFLUSH_ENTRY",
+ [ RTEMS_RECORD_SORFLUSH_EXIT ] = "SORFLUSH_EXIT",
+ [ RTEMS_RECORD_SOSEND_ENTRY ] = "SOSEND_ENTRY",
+ [ RTEMS_RECORD_SOSEND_EXIT ] = "SOSEND_EXIT",
+ [ RTEMS_RECORD_SOSHUTDOWN_ENTRY ] = "SOSHUTDOWN_ENTRY",
+ [ RTEMS_RECORD_SOSHUTDOWN_EXIT ] = "SOSHUTDOWN_EXIT",
+ [ RTEMS_RECORD_STATVFS_ENTRY ] = "STATVFS_ENTRY",
+ [ RTEMS_RECORD_STATVFS_EXIT ] = "STATVFS_EXIT",
+ [ RTEMS_RECORD_SYMLINK_ENTRY ] = "SYMLINK_ENTRY",
+ [ RTEMS_RECORD_SYMLINK_EXIT ] = "SYMLINK_EXIT",
+ [ RTEMS_RECORD_TCP_CLOSE ] = "TCP_CLOSE",
+ [ RTEMS_RECORD_TCP_INPUT ] = "TCP_INPUT",
+ [ RTEMS_RECORD_TCP_OUTPUT ] = "TCP_OUTPUT",
+ [ RTEMS_RECORD_THREAD_BEGIN ] = "THREAD_BEGIN",
+ [ RTEMS_RECORD_THREAD_CONTINUE_ENTRY ] = "THREAD_CONTINUE_ENTRY",
+ [ RTEMS_RECORD_THREAD_CONTINUE_EXIT ] = "THREAD_CONTINUE_EXIT",
+ [ RTEMS_RECORD_THREAD_CREATE ] = "THREAD_CREATE",
+ [ RTEMS_RECORD_THREAD_DELETE ] = "THREAD_DELETE",
+ [ RTEMS_RECORD_THREAD_DISPATCH_DISABLE ] = "THREAD_DISPATCH_DISABLE",
+ [ RTEMS_RECORD_THREAD_DISPATCH_ENABLE ] = "THREAD_DISPATCH_ENABLE",
+ [ RTEMS_RECORD_THREAD_EXIT ] = "THREAD_EXIT",
+ [ RTEMS_RECORD_THREAD_EXITTED ] = "THREAD_EXITTED",
+ [ RTEMS_RECORD_THREAD_ID ] = "THREAD_ID",
+ [ RTEMS_RECORD_THREAD_NAME ] = "THREAD_NAME",
+ [ RTEMS_RECORD_THREAD_PRIO_CURRENT_HIGH ] = "THREAD_PRIO_CURRENT_HIGH",
+ [ RTEMS_RECORD_THREAD_PRIO_CURRENT_LOW ] = "THREAD_PRIO_CURRENT_LOW",
+ [ RTEMS_RECORD_THREAD_PRIO_REAL_HIGH ] = "THREAD_PRIO_REAL_HIGH",
+ [ RTEMS_RECORD_THREAD_PRIO_REAL_LOW ] = "THREAD_PRIO_REAL_LOW",
+ [ RTEMS_RECORD_THREAD_QUEUE_ADDRESS ] = "THREAD_QUEUE_ADDRESS",
+ [ RTEMS_RECORD_THREAD_QUEUE_DESTROY ] = "THREAD_QUEUE_DESTROY",
+ [ RTEMS_RECORD_THREAD_QUEUE_ENQUEUE ] = "THREAD_QUEUE_ENQUEUE",
+ [ RTEMS_RECORD_THREAD_QUEUE_ENQUEUE_STICKY ] = "THREAD_QUEUE_ENQUEUE_STICKY",
+ [ RTEMS_RECORD_THREAD_QUEUE_EXTRACT ] = "THREAD_QUEUE_EXTRACT",
+ [ RTEMS_RECORD_THREAD_QUEUE_ID ] = "THREAD_QUEUE_ID",
+ [ RTEMS_RECORD_THREAD_QUEUE_INITIALIZE ] = "THREAD_QUEUE_INITIALIZE",
+ [ RTEMS_RECORD_THREAD_QUEUE_NAME ] = "THREAD_QUEUE_NAME",
+ [ RTEMS_RECORD_THREAD_QUEUE_SURRENDER ] = "THREAD_QUEUE_SURRENDER",
+ [ RTEMS_RECORD_THREAD_QUEUE_SURRENDER_STICKY ] = "THREAD_QUEUE_SURRENDER_STICKY",
+ [ RTEMS_RECORD_THREAD_RESOURCE_OBTAIN ] = "THREAD_RESOURCE_OBTAIN",
+ [ RTEMS_RECORD_THREAD_RESOURCE_RELEASE ] = "THREAD_RESOURCE_RELEASE",
+ [ RTEMS_RECORD_THREAD_RESTART ] = "THREAD_RESTART",
+ [ RTEMS_RECORD_THREAD_STACK_CURRENT ] = "THREAD_STACK_CURRENT",
+ [ RTEMS_RECORD_THREAD_STACK_SIZE ] = "THREAD_STACK_SIZE",
+ [ RTEMS_RECORD_THREAD_STACK_USAGE ] = "THREAD_STACK_USAGE",
+ [ RTEMS_RECORD_THREAD_START ] = "THREAD_START",
+ [ RTEMS_RECORD_THREAD_STATE_CLEAR ] = "THREAD_STATE_CLEAR",
+ [ RTEMS_RECORD_THREAD_STATE_SET ] = "THREAD_STATE_SET",
+ [ RTEMS_RECORD_THREAD_SWITCH_IN ] = "THREAD_SWITCH_IN",
+ [ RTEMS_RECORD_THREAD_SWITCH_OUT ] = "THREAD_SWITCH_OUT",
+ [ RTEMS_RECORD_THREAD_TERMINATE ] = "THREAD_TERMINATE",
+ [ RTEMS_RECORD_THREAD_TIMER_INSERT_MONOTONIC ] = "THREAD_TIMER_INSERT_MONOTONIC",
+ [ RTEMS_RECORD_THREAD_TIMER_INSERT_REALTIME ] = "THREAD_TIMER_INSERT_REALTIME",
+ [ RTEMS_RECORD_THREAD_TIMER_INSERT_TICKS ] = "THREAD_TIMER_INSERT_TICKS",
+ [ RTEMS_RECORD_THREAD_TIMER_REMOVE ] = "THREAD_TIMER_REMOVE",
+ [ RTEMS_RECORD_TOOLS ] = "TOOLS",
+ [ RTEMS_RECORD_UDP_INPUT ] = "UDP_INPUT",
+ [ RTEMS_RECORD_UDP_OUTPUT ] = "UDP_OUTPUT",
+ [ RTEMS_RECORD_UMA_ALLOC_PTR ] = "UMA_ALLOC_PTR",
+ [ RTEMS_RECORD_UMA_ALLOC_ZONE ] = "UMA_ALLOC_ZONE",
+ [ RTEMS_RECORD_UMA_FREE_PTR ] = "UMA_FREE_PTR",
+ [ RTEMS_RECORD_UMA_FREE_ZONE ] = "UMA_FREE_ZONE",
+ [ RTEMS_RECORD_UNLINK_ENTRY ] = "UNLINK_ENTRY",
+ [ RTEMS_RECORD_UNLINK_EXIT ] = "UNLINK_EXIT",
+ [ RTEMS_RECORD_UNMOUNT_ENTRY ] = "UNMOUNT_ENTRY",
+ [ RTEMS_RECORD_UNMOUNT_EXIT ] = "UNMOUNT_EXIT",
+ [ RTEMS_RECORD_UPTIME_HIGH ] = "UPTIME_HIGH",
+ [ RTEMS_RECORD_UPTIME_LOW ] = "UPTIME_LOW",
+ [ RTEMS_RECORD_VERSION_CONTROL_KEY ] = "VERSION_CONTROL_KEY",
+ [ RTEMS_RECORD_WATCHDOG_ADDRESS ] = "WATCHDOG_ADDRESS",
+ [ RTEMS_RECORD_WATCHDOG_CPU ] = "WATCHDOG_CPU",
+ [ RTEMS_RECORD_WATCHDOG_INITIALIZE ] = "WATCHDOG_INITIALIZE",
+ [ RTEMS_RECORD_WATCHDOG_INSERT ] = "WATCHDOG_INSERT",
+ [ RTEMS_RECORD_WATCHDOG_PREINITIALIZE ] = "WATCHDOG_PREINITIALIZE",
+ [ RTEMS_RECORD_WATCHDOG_REMOVE ] = "WATCHDOG_REMOVE",
+ [ RTEMS_RECORD_WATCHDOG_ROUTINE ] = "WATCHDOG_ROUTINE",
+ [ RTEMS_RECORD_WATCHDOG_STATE ] = "WATCHDOG_STATE",
+ [ RTEMS_RECORD_WORKSPACE_ALLOC_ENTRY ] = "WORKSPACE_ALLOC_ENTRY",
+ [ RTEMS_RECORD_WORKSPACE_ALLOC_EXIT ] = "WORKSPACE_ALLOC_EXIT",
+ [ RTEMS_RECORD_WORKSPACE_FREE_ENTY ] = "WORKSPACE_FREE_ENTY",
+ [ RTEMS_RECORD_WORKSPACE_FREE_EXIT ] = "WORKSPACE_FREE_EXIT",
+ [ RTEMS_RECORD_WORKSPACE_SIZE ] = "WORKSPACE_SIZE",
+ [ RTEMS_RECORD_WORKSPACE_USAGE ] = "WORKSPACE_USAGE",
+ [ RTEMS_RECORD_WRITE_ENTRY ] = "WRITE_ENTRY",
+ [ RTEMS_RECORD_WRITE_EXIT ] = "WRITE_EXIT",
+ [ RTEMS_RECORD_WRITEV_ENTRY ] = "WRITEV_ENTRY",
+ [ RTEMS_RECORD_WRITEV_EXIT ] = "WRITEV_EXIT",
+ [ RTEMS_RECORD_SYSTEM_341 ] = "SYSTEM_341",
+ [ RTEMS_RECORD_SYSTEM_342 ] = "SYSTEM_342",
+ [ RTEMS_RECORD_SYSTEM_343 ] = "SYSTEM_343",
+ [ RTEMS_RECORD_SYSTEM_344 ] = "SYSTEM_344",
+ [ RTEMS_RECORD_SYSTEM_345 ] = "SYSTEM_345",
+ [ RTEMS_RECORD_SYSTEM_346 ] = "SYSTEM_346",
+ [ RTEMS_RECORD_SYSTEM_347 ] = "SYSTEM_347",
+ [ RTEMS_RECORD_SYSTEM_348 ] = "SYSTEM_348",
+ [ RTEMS_RECORD_SYSTEM_349 ] = "SYSTEM_349",
+ [ RTEMS_RECORD_SYSTEM_350 ] = "SYSTEM_350",
+ [ RTEMS_RECORD_SYSTEM_351 ] = "SYSTEM_351",
+ [ RTEMS_RECORD_SYSTEM_352 ] = "SYSTEM_352",
+ [ RTEMS_RECORD_SYSTEM_353 ] = "SYSTEM_353",
+ [ RTEMS_RECORD_SYSTEM_354 ] = "SYSTEM_354",
+ [ RTEMS_RECORD_SYSTEM_355 ] = "SYSTEM_355",
+ [ RTEMS_RECORD_SYSTEM_356 ] = "SYSTEM_356",
+ [ RTEMS_RECORD_SYSTEM_357 ] = "SYSTEM_357",
+ [ RTEMS_RECORD_SYSTEM_358 ] = "SYSTEM_358",
+ [ RTEMS_RECORD_SYSTEM_359 ] = "SYSTEM_359",
+ [ RTEMS_RECORD_SYSTEM_360 ] = "SYSTEM_360",
+ [ RTEMS_RECORD_SYSTEM_361 ] = "SYSTEM_361",
+ [ RTEMS_RECORD_SYSTEM_362 ] = "SYSTEM_362",
+ [ RTEMS_RECORD_SYSTEM_363 ] = "SYSTEM_363",
+ [ RTEMS_RECORD_SYSTEM_364 ] = "SYSTEM_364",
+ [ RTEMS_RECORD_SYSTEM_365 ] = "SYSTEM_365",
+ [ RTEMS_RECORD_SYSTEM_366 ] = "SYSTEM_366",
+ [ RTEMS_RECORD_SYSTEM_367 ] = "SYSTEM_367",
+ [ RTEMS_RECORD_SYSTEM_368 ] = "SYSTEM_368",
+ [ RTEMS_RECORD_SYSTEM_369 ] = "SYSTEM_369",
+ [ RTEMS_RECORD_SYSTEM_370 ] = "SYSTEM_370",
+ [ RTEMS_RECORD_SYSTEM_371 ] = "SYSTEM_371",
+ [ RTEMS_RECORD_SYSTEM_372 ] = "SYSTEM_372",
+ [ RTEMS_RECORD_SYSTEM_373 ] = "SYSTEM_373",
+ [ RTEMS_RECORD_SYSTEM_374 ] = "SYSTEM_374",
+ [ RTEMS_RECORD_SYSTEM_375 ] = "SYSTEM_375",
+ [ RTEMS_RECORD_SYSTEM_376 ] = "SYSTEM_376",
+ [ RTEMS_RECORD_SYSTEM_377 ] = "SYSTEM_377",
+ [ RTEMS_RECORD_SYSTEM_378 ] = "SYSTEM_378",
+ [ RTEMS_RECORD_SYSTEM_379 ] = "SYSTEM_379",
+ [ RTEMS_RECORD_SYSTEM_380 ] = "SYSTEM_380",
+ [ RTEMS_RECORD_SYSTEM_381 ] = "SYSTEM_381",
+ [ RTEMS_RECORD_SYSTEM_382 ] = "SYSTEM_382",
+ [ RTEMS_RECORD_SYSTEM_383 ] = "SYSTEM_383",
+ [ RTEMS_RECORD_SYSTEM_384 ] = "SYSTEM_384",
+ [ RTEMS_RECORD_SYSTEM_385 ] = "SYSTEM_385",
+ [ RTEMS_RECORD_SYSTEM_386 ] = "SYSTEM_386",
+ [ RTEMS_RECORD_SYSTEM_387 ] = "SYSTEM_387",
+ [ RTEMS_RECORD_SYSTEM_388 ] = "SYSTEM_388",
+ [ RTEMS_RECORD_SYSTEM_389 ] = "SYSTEM_389",
+ [ RTEMS_RECORD_SYSTEM_390 ] = "SYSTEM_390",
+ [ RTEMS_RECORD_SYSTEM_391 ] = "SYSTEM_391",
+ [ RTEMS_RECORD_SYSTEM_392 ] = "SYSTEM_392",
+ [ RTEMS_RECORD_SYSTEM_393 ] = "SYSTEM_393",
+ [ RTEMS_RECORD_SYSTEM_394 ] = "SYSTEM_394",
+ [ RTEMS_RECORD_SYSTEM_395 ] = "SYSTEM_395",
+ [ RTEMS_RECORD_SYSTEM_396 ] = "SYSTEM_396",
+ [ RTEMS_RECORD_SYSTEM_397 ] = "SYSTEM_397",
+ [ RTEMS_RECORD_SYSTEM_398 ] = "SYSTEM_398",
+ [ RTEMS_RECORD_SYSTEM_399 ] = "SYSTEM_399",
+ [ RTEMS_RECORD_SYSTEM_400 ] = "SYSTEM_400",
+ [ RTEMS_RECORD_SYSTEM_401 ] = "SYSTEM_401",
+ [ RTEMS_RECORD_SYSTEM_402 ] = "SYSTEM_402",
+ [ RTEMS_RECORD_SYSTEM_403 ] = "SYSTEM_403",
+ [ RTEMS_RECORD_SYSTEM_404 ] = "SYSTEM_404",
+ [ RTEMS_RECORD_SYSTEM_405 ] = "SYSTEM_405",
+ [ RTEMS_RECORD_SYSTEM_406 ] = "SYSTEM_406",
+ [ RTEMS_RECORD_SYSTEM_407 ] = "SYSTEM_407",
+ [ RTEMS_RECORD_SYSTEM_408 ] = "SYSTEM_408",
+ [ RTEMS_RECORD_SYSTEM_409 ] = "SYSTEM_409",
+ [ RTEMS_RECORD_SYSTEM_410 ] = "SYSTEM_410",
+ [ RTEMS_RECORD_SYSTEM_411 ] = "SYSTEM_411",
+ [ RTEMS_RECORD_SYSTEM_412 ] = "SYSTEM_412",
+ [ RTEMS_RECORD_SYSTEM_413 ] = "SYSTEM_413",
+ [ RTEMS_RECORD_SYSTEM_414 ] = "SYSTEM_414",
+ [ RTEMS_RECORD_SYSTEM_415 ] = "SYSTEM_415",
+ [ RTEMS_RECORD_SYSTEM_416 ] = "SYSTEM_416",
+ [ RTEMS_RECORD_SYSTEM_417 ] = "SYSTEM_417",
+ [ RTEMS_RECORD_SYSTEM_418 ] = "SYSTEM_418",
+ [ RTEMS_RECORD_SYSTEM_419 ] = "SYSTEM_419",
+ [ RTEMS_RECORD_SYSTEM_420 ] = "SYSTEM_420",
+ [ RTEMS_RECORD_SYSTEM_421 ] = "SYSTEM_421",
+ [ RTEMS_RECORD_SYSTEM_422 ] = "SYSTEM_422",
+ [ RTEMS_RECORD_SYSTEM_423 ] = "SYSTEM_423",
+ [ RTEMS_RECORD_SYSTEM_424 ] = "SYSTEM_424",
+ [ RTEMS_RECORD_SYSTEM_425 ] = "SYSTEM_425",
+ [ RTEMS_RECORD_SYSTEM_426 ] = "SYSTEM_426",
+ [ RTEMS_RECORD_SYSTEM_427 ] = "SYSTEM_427",
+ [ RTEMS_RECORD_SYSTEM_428 ] = "SYSTEM_428",
+ [ RTEMS_RECORD_SYSTEM_429 ] = "SYSTEM_429",
+ [ RTEMS_RECORD_SYSTEM_430 ] = "SYSTEM_430",
+ [ RTEMS_RECORD_SYSTEM_431 ] = "SYSTEM_431",
+ [ RTEMS_RECORD_SYSTEM_432 ] = "SYSTEM_432",
+ [ RTEMS_RECORD_SYSTEM_433 ] = "SYSTEM_433",
+ [ RTEMS_RECORD_SYSTEM_434 ] = "SYSTEM_434",
+ [ RTEMS_RECORD_SYSTEM_435 ] = "SYSTEM_435",
+ [ RTEMS_RECORD_SYSTEM_436 ] = "SYSTEM_436",
+ [ RTEMS_RECORD_SYSTEM_437 ] = "SYSTEM_437",
+ [ RTEMS_RECORD_SYSTEM_438 ] = "SYSTEM_438",
+ [ RTEMS_RECORD_SYSTEM_439 ] = "SYSTEM_439",
+ [ RTEMS_RECORD_SYSTEM_440 ] = "SYSTEM_440",
+ [ RTEMS_RECORD_SYSTEM_441 ] = "SYSTEM_441",
+ [ RTEMS_RECORD_SYSTEM_442 ] = "SYSTEM_442",
+ [ RTEMS_RECORD_SYSTEM_443 ] = "SYSTEM_443",
+ [ RTEMS_RECORD_SYSTEM_444 ] = "SYSTEM_444",
+ [ RTEMS_RECORD_SYSTEM_445 ] = "SYSTEM_445",
+ [ RTEMS_RECORD_SYSTEM_446 ] = "SYSTEM_446",
+ [ RTEMS_RECORD_SYSTEM_447 ] = "SYSTEM_447",
+ [ RTEMS_RECORD_SYSTEM_448 ] = "SYSTEM_448",
+ [ RTEMS_RECORD_SYSTEM_449 ] = "SYSTEM_449",
+ [ RTEMS_RECORD_SYSTEM_450 ] = "SYSTEM_450",
+ [ RTEMS_RECORD_SYSTEM_451 ] = "SYSTEM_451",
+ [ RTEMS_RECORD_SYSTEM_452 ] = "SYSTEM_452",
+ [ RTEMS_RECORD_SYSTEM_453 ] = "SYSTEM_453",
+ [ RTEMS_RECORD_SYSTEM_454 ] = "SYSTEM_454",
+ [ RTEMS_RECORD_SYSTEM_455 ] = "SYSTEM_455",
+ [ RTEMS_RECORD_SYSTEM_456 ] = "SYSTEM_456",
+ [ RTEMS_RECORD_SYSTEM_457 ] = "SYSTEM_457",
+ [ RTEMS_RECORD_SYSTEM_458 ] = "SYSTEM_458",
+ [ RTEMS_RECORD_SYSTEM_459 ] = "SYSTEM_459",
+ [ RTEMS_RECORD_SYSTEM_460 ] = "SYSTEM_460",
+ [ RTEMS_RECORD_SYSTEM_461 ] = "SYSTEM_461",
+ [ RTEMS_RECORD_SYSTEM_462 ] = "SYSTEM_462",
+ [ RTEMS_RECORD_SYSTEM_463 ] = "SYSTEM_463",
+ [ RTEMS_RECORD_SYSTEM_464 ] = "SYSTEM_464",
+ [ RTEMS_RECORD_SYSTEM_465 ] = "SYSTEM_465",
+ [ RTEMS_RECORD_SYSTEM_466 ] = "SYSTEM_466",
+ [ RTEMS_RECORD_SYSTEM_467 ] = "SYSTEM_467",
+ [ RTEMS_RECORD_SYSTEM_468 ] = "SYSTEM_468",
+ [ RTEMS_RECORD_SYSTEM_469 ] = "SYSTEM_469",
+ [ RTEMS_RECORD_SYSTEM_470 ] = "SYSTEM_470",
+ [ RTEMS_RECORD_SYSTEM_471 ] = "SYSTEM_471",
+ [ RTEMS_RECORD_SYSTEM_472 ] = "SYSTEM_472",
+ [ RTEMS_RECORD_SYSTEM_473 ] = "SYSTEM_473",
+ [ RTEMS_RECORD_SYSTEM_474 ] = "SYSTEM_474",
+ [ RTEMS_RECORD_SYSTEM_475 ] = "SYSTEM_475",
+ [ RTEMS_RECORD_SYSTEM_476 ] = "SYSTEM_476",
+ [ RTEMS_RECORD_SYSTEM_477 ] = "SYSTEM_477",
+ [ RTEMS_RECORD_SYSTEM_478 ] = "SYSTEM_478",
+ [ RTEMS_RECORD_SYSTEM_479 ] = "SYSTEM_479",
+ [ RTEMS_RECORD_SYSTEM_480 ] = "SYSTEM_480",
+ [ RTEMS_RECORD_SYSTEM_481 ] = "SYSTEM_481",
+ [ RTEMS_RECORD_SYSTEM_482 ] = "SYSTEM_482",
+ [ RTEMS_RECORD_SYSTEM_483 ] = "SYSTEM_483",
+ [ RTEMS_RECORD_SYSTEM_484 ] = "SYSTEM_484",
+ [ RTEMS_RECORD_SYSTEM_485 ] = "SYSTEM_485",
+ [ RTEMS_RECORD_SYSTEM_486 ] = "SYSTEM_486",
+ [ RTEMS_RECORD_SYSTEM_487 ] = "SYSTEM_487",
+ [ RTEMS_RECORD_SYSTEM_488 ] = "SYSTEM_488",
+ [ RTEMS_RECORD_SYSTEM_489 ] = "SYSTEM_489",
+ [ RTEMS_RECORD_SYSTEM_490 ] = "SYSTEM_490",
+ [ RTEMS_RECORD_SYSTEM_491 ] = "SYSTEM_491",
+ [ RTEMS_RECORD_SYSTEM_492 ] = "SYSTEM_492",
+ [ RTEMS_RECORD_SYSTEM_493 ] = "SYSTEM_493",
+ [ RTEMS_RECORD_SYSTEM_494 ] = "SYSTEM_494",
+ [ RTEMS_RECORD_SYSTEM_495 ] = "SYSTEM_495",
+ [ RTEMS_RECORD_SYSTEM_496 ] = "SYSTEM_496",
+ [ RTEMS_RECORD_SYSTEM_497 ] = "SYSTEM_497",
+ [ RTEMS_RECORD_SYSTEM_498 ] = "SYSTEM_498",
+ [ RTEMS_RECORD_SYSTEM_499 ] = "SYSTEM_499",
+ [ RTEMS_RECORD_SYSTEM_500 ] = "SYSTEM_500",
+ [ RTEMS_RECORD_SYSTEM_501 ] = "SYSTEM_501",
+ [ RTEMS_RECORD_SYSTEM_502 ] = "SYSTEM_502",
+ [ RTEMS_RECORD_SYSTEM_503 ] = "SYSTEM_503",
+ [ RTEMS_RECORD_SYSTEM_504 ] = "SYSTEM_504",
+ [ RTEMS_RECORD_SYSTEM_505 ] = "SYSTEM_505",
+ [ RTEMS_RECORD_SYSTEM_506 ] = "SYSTEM_506",
+ [ RTEMS_RECORD_SYSTEM_507 ] = "SYSTEM_507",
+ [ RTEMS_RECORD_SYSTEM_508 ] = "SYSTEM_508",
+ [ RTEMS_RECORD_SYSTEM_509 ] = "SYSTEM_509",
+ [ RTEMS_RECORD_SYSTEM_510 ] = "SYSTEM_510",
+ [ RTEMS_RECORD_SYSTEM_511 ] = "SYSTEM_511",
+ [ RTEMS_RECORD_USER_0 ] = "USER_0",
+ [ RTEMS_RECORD_USER_1 ] = "USER_1",
+ [ RTEMS_RECORD_USER_2 ] = "USER_2",
+ [ RTEMS_RECORD_USER_3 ] = "USER_3",
+ [ RTEMS_RECORD_USER_4 ] = "USER_4",
+ [ RTEMS_RECORD_USER_5 ] = "USER_5",
+ [ RTEMS_RECORD_USER_6 ] = "USER_6",
+ [ RTEMS_RECORD_USER_7 ] = "USER_7",
+ [ RTEMS_RECORD_USER_8 ] = "USER_8",
+ [ RTEMS_RECORD_USER_9 ] = "USER_9",
+ [ RTEMS_RECORD_USER_10 ] = "USER_10",
+ [ RTEMS_RECORD_USER_11 ] = "USER_11",
+ [ RTEMS_RECORD_USER_12 ] = "USER_12",
+ [ RTEMS_RECORD_USER_13 ] = "USER_13",
+ [ RTEMS_RECORD_USER_14 ] = "USER_14",
+ [ RTEMS_RECORD_USER_15 ] = "USER_15",
+ [ RTEMS_RECORD_USER_16 ] = "USER_16",
+ [ RTEMS_RECORD_USER_17 ] = "USER_17",
+ [ RTEMS_RECORD_USER_18 ] = "USER_18",
+ [ RTEMS_RECORD_USER_19 ] = "USER_19",
+ [ RTEMS_RECORD_USER_20 ] = "USER_20",
+ [ RTEMS_RECORD_USER_21 ] = "USER_21",
+ [ RTEMS_RECORD_USER_22 ] = "USER_22",
+ [ RTEMS_RECORD_USER_23 ] = "USER_23",
+ [ RTEMS_RECORD_USER_24 ] = "USER_24",
+ [ RTEMS_RECORD_USER_25 ] = "USER_25",
+ [ RTEMS_RECORD_USER_26 ] = "USER_26",
+ [ RTEMS_RECORD_USER_27 ] = "USER_27",
+ [ RTEMS_RECORD_USER_28 ] = "USER_28",
+ [ RTEMS_RECORD_USER_29 ] = "USER_29",
+ [ RTEMS_RECORD_USER_30 ] = "USER_30",
+ [ RTEMS_RECORD_USER_31 ] = "USER_31",
+ [ RTEMS_RECORD_USER_32 ] = "USER_32",
+ [ RTEMS_RECORD_USER_33 ] = "USER_33",
+ [ RTEMS_RECORD_USER_34 ] = "USER_34",
+ [ RTEMS_RECORD_USER_35 ] = "USER_35",
+ [ RTEMS_RECORD_USER_36 ] = "USER_36",
+ [ RTEMS_RECORD_USER_37 ] = "USER_37",
+ [ RTEMS_RECORD_USER_38 ] = "USER_38",
+ [ RTEMS_RECORD_USER_39 ] = "USER_39",
+ [ RTEMS_RECORD_USER_40 ] = "USER_40",
+ [ RTEMS_RECORD_USER_41 ] = "USER_41",
+ [ RTEMS_RECORD_USER_42 ] = "USER_42",
+ [ RTEMS_RECORD_USER_43 ] = "USER_43",
+ [ RTEMS_RECORD_USER_44 ] = "USER_44",
+ [ RTEMS_RECORD_USER_45 ] = "USER_45",
+ [ RTEMS_RECORD_USER_46 ] = "USER_46",
+ [ RTEMS_RECORD_USER_47 ] = "USER_47",
+ [ RTEMS_RECORD_USER_48 ] = "USER_48",
+ [ RTEMS_RECORD_USER_49 ] = "USER_49",
+ [ RTEMS_RECORD_USER_50 ] = "USER_50",
+ [ RTEMS_RECORD_USER_51 ] = "USER_51",
+ [ RTEMS_RECORD_USER_52 ] = "USER_52",
+ [ RTEMS_RECORD_USER_53 ] = "USER_53",
+ [ RTEMS_RECORD_USER_54 ] = "USER_54",
+ [ RTEMS_RECORD_USER_55 ] = "USER_55",
+ [ RTEMS_RECORD_USER_56 ] = "USER_56",
+ [ RTEMS_RECORD_USER_57 ] = "USER_57",
+ [ RTEMS_RECORD_USER_58 ] = "USER_58",
+ [ RTEMS_RECORD_USER_59 ] = "USER_59",
+ [ RTEMS_RECORD_USER_60 ] = "USER_60",
+ [ RTEMS_RECORD_USER_61 ] = "USER_61",
+ [ RTEMS_RECORD_USER_62 ] = "USER_62",
+ [ RTEMS_RECORD_USER_63 ] = "USER_63",
+ [ RTEMS_RECORD_USER_64 ] = "USER_64",
+ [ RTEMS_RECORD_USER_65 ] = "USER_65",
+ [ RTEMS_RECORD_USER_66 ] = "USER_66",
+ [ RTEMS_RECORD_USER_67 ] = "USER_67",
+ [ RTEMS_RECORD_USER_68 ] = "USER_68",
+ [ RTEMS_RECORD_USER_69 ] = "USER_69",
+ [ RTEMS_RECORD_USER_70 ] = "USER_70",
+ [ RTEMS_RECORD_USER_71 ] = "USER_71",
+ [ RTEMS_RECORD_USER_72 ] = "USER_72",
+ [ RTEMS_RECORD_USER_73 ] = "USER_73",
+ [ RTEMS_RECORD_USER_74 ] = "USER_74",
+ [ RTEMS_RECORD_USER_75 ] = "USER_75",
+ [ RTEMS_RECORD_USER_76 ] = "USER_76",
+ [ RTEMS_RECORD_USER_77 ] = "USER_77",
+ [ RTEMS_RECORD_USER_78 ] = "USER_78",
+ [ RTEMS_RECORD_USER_79 ] = "USER_79",
+ [ RTEMS_RECORD_USER_80 ] = "USER_80",
+ [ RTEMS_RECORD_USER_81 ] = "USER_81",
+ [ RTEMS_RECORD_USER_82 ] = "USER_82",
+ [ RTEMS_RECORD_USER_83 ] = "USER_83",
+ [ RTEMS_RECORD_USER_84 ] = "USER_84",
+ [ RTEMS_RECORD_USER_85 ] = "USER_85",
+ [ RTEMS_RECORD_USER_86 ] = "USER_86",
+ [ RTEMS_RECORD_USER_87 ] = "USER_87",
+ [ RTEMS_RECORD_USER_88 ] = "USER_88",
+ [ RTEMS_RECORD_USER_89 ] = "USER_89",
+ [ RTEMS_RECORD_USER_90 ] = "USER_90",
+ [ RTEMS_RECORD_USER_91 ] = "USER_91",
+ [ RTEMS_RECORD_USER_92 ] = "USER_92",
+ [ RTEMS_RECORD_USER_93 ] = "USER_93",
+ [ RTEMS_RECORD_USER_94 ] = "USER_94",
+ [ RTEMS_RECORD_USER_95 ] = "USER_95",
+ [ RTEMS_RECORD_USER_96 ] = "USER_96",
+ [ RTEMS_RECORD_USER_97 ] = "USER_97",
+ [ RTEMS_RECORD_USER_98 ] = "USER_98",
+ [ RTEMS_RECORD_USER_99 ] = "USER_99",
+ [ RTEMS_RECORD_USER_100 ] = "USER_100",
+ [ RTEMS_RECORD_USER_101 ] = "USER_101",
+ [ RTEMS_RECORD_USER_102 ] = "USER_102",
+ [ RTEMS_RECORD_USER_103 ] = "USER_103",
+ [ RTEMS_RECORD_USER_104 ] = "USER_104",
+ [ RTEMS_RECORD_USER_105 ] = "USER_105",
+ [ RTEMS_RECORD_USER_106 ] = "USER_106",
+ [ RTEMS_RECORD_USER_107 ] = "USER_107",
+ [ RTEMS_RECORD_USER_108 ] = "USER_108",
+ [ RTEMS_RECORD_USER_109 ] = "USER_109",
+ [ RTEMS_RECORD_USER_110 ] = "USER_110",
+ [ RTEMS_RECORD_USER_111 ] = "USER_111",
+ [ RTEMS_RECORD_USER_112 ] = "USER_112",
+ [ RTEMS_RECORD_USER_113 ] = "USER_113",
+ [ RTEMS_RECORD_USER_114 ] = "USER_114",
+ [ RTEMS_RECORD_USER_115 ] = "USER_115",
+ [ RTEMS_RECORD_USER_116 ] = "USER_116",
+ [ RTEMS_RECORD_USER_117 ] = "USER_117",
+ [ RTEMS_RECORD_USER_118 ] = "USER_118",
+ [ RTEMS_RECORD_USER_119 ] = "USER_119",
+ [ RTEMS_RECORD_USER_120 ] = "USER_120",
+ [ RTEMS_RECORD_USER_121 ] = "USER_121",
+ [ RTEMS_RECORD_USER_122 ] = "USER_122",
+ [ RTEMS_RECORD_USER_123 ] = "USER_123",
+ [ RTEMS_RECORD_USER_124 ] = "USER_124",
+ [ RTEMS_RECORD_USER_125 ] = "USER_125",
+ [ RTEMS_RECORD_USER_126 ] = "USER_126",
+ [ RTEMS_RECORD_USER_127 ] = "USER_127",
+ [ RTEMS_RECORD_USER_128 ] = "USER_128",
+ [ RTEMS_RECORD_USER_129 ] = "USER_129",
+ [ RTEMS_RECORD_USER_130 ] = "USER_130",
+ [ RTEMS_RECORD_USER_131 ] = "USER_131",
+ [ RTEMS_RECORD_USER_132 ] = "USER_132",
+ [ RTEMS_RECORD_USER_133 ] = "USER_133",
+ [ RTEMS_RECORD_USER_134 ] = "USER_134",
+ [ RTEMS_RECORD_USER_135 ] = "USER_135",
+ [ RTEMS_RECORD_USER_136 ] = "USER_136",
+ [ RTEMS_RECORD_USER_137 ] = "USER_137",
+ [ RTEMS_RECORD_USER_138 ] = "USER_138",
+ [ RTEMS_RECORD_USER_139 ] = "USER_139",
+ [ RTEMS_RECORD_USER_140 ] = "USER_140",
+ [ RTEMS_RECORD_USER_141 ] = "USER_141",
+ [ RTEMS_RECORD_USER_142 ] = "USER_142",
+ [ RTEMS_RECORD_USER_143 ] = "USER_143",
+ [ RTEMS_RECORD_USER_144 ] = "USER_144",
+ [ RTEMS_RECORD_USER_145 ] = "USER_145",
+ [ RTEMS_RECORD_USER_146 ] = "USER_146",
+ [ RTEMS_RECORD_USER_147 ] = "USER_147",
+ [ RTEMS_RECORD_USER_148 ] = "USER_148",
+ [ RTEMS_RECORD_USER_149 ] = "USER_149",
+ [ RTEMS_RECORD_USER_150 ] = "USER_150",
+ [ RTEMS_RECORD_USER_151 ] = "USER_151",
+ [ RTEMS_RECORD_USER_152 ] = "USER_152",
+ [ RTEMS_RECORD_USER_153 ] = "USER_153",
+ [ RTEMS_RECORD_USER_154 ] = "USER_154",
+ [ RTEMS_RECORD_USER_155 ] = "USER_155",
+ [ RTEMS_RECORD_USER_156 ] = "USER_156",
+ [ RTEMS_RECORD_USER_157 ] = "USER_157",
+ [ RTEMS_RECORD_USER_158 ] = "USER_158",
+ [ RTEMS_RECORD_USER_159 ] = "USER_159",
+ [ RTEMS_RECORD_USER_160 ] = "USER_160",
+ [ RTEMS_RECORD_USER_161 ] = "USER_161",
+ [ RTEMS_RECORD_USER_162 ] = "USER_162",
+ [ RTEMS_RECORD_USER_163 ] = "USER_163",
+ [ RTEMS_RECORD_USER_164 ] = "USER_164",
+ [ RTEMS_RECORD_USER_165 ] = "USER_165",
+ [ RTEMS_RECORD_USER_166 ] = "USER_166",
+ [ RTEMS_RECORD_USER_167 ] = "USER_167",
+ [ RTEMS_RECORD_USER_168 ] = "USER_168",
+ [ RTEMS_RECORD_USER_169 ] = "USER_169",
+ [ RTEMS_RECORD_USER_170 ] = "USER_170",
+ [ RTEMS_RECORD_USER_171 ] = "USER_171",
+ [ RTEMS_RECORD_USER_172 ] = "USER_172",
+ [ RTEMS_RECORD_USER_173 ] = "USER_173",
+ [ RTEMS_RECORD_USER_174 ] = "USER_174",
+ [ RTEMS_RECORD_USER_175 ] = "USER_175",
+ [ RTEMS_RECORD_USER_176 ] = "USER_176",
+ [ RTEMS_RECORD_USER_177 ] = "USER_177",
+ [ RTEMS_RECORD_USER_178 ] = "USER_178",
+ [ RTEMS_RECORD_USER_179 ] = "USER_179",
+ [ RTEMS_RECORD_USER_180 ] = "USER_180",
+ [ RTEMS_RECORD_USER_181 ] = "USER_181",
+ [ RTEMS_RECORD_USER_182 ] = "USER_182",
+ [ RTEMS_RECORD_USER_183 ] = "USER_183",
+ [ RTEMS_RECORD_USER_184 ] = "USER_184",
+ [ RTEMS_RECORD_USER_185 ] = "USER_185",
+ [ RTEMS_RECORD_USER_186 ] = "USER_186",
+ [ RTEMS_RECORD_USER_187 ] = "USER_187",
+ [ RTEMS_RECORD_USER_188 ] = "USER_188",
+ [ RTEMS_RECORD_USER_189 ] = "USER_189",
+ [ RTEMS_RECORD_USER_190 ] = "USER_190",
+ [ RTEMS_RECORD_USER_191 ] = "USER_191",
+ [ RTEMS_RECORD_USER_192 ] = "USER_192",
+ [ RTEMS_RECORD_USER_193 ] = "USER_193",
+ [ RTEMS_RECORD_USER_194 ] = "USER_194",
+ [ RTEMS_RECORD_USER_195 ] = "USER_195",
+ [ RTEMS_RECORD_USER_196 ] = "USER_196",
+ [ RTEMS_RECORD_USER_197 ] = "USER_197",
+ [ RTEMS_RECORD_USER_198 ] = "USER_198",
+ [ RTEMS_RECORD_USER_199 ] = "USER_199",
+ [ RTEMS_RECORD_USER_200 ] = "USER_200",
+ [ RTEMS_RECORD_USER_201 ] = "USER_201",
+ [ RTEMS_RECORD_USER_202 ] = "USER_202",
+ [ RTEMS_RECORD_USER_203 ] = "USER_203",
+ [ RTEMS_RECORD_USER_204 ] = "USER_204",
+ [ RTEMS_RECORD_USER_205 ] = "USER_205",
+ [ RTEMS_RECORD_USER_206 ] = "USER_206",
+ [ RTEMS_RECORD_USER_207 ] = "USER_207",
+ [ RTEMS_RECORD_USER_208 ] = "USER_208",
+ [ RTEMS_RECORD_USER_209 ] = "USER_209",
+ [ RTEMS_RECORD_USER_210 ] = "USER_210",
+ [ RTEMS_RECORD_USER_211 ] = "USER_211",
+ [ RTEMS_RECORD_USER_212 ] = "USER_212",
+ [ RTEMS_RECORD_USER_213 ] = "USER_213",
+ [ RTEMS_RECORD_USER_214 ] = "USER_214",
+ [ RTEMS_RECORD_USER_215 ] = "USER_215",
+ [ RTEMS_RECORD_USER_216 ] = "USER_216",
+ [ RTEMS_RECORD_USER_217 ] = "USER_217",
+ [ RTEMS_RECORD_USER_218 ] = "USER_218",
+ [ RTEMS_RECORD_USER_219 ] = "USER_219",
+ [ RTEMS_RECORD_USER_220 ] = "USER_220",
+ [ RTEMS_RECORD_USER_221 ] = "USER_221",
+ [ RTEMS_RECORD_USER_222 ] = "USER_222",
+ [ RTEMS_RECORD_USER_223 ] = "USER_223",
+ [ RTEMS_RECORD_USER_224 ] = "USER_224",
+ [ RTEMS_RECORD_USER_225 ] = "USER_225",
+ [ RTEMS_RECORD_USER_226 ] = "USER_226",
+ [ RTEMS_RECORD_USER_227 ] = "USER_227",
+ [ RTEMS_RECORD_USER_228 ] = "USER_228",
+ [ RTEMS_RECORD_USER_229 ] = "USER_229",
+ [ RTEMS_RECORD_USER_230 ] = "USER_230",
+ [ RTEMS_RECORD_USER_231 ] = "USER_231",
+ [ RTEMS_RECORD_USER_232 ] = "USER_232",
+ [ RTEMS_RECORD_USER_233 ] = "USER_233",
+ [ RTEMS_RECORD_USER_234 ] = "USER_234",
+ [ RTEMS_RECORD_USER_235 ] = "USER_235",
+ [ RTEMS_RECORD_USER_236 ] = "USER_236",
+ [ RTEMS_RECORD_USER_237 ] = "USER_237",
+ [ RTEMS_RECORD_USER_238 ] = "USER_238",
+ [ RTEMS_RECORD_USER_239 ] = "USER_239",
+ [ RTEMS_RECORD_USER_240 ] = "USER_240",
+ [ RTEMS_RECORD_USER_241 ] = "USER_241",
+ [ RTEMS_RECORD_USER_242 ] = "USER_242",
+ [ RTEMS_RECORD_USER_243 ] = "USER_243",
+ [ RTEMS_RECORD_USER_244 ] = "USER_244",
+ [ RTEMS_RECORD_USER_245 ] = "USER_245",
+ [ RTEMS_RECORD_USER_246 ] = "USER_246",
+ [ RTEMS_RECORD_USER_247 ] = "USER_247",
+ [ RTEMS_RECORD_USER_248 ] = "USER_248",
+ [ RTEMS_RECORD_USER_249 ] = "USER_249",
+ [ RTEMS_RECORD_USER_250 ] = "USER_250",
+ [ RTEMS_RECORD_USER_251 ] = "USER_251",
+ [ RTEMS_RECORD_USER_252 ] = "USER_252",
+ [ RTEMS_RECORD_USER_253 ] = "USER_253",
+ [ RTEMS_RECORD_USER_254 ] = "USER_254",
+ [ RTEMS_RECORD_USER_255 ] = "USER_255",
+ [ RTEMS_RECORD_USER_256 ] = "USER_256",
+ [ RTEMS_RECORD_USER_257 ] = "USER_257",
+ [ RTEMS_RECORD_USER_258 ] = "USER_258",
+ [ RTEMS_RECORD_USER_259 ] = "USER_259",
+ [ RTEMS_RECORD_USER_260 ] = "USER_260",
+ [ RTEMS_RECORD_USER_261 ] = "USER_261",
+ [ RTEMS_RECORD_USER_262 ] = "USER_262",
+ [ RTEMS_RECORD_USER_263 ] = "USER_263",
+ [ RTEMS_RECORD_USER_264 ] = "USER_264",
+ [ RTEMS_RECORD_USER_265 ] = "USER_265",
+ [ RTEMS_RECORD_USER_266 ] = "USER_266",
+ [ RTEMS_RECORD_USER_267 ] = "USER_267",
+ [ RTEMS_RECORD_USER_268 ] = "USER_268",
+ [ RTEMS_RECORD_USER_269 ] = "USER_269",
+ [ RTEMS_RECORD_USER_270 ] = "USER_270",
+ [ RTEMS_RECORD_USER_271 ] = "USER_271",
+ [ RTEMS_RECORD_USER_272 ] = "USER_272",
+ [ RTEMS_RECORD_USER_273 ] = "USER_273",
+ [ RTEMS_RECORD_USER_274 ] = "USER_274",
+ [ RTEMS_RECORD_USER_275 ] = "USER_275",
+ [ RTEMS_RECORD_USER_276 ] = "USER_276",
+ [ RTEMS_RECORD_USER_277 ] = "USER_277",
+ [ RTEMS_RECORD_USER_278 ] = "USER_278",
+ [ RTEMS_RECORD_USER_279 ] = "USER_279",
+ [ RTEMS_RECORD_USER_280 ] = "USER_280",
+ [ RTEMS_RECORD_USER_281 ] = "USER_281",
+ [ RTEMS_RECORD_USER_282 ] = "USER_282",
+ [ RTEMS_RECORD_USER_283 ] = "USER_283",
+ [ RTEMS_RECORD_USER_284 ] = "USER_284",
+ [ RTEMS_RECORD_USER_285 ] = "USER_285",
+ [ RTEMS_RECORD_USER_286 ] = "USER_286",
+ [ RTEMS_RECORD_USER_287 ] = "USER_287",
+ [ RTEMS_RECORD_USER_288 ] = "USER_288",
+ [ RTEMS_RECORD_USER_289 ] = "USER_289",
+ [ RTEMS_RECORD_USER_290 ] = "USER_290",
+ [ RTEMS_RECORD_USER_291 ] = "USER_291",
+ [ RTEMS_RECORD_USER_292 ] = "USER_292",
+ [ RTEMS_RECORD_USER_293 ] = "USER_293",
+ [ RTEMS_RECORD_USER_294 ] = "USER_294",
+ [ RTEMS_RECORD_USER_295 ] = "USER_295",
+ [ RTEMS_RECORD_USER_296 ] = "USER_296",
+ [ RTEMS_RECORD_USER_297 ] = "USER_297",
+ [ RTEMS_RECORD_USER_298 ] = "USER_298",
+ [ RTEMS_RECORD_USER_299 ] = "USER_299",
+ [ RTEMS_RECORD_USER_300 ] = "USER_300",
+ [ RTEMS_RECORD_USER_301 ] = "USER_301",
+ [ RTEMS_RECORD_USER_302 ] = "USER_302",
+ [ RTEMS_RECORD_USER_303 ] = "USER_303",
+ [ RTEMS_RECORD_USER_304 ] = "USER_304",
+ [ RTEMS_RECORD_USER_305 ] = "USER_305",
+ [ RTEMS_RECORD_USER_306 ] = "USER_306",
+ [ RTEMS_RECORD_USER_307 ] = "USER_307",
+ [ RTEMS_RECORD_USER_308 ] = "USER_308",
+ [ RTEMS_RECORD_USER_309 ] = "USER_309",
+ [ RTEMS_RECORD_USER_310 ] = "USER_310",
+ [ RTEMS_RECORD_USER_311 ] = "USER_311",
+ [ RTEMS_RECORD_USER_312 ] = "USER_312",
+ [ RTEMS_RECORD_USER_313 ] = "USER_313",
+ [ RTEMS_RECORD_USER_314 ] = "USER_314",
+ [ RTEMS_RECORD_USER_315 ] = "USER_315",
+ [ RTEMS_RECORD_USER_316 ] = "USER_316",
+ [ RTEMS_RECORD_USER_317 ] = "USER_317",
+ [ RTEMS_RECORD_USER_318 ] = "USER_318",
+ [ RTEMS_RECORD_USER_319 ] = "USER_319",
+ [ RTEMS_RECORD_USER_320 ] = "USER_320",
+ [ RTEMS_RECORD_USER_321 ] = "USER_321",
+ [ RTEMS_RECORD_USER_322 ] = "USER_322",
+ [ RTEMS_RECORD_USER_323 ] = "USER_323",
+ [ RTEMS_RECORD_USER_324 ] = "USER_324",
+ [ RTEMS_RECORD_USER_325 ] = "USER_325",
+ [ RTEMS_RECORD_USER_326 ] = "USER_326",
+ [ RTEMS_RECORD_USER_327 ] = "USER_327",
+ [ RTEMS_RECORD_USER_328 ] = "USER_328",
+ [ RTEMS_RECORD_USER_329 ] = "USER_329",
+ [ RTEMS_RECORD_USER_330 ] = "USER_330",
+ [ RTEMS_RECORD_USER_331 ] = "USER_331",
+ [ RTEMS_RECORD_USER_332 ] = "USER_332",
+ [ RTEMS_RECORD_USER_333 ] = "USER_333",
+ [ RTEMS_RECORD_USER_334 ] = "USER_334",
+ [ RTEMS_RECORD_USER_335 ] = "USER_335",
+ [ RTEMS_RECORD_USER_336 ] = "USER_336",
+ [ RTEMS_RECORD_USER_337 ] = "USER_337",
+ [ RTEMS_RECORD_USER_338 ] = "USER_338",
+ [ RTEMS_RECORD_USER_339 ] = "USER_339",
+ [ RTEMS_RECORD_USER_340 ] = "USER_340",
+ [ RTEMS_RECORD_USER_341 ] = "USER_341",
+ [ RTEMS_RECORD_USER_342 ] = "USER_342",
+ [ RTEMS_RECORD_USER_343 ] = "USER_343",
+ [ RTEMS_RECORD_USER_344 ] = "USER_344",
+ [ RTEMS_RECORD_USER_345 ] = "USER_345",
+ [ RTEMS_RECORD_USER_346 ] = "USER_346",
+ [ RTEMS_RECORD_USER_347 ] = "USER_347",
+ [ RTEMS_RECORD_USER_348 ] = "USER_348",
+ [ RTEMS_RECORD_USER_349 ] = "USER_349",
+ [ RTEMS_RECORD_USER_350 ] = "USER_350",
+ [ RTEMS_RECORD_USER_351 ] = "USER_351",
+ [ RTEMS_RECORD_USER_352 ] = "USER_352",
+ [ RTEMS_RECORD_USER_353 ] = "USER_353",
+ [ RTEMS_RECORD_USER_354 ] = "USER_354",
+ [ RTEMS_RECORD_USER_355 ] = "USER_355",
+ [ RTEMS_RECORD_USER_356 ] = "USER_356",
+ [ RTEMS_RECORD_USER_357 ] = "USER_357",
+ [ RTEMS_RECORD_USER_358 ] = "USER_358",
+ [ RTEMS_RECORD_USER_359 ] = "USER_359",
+ [ RTEMS_RECORD_USER_360 ] = "USER_360",
+ [ RTEMS_RECORD_USER_361 ] = "USER_361",
+ [ RTEMS_RECORD_USER_362 ] = "USER_362",
+ [ RTEMS_RECORD_USER_363 ] = "USER_363",
+ [ RTEMS_RECORD_USER_364 ] = "USER_364",
+ [ RTEMS_RECORD_USER_365 ] = "USER_365",
+ [ RTEMS_RECORD_USER_366 ] = "USER_366",
+ [ RTEMS_RECORD_USER_367 ] = "USER_367",
+ [ RTEMS_RECORD_USER_368 ] = "USER_368",
+ [ RTEMS_RECORD_USER_369 ] = "USER_369",
+ [ RTEMS_RECORD_USER_370 ] = "USER_370",
+ [ RTEMS_RECORD_USER_371 ] = "USER_371",
+ [ RTEMS_RECORD_USER_372 ] = "USER_372",
+ [ RTEMS_RECORD_USER_373 ] = "USER_373",
+ [ RTEMS_RECORD_USER_374 ] = "USER_374",
+ [ RTEMS_RECORD_USER_375 ] = "USER_375",
+ [ RTEMS_RECORD_USER_376 ] = "USER_376",
+ [ RTEMS_RECORD_USER_377 ] = "USER_377",
+ [ RTEMS_RECORD_USER_378 ] = "USER_378",
+ [ RTEMS_RECORD_USER_379 ] = "USER_379",
+ [ RTEMS_RECORD_USER_380 ] = "USER_380",
+ [ RTEMS_RECORD_USER_381 ] = "USER_381",
+ [ RTEMS_RECORD_USER_382 ] = "USER_382",
+ [ RTEMS_RECORD_USER_383 ] = "USER_383",
+ [ RTEMS_RECORD_USER_384 ] = "USER_384",
+ [ RTEMS_RECORD_USER_385 ] = "USER_385",
+ [ RTEMS_RECORD_USER_386 ] = "USER_386",
+ [ RTEMS_RECORD_USER_387 ] = "USER_387",
+ [ RTEMS_RECORD_USER_388 ] = "USER_388",
+ [ RTEMS_RECORD_USER_389 ] = "USER_389",
+ [ RTEMS_RECORD_USER_390 ] = "USER_390",
+ [ RTEMS_RECORD_USER_391 ] = "USER_391",
+ [ RTEMS_RECORD_USER_392 ] = "USER_392",
+ [ RTEMS_RECORD_USER_393 ] = "USER_393",
+ [ RTEMS_RECORD_USER_394 ] = "USER_394",
+ [ RTEMS_RECORD_USER_395 ] = "USER_395",
+ [ RTEMS_RECORD_USER_396 ] = "USER_396",
+ [ RTEMS_RECORD_USER_397 ] = "USER_397",
+ [ RTEMS_RECORD_USER_398 ] = "USER_398",
+ [ RTEMS_RECORD_USER_399 ] = "USER_399",
+ [ RTEMS_RECORD_USER_400 ] = "USER_400",
+ [ RTEMS_RECORD_USER_401 ] = "USER_401",
+ [ RTEMS_RECORD_USER_402 ] = "USER_402",
+ [ RTEMS_RECORD_USER_403 ] = "USER_403",
+ [ RTEMS_RECORD_USER_404 ] = "USER_404",
+ [ RTEMS_RECORD_USER_405 ] = "USER_405",
+ [ RTEMS_RECORD_USER_406 ] = "USER_406",
+ [ RTEMS_RECORD_USER_407 ] = "USER_407",
+ [ RTEMS_RECORD_USER_408 ] = "USER_408",
+ [ RTEMS_RECORD_USER_409 ] = "USER_409",
+ [ RTEMS_RECORD_USER_410 ] = "USER_410",
+ [ RTEMS_RECORD_USER_411 ] = "USER_411",
+ [ RTEMS_RECORD_USER_412 ] = "USER_412",
+ [ RTEMS_RECORD_USER_413 ] = "USER_413",
+ [ RTEMS_RECORD_USER_414 ] = "USER_414",
+ [ RTEMS_RECORD_USER_415 ] = "USER_415",
+ [ RTEMS_RECORD_USER_416 ] = "USER_416",
+ [ RTEMS_RECORD_USER_417 ] = "USER_417",
+ [ RTEMS_RECORD_USER_418 ] = "USER_418",
+ [ RTEMS_RECORD_USER_419 ] = "USER_419",
+ [ RTEMS_RECORD_USER_420 ] = "USER_420",
+ [ RTEMS_RECORD_USER_421 ] = "USER_421",
+ [ RTEMS_RECORD_USER_422 ] = "USER_422",
+ [ RTEMS_RECORD_USER_423 ] = "USER_423",
+ [ RTEMS_RECORD_USER_424 ] = "USER_424",
+ [ RTEMS_RECORD_USER_425 ] = "USER_425",
+ [ RTEMS_RECORD_USER_426 ] = "USER_426",
+ [ RTEMS_RECORD_USER_427 ] = "USER_427",
+ [ RTEMS_RECORD_USER_428 ] = "USER_428",
+ [ RTEMS_RECORD_USER_429 ] = "USER_429",
+ [ RTEMS_RECORD_USER_430 ] = "USER_430",
+ [ RTEMS_RECORD_USER_431 ] = "USER_431",
+ [ RTEMS_RECORD_USER_432 ] = "USER_432",
+ [ RTEMS_RECORD_USER_433 ] = "USER_433",
+ [ RTEMS_RECORD_USER_434 ] = "USER_434",
+ [ RTEMS_RECORD_USER_435 ] = "USER_435",
+ [ RTEMS_RECORD_USER_436 ] = "USER_436",
+ [ RTEMS_RECORD_USER_437 ] = "USER_437",
+ [ RTEMS_RECORD_USER_438 ] = "USER_438",
+ [ RTEMS_RECORD_USER_439 ] = "USER_439",
+ [ RTEMS_RECORD_USER_440 ] = "USER_440",
+ [ RTEMS_RECORD_USER_441 ] = "USER_441",
+ [ RTEMS_RECORD_USER_442 ] = "USER_442",
+ [ RTEMS_RECORD_USER_443 ] = "USER_443",
+ [ RTEMS_RECORD_USER_444 ] = "USER_444",
+ [ RTEMS_RECORD_USER_445 ] = "USER_445",
+ [ RTEMS_RECORD_USER_446 ] = "USER_446",
+ [ RTEMS_RECORD_USER_447 ] = "USER_447",
+ [ RTEMS_RECORD_USER_448 ] = "USER_448",
+ [ RTEMS_RECORD_USER_449 ] = "USER_449",
+ [ RTEMS_RECORD_USER_450 ] = "USER_450",
+ [ RTEMS_RECORD_USER_451 ] = "USER_451",
+ [ RTEMS_RECORD_USER_452 ] = "USER_452",
+ [ RTEMS_RECORD_USER_453 ] = "USER_453",
+ [ RTEMS_RECORD_USER_454 ] = "USER_454",
+ [ RTEMS_RECORD_USER_455 ] = "USER_455",
+ [ RTEMS_RECORD_USER_456 ] = "USER_456",
+ [ RTEMS_RECORD_USER_457 ] = "USER_457",
+ [ RTEMS_RECORD_USER_458 ] = "USER_458",
+ [ RTEMS_RECORD_USER_459 ] = "USER_459",
+ [ RTEMS_RECORD_USER_460 ] = "USER_460",
+ [ RTEMS_RECORD_USER_461 ] = "USER_461",
+ [ RTEMS_RECORD_USER_462 ] = "USER_462",
+ [ RTEMS_RECORD_USER_463 ] = "USER_463",
+ [ RTEMS_RECORD_USER_464 ] = "USER_464",
+ [ RTEMS_RECORD_USER_465 ] = "USER_465",
+ [ RTEMS_RECORD_USER_466 ] = "USER_466",
+ [ RTEMS_RECORD_USER_467 ] = "USER_467",
+ [ RTEMS_RECORD_USER_468 ] = "USER_468",
+ [ RTEMS_RECORD_USER_469 ] = "USER_469",
+ [ RTEMS_RECORD_USER_470 ] = "USER_470",
+ [ RTEMS_RECORD_USER_471 ] = "USER_471",
+ [ RTEMS_RECORD_USER_472 ] = "USER_472",
+ [ RTEMS_RECORD_USER_473 ] = "USER_473",
+ [ RTEMS_RECORD_USER_474 ] = "USER_474",
+ [ RTEMS_RECORD_USER_475 ] = "USER_475",
+ [ RTEMS_RECORD_USER_476 ] = "USER_476",
+ [ RTEMS_RECORD_USER_477 ] = "USER_477",
+ [ RTEMS_RECORD_USER_478 ] = "USER_478",
+ [ RTEMS_RECORD_USER_479 ] = "USER_479",
+ [ RTEMS_RECORD_USER_480 ] = "USER_480",
+ [ RTEMS_RECORD_USER_481 ] = "USER_481",
+ [ RTEMS_RECORD_USER_482 ] = "USER_482",
+ [ RTEMS_RECORD_USER_483 ] = "USER_483",
+ [ RTEMS_RECORD_USER_484 ] = "USER_484",
+ [ RTEMS_RECORD_USER_485 ] = "USER_485",
+ [ RTEMS_RECORD_USER_486 ] = "USER_486",
+ [ RTEMS_RECORD_USER_487 ] = "USER_487",
+ [ RTEMS_RECORD_USER_488 ] = "USER_488",
+ [ RTEMS_RECORD_USER_489 ] = "USER_489",
+ [ RTEMS_RECORD_USER_490 ] = "USER_490",
+ [ RTEMS_RECORD_USER_491 ] = "USER_491",
+ [ RTEMS_RECORD_USER_492 ] = "USER_492",
+ [ RTEMS_RECORD_USER_493 ] = "USER_493",
+ [ RTEMS_RECORD_USER_494 ] = "USER_494",
+ [ RTEMS_RECORD_USER_495 ] = "USER_495",
+ [ RTEMS_RECORD_USER_496 ] = "USER_496",
+ [ RTEMS_RECORD_USER_497 ] = "USER_497",
+ [ RTEMS_RECORD_USER_498 ] = "USER_498",
+ [ RTEMS_RECORD_USER_499 ] = "USER_499",
+ [ RTEMS_RECORD_USER_500 ] = "USER_500",
+ [ RTEMS_RECORD_USER_501 ] = "USER_501",
+ [ RTEMS_RECORD_USER_502 ] = "USER_502",
+ [ RTEMS_RECORD_USER_503 ] = "USER_503",
+ [ RTEMS_RECORD_USER_504 ] = "USER_504",
+ [ RTEMS_RECORD_USER_505 ] = "USER_505",
+ [ RTEMS_RECORD_USER_506 ] = "USER_506",
+ [ RTEMS_RECORD_USER_507 ] = "USER_507",
+ [ RTEMS_RECORD_USER_508 ] = "USER_508",
+ [ RTEMS_RECORD_USER_509 ] = "USER_509",
+ [ RTEMS_RECORD_USER_510 ] = "USER_510",
+ [ RTEMS_RECORD_USER_511 ] = "USER_511"
+};
+
+const char *rtems_record_event_text( rtems_record_event event )
+{
+ return event_text[ event ];
+}
diff --git a/trace/record/rtems/recordclient.h b/trace/record/rtems/recordclient.h
new file mode 100644
index 0000000..cb1e704
--- /dev/null
+++ b/trace/record/rtems/recordclient.h
@@ -0,0 +1,245 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018, 2019 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD. It may be used for utility programs.
+ */
+
+#ifndef _RTEMS_RECORDCLIENT_H
+#define _RTEMS_RECORDCLIENT_H
+
+#include "recorddata.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @addtogroup RTEMSRecord
+ *
+ * @{
+ */
+
+#define RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT 32
+
+typedef enum {
+ RTEMS_RECORD_CLIENT_SUCCESS,
+ RTEMS_RECORD_CLIENT_ERROR_INVALID_MAGIC,
+ RTEMS_RECORD_CLIENT_ERROR_UNKNOWN_FORMAT,
+ RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_VERSION,
+ RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU,
+ RTEMS_RECORD_CLIENT_ERROR_UNSUPPORTED_CPU_MAX,
+ RTEMS_RECORD_CLIENT_ERROR_DOUBLE_CPU_MAX,
+ RTEMS_RECORD_CLIENT_ERROR_DOUBLE_PER_CPU_COUNT,
+ RTEMS_RECORD_CLIENT_ERROR_NO_CPU_MAX,
+ RTEMS_RECORD_CLIENT_ERROR_NO_MEMORY,
+ RTEMS_RECORD_CLIENT_ERROR_PER_CPU_ITEMS_OVERFLOW
+} rtems_record_client_status;
+
+typedef rtems_record_client_status ( *rtems_record_client_handler )(
+ uint64_t bt,
+ uint32_t cpu,
+ rtems_record_event event,
+ uint64_t data,
+ void *arg
+);
+
+typedef struct {
+ /**
+ * @brief Event time to uptime maintenance.
+ */
+ struct {
+ uint64_t bt;
+ uint32_t time_at_bt;
+ uint32_t time_last;
+ uint32_t time_accumulated;
+ } uptime;
+
+ /**
+ * @brief The current or previous ring buffer tail.
+ *
+ * Indexed by the tail_head_index member.
+ */
+ uint32_t tail[ 2 ];
+
+ /**
+ * @brief The current or previous ring buffer head.
+ *
+ * Indexed by the tail_head_index member.
+ */
+ uint32_t head[ 2 ];
+
+ /**
+ * @brief The index of the tail and head members.
+ *
+ * This index is used to maintain the current and previous tail/head
+ * positions to detect ring buffer overflows.
+ */
+ size_t tail_head_index;
+
+ /**
+ * @brief Count of lost items due to ring buffer overflows.
+ */
+ uint32_t overflow;
+
+ /**
+ * @brief If true, then hold back items for overflow or initial ramp up
+ * processing.
+ */
+ bool hold_back;
+
+ /**
+ * @brief Storage for hold back items.
+ *
+ * In case of a ring buffer overflow, the rtems_record_drain() will push the
+ * complete ring buffer content to the client. While the items are processed
+ * by the client, new items may overwrite some items being processed. The
+ * overwritten items can be detected in the following iteration once the next
+ * tail/head information is pushed to the client.
+ *
+ * In case of the initial ramp up, the items are stored in the hold back
+ * buffer to determine the uptime of the first event.
+ */
+ rtems_record_item_64 *items;
+
+ /**
+ * @brief The index for the next hold back item.
+ */
+ size_t item_index;
+} rtems_record_client_per_cpu;
+
+typedef struct rtems_record_client_context {
+ uint64_t to_bt_scaler;
+ rtems_record_client_per_cpu per_cpu[ RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT ];
+ uint32_t cpu;
+ uint32_t cpu_count;
+ uint32_t count;
+ union {
+ rtems_record_item_32 format_32;
+ rtems_record_item_64 format_64;
+ } item;
+ size_t todo;
+ void *pos;
+ rtems_record_client_status ( *consume )(
+ struct rtems_record_client_context *,
+ const void *,
+ size_t
+ );
+ rtems_record_client_handler handler;
+ void *handler_arg;
+ size_t data_size;
+ uint32_t header[ 2 ];
+ rtems_record_client_status status;
+} rtems_record_client_context;
+
+/**
+ * @brief Initializes a record client.
+ *
+ * The record client consumes a record item stream produces by the record
+ * server.
+ *
+ * @param ctx The record client context to initialize.
+ * @param handler The handler is invoked for each received record item.
+ * @param arg The handler argument.
+ */
+void rtems_record_client_init(
+ rtems_record_client_context *ctx,
+ rtems_record_client_handler handler,
+ void *arg
+);
+
+/**
+ * @brief Runs the record client to consume new stream data.
+ *
+ * @param ctx The record client context.
+ * @param buf The buffer with new stream data.
+ * @param n The size of the buffer.
+ */
+rtems_record_client_status rtems_record_client_run(
+ rtems_record_client_context *ctx,
+ const void *buf,
+ size_t n
+);
+
+/**
+ * @brief Drains all internal buffers and frees the allocated resources.
+ *
+ * The client context must not be used afterwards. It can be re-initialized
+ * via rtems_record_client_init().
+ *
+ * @param ctx The record client context.
+ */
+void rtems_record_client_destroy(
+ rtems_record_client_context *ctx
+);
+
+static inline void rtems_record_client_set_handler(
+ rtems_record_client_context *ctx,
+ rtems_record_client_handler handler
+)
+{
+ ctx->handler = handler;
+}
+
+static inline uint64_t rtems_record_client_bintime_to_nanoseconds(
+ uint64_t bt
+)
+{
+ uint64_t ns_per_sec;
+ uint64_t nanoseconds;
+
+ ns_per_sec = 1000000000ULL;
+ nanoseconds = ns_per_sec * ( (uint32_t) ( bt >> 32 ) );
+ nanoseconds += ( ns_per_sec * (uint32_t) bt ) >> 32;
+
+ return nanoseconds;
+}
+
+static inline void rtems_record_client_bintime_to_seconds_and_nanoseconds(
+ uint64_t bt,
+ uint32_t *seconds,
+ uint32_t *nanoseconds
+)
+{
+ uint64_t ns_per_sec;
+
+ ns_per_sec = 1000000000ULL;
+ *seconds = (uint32_t) ( bt >> 32 );
+ *nanoseconds = (uint32_t) ( ( ns_per_sec * (uint32_t) bt ) >> 32 );
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_RECORDCLIENT_H */
diff --git a/trace/record/rtems/recorddata.h b/trace/record/rtems/recorddata.h
new file mode 100644
index 0000000..4fa16d6
--- /dev/null
+++ b/trace/record/rtems/recorddata.h
@@ -0,0 +1,1204 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018, 2019 embedded brains GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file must be compatible to general purpose POSIX system, e.g. Linux,
+ * FreeBSD. It may be used for utility programs.
+ */
+
+#ifndef _RTEMS_RECORDDATA_H
+#define _RTEMS_RECORDDATA_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup RTEMSRecord Event Recording
+ *
+ * @ingroup RTEMSAPITracing
+ *
+ * @brief Low-level event recording support.
+ *
+ * @{
+ */
+
+/**
+ * @brief The record version.
+ *
+ * The record version reflects the record event definitions. It is reported by
+ * the RTEMS_RECORD_VERSION event.
+ */
+#define RTEMS_RECORD_THE_VERSION 9
+
+/**
+ * @brief The items are in 32-bit little-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_LE_32 0x11111111
+
+/**
+ * @brief The items are in 64-bit little-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_LE_64 0x22222222
+
+/**
+ * @brief The items are in 32-bit big-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_BE_32 0x33333333
+
+/**
+ * @brief The items are in 64-bit big-endian format.
+ */
+#define RTEMS_RECORD_FORMAT_BE_64 0x44444444
+
+/**
+ * @brief Magic number to identify a record item stream.
+ *
+ * This is a random number.
+ */
+#define RTEMS_RECORD_MAGIC 0x82e14ec1
+
+/**
+ * @brief The record events.
+ */
+typedef enum {
+ /* There are 512 events reserved for the system */
+ RTEMS_RECORD_EMPTY,
+ RTEMS_RECORD_VERSION,
+
+ /*
+ * Keep the following system events in lexicographical order, increment
+ * RTEMS_RECORD_THE_VERSION after each change.
+ */
+ RTEMS_RECORD_ACCEPT_ENTRY,
+ RTEMS_RECORD_ACCEPT_EXIT,
+ RTEMS_RECORD_ADDRESS,
+ RTEMS_RECORD_ALIGNED_ALLOC_ENTRY,
+ RTEMS_RECORD_ALIGNED_ALLOC_EXIT,
+ RTEMS_RECORD_ARCH,
+ RTEMS_RECORD_ARG_0,
+ RTEMS_RECORD_ARG_1,
+ RTEMS_RECORD_ARG_2,
+ RTEMS_RECORD_ARG_3,
+ RTEMS_RECORD_ARG_4,
+ RTEMS_RECORD_ARG_5,
+ RTEMS_RECORD_ARG_6,
+ RTEMS_RECORD_ARG_7,
+ RTEMS_RECORD_ARG_8,
+ RTEMS_RECORD_ARG_9,
+ RTEMS_RECORD_BIND_ENTRY,
+ RTEMS_RECORD_BIND_EXIT,
+ RTEMS_RECORD_BSP,
+ RTEMS_RECORD_BUFFER,
+ RTEMS_RECORD_CALLER,
+ RTEMS_RECORD_CALLOC_ENTRY,
+ RTEMS_RECORD_CALLOC_EXIT,
+ RTEMS_RECORD_CHOWN_ENTRY,
+ RTEMS_RECORD_CHOWN_EXIT,
+ RTEMS_RECORD_CLOSE_ENTRY,
+ RTEMS_RECORD_CLOSE_EXIT,
+ RTEMS_RECORD_CONNECT_ENTRY,
+ RTEMS_RECORD_CONNECT_EXIT,
+ RTEMS_RECORD_ETHER_INPUT,
+ RTEMS_RECORD_ETHER_OUTPUT,
+ RTEMS_RECORD_ERRNO,
+ RTEMS_RECORD_FATAL_CODE,
+ RTEMS_RECORD_FATAL_SOURCE,
+ RTEMS_RECORD_FCHMOD_ENTRY,
+ RTEMS_RECORD_FCHMOD_EXIT,
+ RTEMS_RECORD_FCNTL_ENTRY,
+ RTEMS_RECORD_FCNTL_EXIT,
+ RTEMS_RECORD_FDATASYNC_ENTRY,
+ RTEMS_RECORD_FDATASYNC_EXIT,
+ RTEMS_RECORD_FREE_ENTRY,
+ RTEMS_RECORD_FREE_EXIT,
+ RTEMS_RECORD_FREQUENCY,
+ RTEMS_RECORD_FSTAT_ENTRY,
+ RTEMS_RECORD_FSTAT_EXIT,
+ RTEMS_RECORD_FSYNC_ENTRY,
+ RTEMS_RECORD_FSYNC_EXIT,
+ RTEMS_RECORD_FTRUNCATE_ENTRY,
+ RTEMS_RECORD_FTRUNCATE_EXIT,
+ RTEMS_RECORD_FUNCTION_ENTRY,
+ RTEMS_RECORD_FUNCTION_EXIT,
+ RTEMS_RECORD_GETSOCKOPT_ENTRY,
+ RTEMS_RECORD_GETSOCKOPT_EXIT,
+ RTEMS_RECORD_HEAP_ALLOC,
+ RTEMS_RECORD_HEAP_FREE,
+ RTEMS_RECORD_HEAP_SIZE,
+ RTEMS_RECORD_HEAP_USAGE,
+ RTEMS_RECORD_INTERRUPT_ENTRY,
+ RTEMS_RECORD_INTERRUPT_EXIT,
+ RTEMS_RECORD_INTERRUPT_INSTALL,
+ RTEMS_RECORD_INTERRUPT_REMOVE,
+ RTEMS_RECORD_INTERRUPT_SERVER_ENTRY,
+ RTEMS_RECORD_INTERRUPT_SERVER_EXIT,
+ RTEMS_RECORD_INTERRUPT_SERVER_INSTALL,
+ RTEMS_RECORD_INTERRUPT_SERVER_MOVE,
+ RTEMS_RECORD_INTERRUPT_SERVER_REMOVE,
+ RTEMS_RECORD_INTERRUPT_SERVER_TRIGGER,
+ RTEMS_RECORD_IOCTL_ENTRY,
+ RTEMS_RECORD_IOCTL_EXIT,
+ RTEMS_RECORD_IP6_INPUT,
+ RTEMS_RECORD_IP6_OUTPUT,
+ RTEMS_RECORD_IP_INPUT,
+ RTEMS_RECORD_IP_OUTPUT,
+ RTEMS_RECORD_ISR_DISABLE,
+ RTEMS_RECORD_ISR_ENABLE,
+ RTEMS_RECORD_ISR_LOCK_ACQUIRE_ENTRY,
+ RTEMS_RECORD_ISR_LOCK_ACQUIRE_EXIT,
+ RTEMS_RECORD_ISR_LOCK_ADDRESS,
+ RTEMS_RECORD_ISR_LOCK_DESTROY,
+ RTEMS_RECORD_ISR_LOCK_INITIALIZE,
+ RTEMS_RECORD_ISR_LOCK_NAME,
+ RTEMS_RECORD_ISR_LOCK_RELEASE,
+ RTEMS_RECORD_KEVENT_ENTRY,
+ RTEMS_RECORD_KEVENT_EXIT,
+ RTEMS_RECORD_KQUEUE_ENTRY,
+ RTEMS_RECORD_KQUEUE_EXIT,
+ RTEMS_RECORD_LENGTH,
+ RTEMS_RECORD_LINE,
+ RTEMS_RECORD_LINK_ENTRY,
+ RTEMS_RECORD_LINK_EXIT,
+ RTEMS_RECORD_LISTEN_ENTRY,
+ RTEMS_RECORD_LISTEN_EXIT,
+ RTEMS_RECORD_LSEEK_ENTRY,
+ RTEMS_RECORD_LSEEK_EXIT,
+ RTEMS_RECORD_MALLOC_ENTRY,
+ RTEMS_RECORD_MALLOC_EXIT,
+ RTEMS_RECORD_MEMORY,
+ RTEMS_RECORD_MKNOD_ENTRY,
+ RTEMS_RECORD_MKNOD_EXIT,
+ RTEMS_RECORD_MMAP_ENTRY,
+ RTEMS_RECORD_MMAP_EXIT,
+ RTEMS_RECORD_MOUNT_ENTRY,
+ RTEMS_RECORD_MOUNT_EXIT,
+ RTEMS_RECORD_MULTILIB,
+ RTEMS_RECORD_OPEN_ENTRY,
+ RTEMS_RECORD_OPEN_EXIT,
+ RTEMS_RECORD_PAGE_ALLOC,
+ RTEMS_RECORD_PAGE_FREE,
+ RTEMS_RECORD_PER_CPU_COUNT,
+ RTEMS_RECORD_PER_CPU_HEAD,
+ RTEMS_RECORD_PER_CPU_OVERFLOW,
+ RTEMS_RECORD_PER_CPU_TAIL,
+ RTEMS_RECORD_POLL_ENTRY,
+ RTEMS_RECORD_POLL_EXIT,
+ RTEMS_RECORD_POSIX_MEMALIGN_ENTRY,
+ RTEMS_RECORD_POSIX_MEMALIGN_EXIT,
+ RTEMS_RECORD_PROCESSOR,
+ RTEMS_RECORD_PROCESSOR_MAXIMUM,
+ RTEMS_RECORD_READ_ENTRY,
+ RTEMS_RECORD_READ_EXIT,
+ RTEMS_RECORD_READLINK_ENTRY,
+ RTEMS_RECORD_READLINK_EXIT,
+ RTEMS_RECORD_READV_ENTRY,
+ RTEMS_RECORD_READV_EXIT,
+ RTEMS_RECORD_REALLOC_ENTRY,
+ RTEMS_RECORD_REALLOC_EXIT,
+ RTEMS_RECORD_RECV_ENTRY,
+ RTEMS_RECORD_RECV_EXIT,
+ RTEMS_RECORD_RECVFROM_ENTRY,
+ RTEMS_RECORD_RECVFROM_EXIT,
+ RTEMS_RECORD_RECVMSG_ENTRY,
+ RTEMS_RECORD_RECVMSG_EXIT,
+ RTEMS_RECORD_REGISTERS,
+ RTEMS_RECORD_RENAME_ENTRY,
+ RTEMS_RECORD_RENAME_EXIT,
+ RTEMS_RECORD_RETURN_0,
+ RTEMS_RECORD_RETURN_1,
+ RTEMS_RECORD_RETURN_2,
+ RTEMS_RECORD_RETURN_3,
+ RTEMS_RECORD_RETURN_4,
+ RTEMS_RECORD_RETURN_5,
+ RTEMS_RECORD_RETURN_6,
+ RTEMS_RECORD_RETURN_7,
+ RTEMS_RECORD_RETURN_8,
+ RTEMS_RECORD_RETURN_9,
+ RTEMS_RECORD_RTEMS_BARRIER_CREATE,
+ RTEMS_RECORD_RTEMS_BARRIER_DELETE,
+ RTEMS_RECORD_RTEMS_BARRIER_RELEASE,
+ RTEMS_RECORD_RTEMS_BARRIER_WAIT,
+ RTEMS_RECORD_RTEMS_CALLOC_ENTRY,
+ RTEMS_RECORD_RTEMS_CALLOC_EXIT,
+ RTEMS_RECORD_RTEMS_EVENT_RECEIVE,
+ RTEMS_RECORD_RTEMS_EVENT_SEND,
+ RTEMS_RECORD_RTEMS_EVENT_SYSTEM_RECEIVE,
+ RTEMS_RECORD_RTEMS_EVENT_SYSTEM_SEND,
+ RTEMS_RECORD_RTEMS_MALLOC_ENTRY,
+ RTEMS_RECORD_RTEMS_MALLOC_EXIT,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_BROADCAST,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_CREATE,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_DELETE,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_FLUSH,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_RECEIVE,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_SEND,
+ RTEMS_RECORD_RTEMS_MESSAGE_QUEUE_URGENT,
+ RTEMS_RECORD_RTEMS_PARTITION_CREATE,
+ RTEMS_RECORD_RTEMS_PARTITION_DELETE,
+ RTEMS_RECORD_RTEMS_PARTITION_GET_BUFFER,
+ RTEMS_RECORD_RTEMS_PARTITION_RETURN_BUFFER,
+ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CANCEL,
+ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_CREATE,
+ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_DELETE,
+ RTEMS_RECORD_RTEMS_RATE_MONOTONIC_PERIOD,
+ RTEMS_RECORD_RTEMS_SEMAPHORE_CREATE,
+ RTEMS_RECORD_RTEMS_SEMAPHORE_DELETE,
+ RTEMS_RECORD_RTEMS_SEMAPHORE_FLUSH,
+ RTEMS_RECORD_RTEMS_SEMAPHORE_OBTAIN,
+ RTEMS_RECORD_RTEMS_SEMAPHORE_RELEASE,
+ RTEMS_RECORD_RTEMS_TIMER_CANCEL,
+ RTEMS_RECORD_RTEMS_TIMER_CREATE,
+ RTEMS_RECORD_RTEMS_TIMER_DELETE,
+ RTEMS_RECORD_RTEMS_TIMER_FIRE_AFTER,
+ RTEMS_RECORD_RTEMS_TIMER_FIRE_WHEN,
+ RTEMS_RECORD_RTEMS_TIMER_RESET,
+ RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_AFTER,
+ RTEMS_RECORD_RTEMS_TIMER_SERVER_FIRE_WHEN,
+ RTEMS_RECORD_SBWAIT_ENTRY,
+ RTEMS_RECORD_SBWAIT_EXIT,
+ RTEMS_RECORD_SBWAKEUP_ENTRY,
+ RTEMS_RECORD_SBWAKEUP_EXIT,
+ RTEMS_RECORD_SCHEDULER_ADD_PROCESSOR,
+ RTEMS_RECORD_SCHEDULER_ASK_FOR_HELP,
+ RTEMS_RECORD_SCHEDULER_BLOCK,
+ RTEMS_RECORD_SCHEDULER_CANCEL_JOB,
+ RTEMS_RECORD_SCHEDULER_ID,
+ RTEMS_RECORD_SCHEDULER_MAP_PRIORITY,
+ RTEMS_RECORD_SCHEDULER_NAME,
+ RTEMS_RECORD_SCHEDULER_PIN,
+ RTEMS_RECORD_SCHEDULER_RECONSIDER_HELP_REQUEST,
+ RTEMS_RECORD_SCHEDULER_RELEASE_JOB,
+ RTEMS_RECORD_SCHEDULER_REMOVE_PROCESSOR,
+ RTEMS_RECORD_SCHEDULER_SCHEDULE,
+ RTEMS_RECORD_SCHEDULER_SET_AFFINITY,
+ RTEMS_RECORD_SCHEDULER_TICK,
+ RTEMS_RECORD_SCHEDULER_UNBLOCK,
+ RTEMS_RECORD_SCHEDULER_UNMAP_PRIORITY,
+ RTEMS_RECORD_SCHEDULER_UNPIN,
+ RTEMS_RECORD_SCHEDULER_UPDATE_PRIORITY,
+ RTEMS_RECORD_SCHEDULER_WITHDRAW_NODE,
+ RTEMS_RECORD_SCHEDULER_YIELD,
+ RTEMS_RECORD_SELECT_ENTRY,
+ RTEMS_RECORD_SELECT_EXIT,
+ RTEMS_RECORD_SEND_ENTRY,
+ RTEMS_RECORD_SEND_EXIT,
+ RTEMS_RECORD_SENDMSG_ENTRY,
+ RTEMS_RECORD_SENDMSG_EXIT,
+ RTEMS_RECORD_SENDTO_ENTRY,
+ RTEMS_RECORD_SENDTO_EXIT,
+ RTEMS_RECORD_SETSOCKOPT_ENTRY,
+ RTEMS_RECORD_SETSOCKOPT_EXIT,
+ RTEMS_RECORD_SHUTDOWN_ENTRY,
+ RTEMS_RECORD_SHUTDOWN_EXIT,
+ RTEMS_RECORD_SOABORT_ENTRY,
+ RTEMS_RECORD_SOABORT_EXIT,
+ RTEMS_RECORD_SOACCEPT_ENTRY,
+ RTEMS_RECORD_SOACCEPT_EXIT,
+ RTEMS_RECORD_SOALLOC_ENTRY,
+ RTEMS_RECORD_SOALLOC_EXIT,
+ RTEMS_RECORD_SOBINDAT_ENTRY,
+ RTEMS_RECORD_SOBINDAT_EXIT,
+ RTEMS_RECORD_SOBIND_ENTRY,
+ RTEMS_RECORD_SOBIND_EXIT,
+ RTEMS_RECORD_SOCKET_ENTRY,
+ RTEMS_RECORD_SOCKET_EXIT,
+ RTEMS_RECORD_SOCLOSE_ENTRY,
+ RTEMS_RECORD_SOCLOSE_EXIT,
+ RTEMS_RECORD_SOCONNECT2_ENTRY,
+ RTEMS_RECORD_SOCONNECT2_EXIT,
+ RTEMS_RECORD_SOCONNECTAT_ENTRY,
+ RTEMS_RECORD_SOCONNECTAT_EXIT,
+ RTEMS_RECORD_SOCREATE_ENTRY,
+ RTEMS_RECORD_SOCREATE_EXIT,
+ RTEMS_RECORD_SODEALLOC_ENTRY,
+ RTEMS_RECORD_SODEALLOC_EXIT,
+ RTEMS_RECORD_SODISCONNECT_ENTRY,
+ RTEMS_RECORD_SODISCONNECT_EXIT,
+ RTEMS_RECORD_SOFREE_ENTRY,
+ RTEMS_RECORD_SOFREE_EXIT,
+ RTEMS_RECORD_SOLISTEN_ENTRY,
+ RTEMS_RECORD_SOLISTEN_EXIT,
+ RTEMS_RECORD_SONEWCONN_ENTRY,
+ RTEMS_RECORD_SONEWCONN_EXIT,
+ RTEMS_RECORD_SORECEIVE_ENTRY,
+ RTEMS_RECORD_SORECEIVE_EXIT,
+ RTEMS_RECORD_SORFLUSH_ENTRY,
+ RTEMS_RECORD_SORFLUSH_EXIT,
+ RTEMS_RECORD_SOSEND_ENTRY,
+ RTEMS_RECORD_SOSEND_EXIT,
+ RTEMS_RECORD_SOSHUTDOWN_ENTRY,
+ RTEMS_RECORD_SOSHUTDOWN_EXIT,
+ RTEMS_RECORD_STATVFS_ENTRY,
+ RTEMS_RECORD_STATVFS_EXIT,
+ RTEMS_RECORD_SYMLINK_ENTRY,
+ RTEMS_RECORD_SYMLINK_EXIT,
+ RTEMS_RECORD_TCP_CLOSE,
+ RTEMS_RECORD_TCP_INPUT,
+ RTEMS_RECORD_TCP_OUTPUT,
+ RTEMS_RECORD_THREAD_BEGIN,
+ RTEMS_RECORD_THREAD_CONTINUE_ENTRY,
+ RTEMS_RECORD_THREAD_CONTINUE_EXIT,
+ RTEMS_RECORD_THREAD_CREATE,
+ RTEMS_RECORD_THREAD_DELETE,
+ RTEMS_RECORD_THREAD_DISPATCH_DISABLE,
+ RTEMS_RECORD_THREAD_DISPATCH_ENABLE,
+ RTEMS_RECORD_THREAD_EXIT,
+ RTEMS_RECORD_THREAD_EXITTED,
+ RTEMS_RECORD_THREAD_ID,
+ RTEMS_RECORD_THREAD_NAME,
+ RTEMS_RECORD_THREAD_PRIO_CURRENT_HIGH,
+ RTEMS_RECORD_THREAD_PRIO_CURRENT_LOW,
+ RTEMS_RECORD_THREAD_PRIO_REAL_HIGH,
+ RTEMS_RECORD_THREAD_PRIO_REAL_LOW,
+ RTEMS_RECORD_THREAD_QUEUE_ADDRESS,
+ RTEMS_RECORD_THREAD_QUEUE_DESTROY,
+ RTEMS_RECORD_THREAD_QUEUE_ENQUEUE,
+ RTEMS_RECORD_THREAD_QUEUE_ENQUEUE_STICKY,
+ RTEMS_RECORD_THREAD_QUEUE_EXTRACT,
+ RTEMS_RECORD_THREAD_QUEUE_ID,
+ RTEMS_RECORD_THREAD_QUEUE_INITIALIZE,
+ RTEMS_RECORD_THREAD_QUEUE_NAME,
+ RTEMS_RECORD_THREAD_QUEUE_SURRENDER,
+ RTEMS_RECORD_THREAD_QUEUE_SURRENDER_STICKY,
+ RTEMS_RECORD_THREAD_RESOURCE_OBTAIN,
+ RTEMS_RECORD_THREAD_RESOURCE_RELEASE,
+ RTEMS_RECORD_THREAD_RESTART,
+ RTEMS_RECORD_THREAD_STACK_CURRENT,
+ RTEMS_RECORD_THREAD_STACK_SIZE,
+ RTEMS_RECORD_THREAD_STACK_USAGE,
+ RTEMS_RECORD_THREAD_START,
+ RTEMS_RECORD_THREAD_STATE_CLEAR,
+ RTEMS_RECORD_THREAD_STATE_SET,
+ RTEMS_RECORD_THREAD_SWITCH_IN,
+ RTEMS_RECORD_THREAD_SWITCH_OUT,
+ RTEMS_RECORD_THREAD_TERMINATE,
+ RTEMS_RECORD_THREAD_TIMER_INSERT_MONOTONIC,
+ RTEMS_RECORD_THREAD_TIMER_INSERT_REALTIME,
+ RTEMS_RECORD_THREAD_TIMER_INSERT_TICKS,
+ RTEMS_RECORD_THREAD_TIMER_REMOVE,
+ RTEMS_RECORD_TOOLS,
+ RTEMS_RECORD_UDP_INPUT,
+ RTEMS_RECORD_UDP_OUTPUT,
+ RTEMS_RECORD_UMA_ALLOC_PTR,
+ RTEMS_RECORD_UMA_ALLOC_ZONE,
+ RTEMS_RECORD_UMA_FREE_PTR,
+ RTEMS_RECORD_UMA_FREE_ZONE,
+ RTEMS_RECORD_UNLINK_ENTRY,
+ RTEMS_RECORD_UNLINK_EXIT,
+ RTEMS_RECORD_UNMOUNT_ENTRY,
+ RTEMS_RECORD_UNMOUNT_EXIT,
+ RTEMS_RECORD_UPTIME_HIGH,
+ RTEMS_RECORD_UPTIME_LOW,
+ RTEMS_RECORD_VERSION_CONTROL_KEY,
+ RTEMS_RECORD_WATCHDOG_ADDRESS,
+ RTEMS_RECORD_WATCHDOG_CPU,
+ RTEMS_RECORD_WATCHDOG_INITIALIZE,
+ RTEMS_RECORD_WATCHDOG_INSERT,
+ RTEMS_RECORD_WATCHDOG_PREINITIALIZE,
+ RTEMS_RECORD_WATCHDOG_REMOVE,
+ RTEMS_RECORD_WATCHDOG_ROUTINE,
+ RTEMS_RECORD_WATCHDOG_STATE,
+ RTEMS_RECORD_WORKSPACE_ALLOC_ENTRY,
+ RTEMS_RECORD_WORKSPACE_ALLOC_EXIT,
+ RTEMS_RECORD_WORKSPACE_FREE_ENTY,
+ RTEMS_RECORD_WORKSPACE_FREE_EXIT,
+ RTEMS_RECORD_WORKSPACE_SIZE,
+ RTEMS_RECORD_WORKSPACE_USAGE,
+ RTEMS_RECORD_WRITE_ENTRY,
+ RTEMS_RECORD_WRITE_EXIT,
+ RTEMS_RECORD_WRITEV_ENTRY,
+ RTEMS_RECORD_WRITEV_EXIT,
+
+ /* Unused system events */
+ RTEMS_RECORD_SYSTEM_341,
+ RTEMS_RECORD_SYSTEM_342,
+ RTEMS_RECORD_SYSTEM_343,
+ RTEMS_RECORD_SYSTEM_344,
+ RTEMS_RECORD_SYSTEM_345,
+ RTEMS_RECORD_SYSTEM_346,
+ RTEMS_RECORD_SYSTEM_347,
+ RTEMS_RECORD_SYSTEM_348,
+ RTEMS_RECORD_SYSTEM_349,
+ RTEMS_RECORD_SYSTEM_350,
+ RTEMS_RECORD_SYSTEM_351,
+ RTEMS_RECORD_SYSTEM_352,
+ RTEMS_RECORD_SYSTEM_353,
+ RTEMS_RECORD_SYSTEM_354,
+ RTEMS_RECORD_SYSTEM_355,
+ RTEMS_RECORD_SYSTEM_356,
+ RTEMS_RECORD_SYSTEM_357,
+ RTEMS_RECORD_SYSTEM_358,
+ RTEMS_RECORD_SYSTEM_359,
+ RTEMS_RECORD_SYSTEM_360,
+ RTEMS_RECORD_SYSTEM_361,
+ RTEMS_RECORD_SYSTEM_362,
+ RTEMS_RECORD_SYSTEM_363,
+ RTEMS_RECORD_SYSTEM_364,
+ RTEMS_RECORD_SYSTEM_365,
+ RTEMS_RECORD_SYSTEM_366,
+ RTEMS_RECORD_SYSTEM_367,
+ RTEMS_RECORD_SYSTEM_368,
+ RTEMS_RECORD_SYSTEM_369,
+ RTEMS_RECORD_SYSTEM_370,
+ RTEMS_RECORD_SYSTEM_371,
+ RTEMS_RECORD_SYSTEM_372,
+ RTEMS_RECORD_SYSTEM_373,
+ RTEMS_RECORD_SYSTEM_374,
+ RTEMS_RECORD_SYSTEM_375,
+ RTEMS_RECORD_SYSTEM_376,
+ RTEMS_RECORD_SYSTEM_377,
+ RTEMS_RECORD_SYSTEM_378,
+ RTEMS_RECORD_SYSTEM_379,
+ RTEMS_RECORD_SYSTEM_380,
+ RTEMS_RECORD_SYSTEM_381,
+ RTEMS_RECORD_SYSTEM_382,
+ RTEMS_RECORD_SYSTEM_383,
+ RTEMS_RECORD_SYSTEM_384,
+ RTEMS_RECORD_SYSTEM_385,
+ RTEMS_RECORD_SYSTEM_386,
+ RTEMS_RECORD_SYSTEM_387,
+ RTEMS_RECORD_SYSTEM_388,
+ RTEMS_RECORD_SYSTEM_389,
+ RTEMS_RECORD_SYSTEM_390,
+ RTEMS_RECORD_SYSTEM_391,
+ RTEMS_RECORD_SYSTEM_392,
+ RTEMS_RECORD_SYSTEM_393,
+ RTEMS_RECORD_SYSTEM_394,
+ RTEMS_RECORD_SYSTEM_395,
+ RTEMS_RECORD_SYSTEM_396,
+ RTEMS_RECORD_SYSTEM_397,
+ RTEMS_RECORD_SYSTEM_398,
+ RTEMS_RECORD_SYSTEM_399,
+ RTEMS_RECORD_SYSTEM_400,
+ RTEMS_RECORD_SYSTEM_401,
+ RTEMS_RECORD_SYSTEM_402,
+ RTEMS_RECORD_SYSTEM_403,
+ RTEMS_RECORD_SYSTEM_404,
+ RTEMS_RECORD_SYSTEM_405,
+ RTEMS_RECORD_SYSTEM_406,
+ RTEMS_RECORD_SYSTEM_407,
+ RTEMS_RECORD_SYSTEM_408,
+ RTEMS_RECORD_SYSTEM_409,
+ RTEMS_RECORD_SYSTEM_410,
+ RTEMS_RECORD_SYSTEM_411,
+ RTEMS_RECORD_SYSTEM_412,
+ RTEMS_RECORD_SYSTEM_413,
+ RTEMS_RECORD_SYSTEM_414,
+ RTEMS_RECORD_SYSTEM_415,
+ RTEMS_RECORD_SYSTEM_416,
+ RTEMS_RECORD_SYSTEM_417,
+ RTEMS_RECORD_SYSTEM_418,
+ RTEMS_RECORD_SYSTEM_419,
+ RTEMS_RECORD_SYSTEM_420,
+ RTEMS_RECORD_SYSTEM_421,
+ RTEMS_RECORD_SYSTEM_422,
+ RTEMS_RECORD_SYSTEM_423,
+ RTEMS_RECORD_SYSTEM_424,
+ RTEMS_RECORD_SYSTEM_425,
+ RTEMS_RECORD_SYSTEM_426,
+ RTEMS_RECORD_SYSTEM_427,
+ RTEMS_RECORD_SYSTEM_428,
+ RTEMS_RECORD_SYSTEM_429,
+ RTEMS_RECORD_SYSTEM_430,
+ RTEMS_RECORD_SYSTEM_431,
+ RTEMS_RECORD_SYSTEM_432,
+ RTEMS_RECORD_SYSTEM_433,
+ RTEMS_RECORD_SYSTEM_434,
+ RTEMS_RECORD_SYSTEM_435,
+ RTEMS_RECORD_SYSTEM_436,
+ RTEMS_RECORD_SYSTEM_437,
+ RTEMS_RECORD_SYSTEM_438,
+ RTEMS_RECORD_SYSTEM_439,
+ RTEMS_RECORD_SYSTEM_440,
+ RTEMS_RECORD_SYSTEM_441,
+ RTEMS_RECORD_SYSTEM_442,
+ RTEMS_RECORD_SYSTEM_443,
+ RTEMS_RECORD_SYSTEM_444,
+ RTEMS_RECORD_SYSTEM_445,
+ RTEMS_RECORD_SYSTEM_446,
+ RTEMS_RECORD_SYSTEM_447,
+ RTEMS_RECORD_SYSTEM_448,
+ RTEMS_RECORD_SYSTEM_449,
+ RTEMS_RECORD_SYSTEM_450,
+ RTEMS_RECORD_SYSTEM_451,
+ RTEMS_RECORD_SYSTEM_452,
+ RTEMS_RECORD_SYSTEM_453,
+ RTEMS_RECORD_SYSTEM_454,
+ RTEMS_RECORD_SYSTEM_455,
+ RTEMS_RECORD_SYSTEM_456,
+ RTEMS_RECORD_SYSTEM_457,
+ RTEMS_RECORD_SYSTEM_458,
+ RTEMS_RECORD_SYSTEM_459,
+ RTEMS_RECORD_SYSTEM_460,
+ RTEMS_RECORD_SYSTEM_461,
+ RTEMS_RECORD_SYSTEM_462,
+ RTEMS_RECORD_SYSTEM_463,
+ RTEMS_RECORD_SYSTEM_464,
+ RTEMS_RECORD_SYSTEM_465,
+ RTEMS_RECORD_SYSTEM_466,
+ RTEMS_RECORD_SYSTEM_467,
+ RTEMS_RECORD_SYSTEM_468,
+ RTEMS_RECORD_SYSTEM_469,
+ RTEMS_RECORD_SYSTEM_470,
+ RTEMS_RECORD_SYSTEM_471,
+ RTEMS_RECORD_SYSTEM_472,
+ RTEMS_RECORD_SYSTEM_473,
+ RTEMS_RECORD_SYSTEM_474,
+ RTEMS_RECORD_SYSTEM_475,
+ RTEMS_RECORD_SYSTEM_476,
+ RTEMS_RECORD_SYSTEM_477,
+ RTEMS_RECORD_SYSTEM_478,
+ RTEMS_RECORD_SYSTEM_479,
+ RTEMS_RECORD_SYSTEM_480,
+ RTEMS_RECORD_SYSTEM_481,
+ RTEMS_RECORD_SYSTEM_482,
+ RTEMS_RECORD_SYSTEM_483,
+ RTEMS_RECORD_SYSTEM_484,
+ RTEMS_RECORD_SYSTEM_485,
+ RTEMS_RECORD_SYSTEM_486,
+ RTEMS_RECORD_SYSTEM_487,
+ RTEMS_RECORD_SYSTEM_488,
+ RTEMS_RECORD_SYSTEM_489,
+ RTEMS_RECORD_SYSTEM_490,
+ RTEMS_RECORD_SYSTEM_491,
+ RTEMS_RECORD_SYSTEM_492,
+ RTEMS_RECORD_SYSTEM_493,
+ RTEMS_RECORD_SYSTEM_494,
+ RTEMS_RECORD_SYSTEM_495,
+ RTEMS_RECORD_SYSTEM_496,
+ RTEMS_RECORD_SYSTEM_497,
+ RTEMS_RECORD_SYSTEM_498,
+ RTEMS_RECORD_SYSTEM_499,
+ RTEMS_RECORD_SYSTEM_500,
+ RTEMS_RECORD_SYSTEM_501,
+ RTEMS_RECORD_SYSTEM_502,
+ RTEMS_RECORD_SYSTEM_503,
+ RTEMS_RECORD_SYSTEM_504,
+ RTEMS_RECORD_SYSTEM_505,
+ RTEMS_RECORD_SYSTEM_506,
+ RTEMS_RECORD_SYSTEM_507,
+ RTEMS_RECORD_SYSTEM_508,
+ RTEMS_RECORD_SYSTEM_509,
+ RTEMS_RECORD_SYSTEM_510,
+ RTEMS_RECORD_SYSTEM_511,
+
+ /* There are 512 events reserved for the user */
+ RTEMS_RECORD_USER_0,
+ RTEMS_RECORD_USER_1,
+ RTEMS_RECORD_USER_2,
+ RTEMS_RECORD_USER_3,
+ RTEMS_RECORD_USER_4,
+ RTEMS_RECORD_USER_5,
+ RTEMS_RECORD_USER_6,
+ RTEMS_RECORD_USER_7,
+ RTEMS_RECORD_USER_8,
+ RTEMS_RECORD_USER_9,
+ RTEMS_RECORD_USER_10,
+ RTEMS_RECORD_USER_11,
+ RTEMS_RECORD_USER_12,
+ RTEMS_RECORD_USER_13,
+ RTEMS_RECORD_USER_14,
+ RTEMS_RECORD_USER_15,
+ RTEMS_RECORD_USER_16,
+ RTEMS_RECORD_USER_17,
+ RTEMS_RECORD_USER_18,
+ RTEMS_RECORD_USER_19,
+ RTEMS_RECORD_USER_20,
+ RTEMS_RECORD_USER_21,
+ RTEMS_RECORD_USER_22,
+ RTEMS_RECORD_USER_23,
+ RTEMS_RECORD_USER_24,
+ RTEMS_RECORD_USER_25,
+ RTEMS_RECORD_USER_26,
+ RTEMS_RECORD_USER_27,
+ RTEMS_RECORD_USER_28,
+ RTEMS_RECORD_USER_29,
+ RTEMS_RECORD_USER_30,
+ RTEMS_RECORD_USER_31,
+ RTEMS_RECORD_USER_32,
+ RTEMS_RECORD_USER_33,
+ RTEMS_RECORD_USER_34,
+ RTEMS_RECORD_USER_35,
+ RTEMS_RECORD_USER_36,
+ RTEMS_RECORD_USER_37,
+ RTEMS_RECORD_USER_38,
+ RTEMS_RECORD_USER_39,
+ RTEMS_RECORD_USER_40,
+ RTEMS_RECORD_USER_41,
+ RTEMS_RECORD_USER_42,
+ RTEMS_RECORD_USER_43,
+ RTEMS_RECORD_USER_44,
+ RTEMS_RECORD_USER_45,
+ RTEMS_RECORD_USER_46,
+ RTEMS_RECORD_USER_47,
+ RTEMS_RECORD_USER_48,
+ RTEMS_RECORD_USER_49,
+ RTEMS_RECORD_USER_50,
+ RTEMS_RECORD_USER_51,
+ RTEMS_RECORD_USER_52,
+ RTEMS_RECORD_USER_53,
+ RTEMS_RECORD_USER_54,
+ RTEMS_RECORD_USER_55,
+ RTEMS_RECORD_USER_56,
+ RTEMS_RECORD_USER_57,
+ RTEMS_RECORD_USER_58,
+ RTEMS_RECORD_USER_59,
+ RTEMS_RECORD_USER_60,
+ RTEMS_RECORD_USER_61,
+ RTEMS_RECORD_USER_62,
+ RTEMS_RECORD_USER_63,
+ RTEMS_RECORD_USER_64,
+ RTEMS_RECORD_USER_65,
+ RTEMS_RECORD_USER_66,
+ RTEMS_RECORD_USER_67,
+ RTEMS_RECORD_USER_68,
+ RTEMS_RECORD_USER_69,
+ RTEMS_RECORD_USER_70,
+ RTEMS_RECORD_USER_71,
+ RTEMS_RECORD_USER_72,
+ RTEMS_RECORD_USER_73,
+ RTEMS_RECORD_USER_74,
+ RTEMS_RECORD_USER_75,
+ RTEMS_RECORD_USER_76,
+ RTEMS_RECORD_USER_77,
+ RTEMS_RECORD_USER_78,
+ RTEMS_RECORD_USER_79,
+ RTEMS_RECORD_USER_80,
+ RTEMS_RECORD_USER_81,
+ RTEMS_RECORD_USER_82,
+ RTEMS_RECORD_USER_83,
+ RTEMS_RECORD_USER_84,
+ RTEMS_RECORD_USER_85,
+ RTEMS_RECORD_USER_86,
+ RTEMS_RECORD_USER_87,
+ RTEMS_RECORD_USER_88,
+ RTEMS_RECORD_USER_89,
+ RTEMS_RECORD_USER_90,
+ RTEMS_RECORD_USER_91,
+ RTEMS_RECORD_USER_92,
+ RTEMS_RECORD_USER_93,
+ RTEMS_RECORD_USER_94,
+ RTEMS_RECORD_USER_95,
+ RTEMS_RECORD_USER_96,
+ RTEMS_RECORD_USER_97,
+ RTEMS_RECORD_USER_98,
+ RTEMS_RECORD_USER_99,
+ RTEMS_RECORD_USER_100,
+ RTEMS_RECORD_USER_101,
+ RTEMS_RECORD_USER_102,
+ RTEMS_RECORD_USER_103,
+ RTEMS_RECORD_USER_104,
+ RTEMS_RECORD_USER_105,
+ RTEMS_RECORD_USER_106,
+ RTEMS_RECORD_USER_107,
+ RTEMS_RECORD_USER_108,
+ RTEMS_RECORD_USER_109,
+ RTEMS_RECORD_USER_110,
+ RTEMS_RECORD_USER_111,
+ RTEMS_RECORD_USER_112,
+ RTEMS_RECORD_USER_113,
+ RTEMS_RECORD_USER_114,
+ RTEMS_RECORD_USER_115,
+ RTEMS_RECORD_USER_116,
+ RTEMS_RECORD_USER_117,
+ RTEMS_RECORD_USER_118,
+ RTEMS_RECORD_USER_119,
+ RTEMS_RECORD_USER_120,
+ RTEMS_RECORD_USER_121,
+ RTEMS_RECORD_USER_122,
+ RTEMS_RECORD_USER_123,
+ RTEMS_RECORD_USER_124,
+ RTEMS_RECORD_USER_125,
+ RTEMS_RECORD_USER_126,
+ RTEMS_RECORD_USER_127,
+ RTEMS_RECORD_USER_128,
+ RTEMS_RECORD_USER_129,
+ RTEMS_RECORD_USER_130,
+ RTEMS_RECORD_USER_131,
+ RTEMS_RECORD_USER_132,
+ RTEMS_RECORD_USER_133,
+ RTEMS_RECORD_USER_134,
+ RTEMS_RECORD_USER_135,
+ RTEMS_RECORD_USER_136,
+ RTEMS_RECORD_USER_137,
+ RTEMS_RECORD_USER_138,
+ RTEMS_RECORD_USER_139,
+ RTEMS_RECORD_USER_140,
+ RTEMS_RECORD_USER_141,
+ RTEMS_RECORD_USER_142,
+ RTEMS_RECORD_USER_143,
+ RTEMS_RECORD_USER_144,
+ RTEMS_RECORD_USER_145,
+ RTEMS_RECORD_USER_146,
+ RTEMS_RECORD_USER_147,
+ RTEMS_RECORD_USER_148,
+ RTEMS_RECORD_USER_149,
+ RTEMS_RECORD_USER_150,
+ RTEMS_RECORD_USER_151,
+ RTEMS_RECORD_USER_152,
+ RTEMS_RECORD_USER_153,
+ RTEMS_RECORD_USER_154,
+ RTEMS_RECORD_USER_155,
+ RTEMS_RECORD_USER_156,
+ RTEMS_RECORD_USER_157,
+ RTEMS_RECORD_USER_158,
+ RTEMS_RECORD_USER_159,
+ RTEMS_RECORD_USER_160,
+ RTEMS_RECORD_USER_161,
+ RTEMS_RECORD_USER_162,
+ RTEMS_RECORD_USER_163,
+ RTEMS_RECORD_USER_164,
+ RTEMS_RECORD_USER_165,
+ RTEMS_RECORD_USER_166,
+ RTEMS_RECORD_USER_167,
+ RTEMS_RECORD_USER_168,
+ RTEMS_RECORD_USER_169,
+ RTEMS_RECORD_USER_170,
+ RTEMS_RECORD_USER_171,
+ RTEMS_RECORD_USER_172,
+ RTEMS_RECORD_USER_173,
+ RTEMS_RECORD_USER_174,
+ RTEMS_RECORD_USER_175,
+ RTEMS_RECORD_USER_176,
+ RTEMS_RECORD_USER_177,
+ RTEMS_RECORD_USER_178,
+ RTEMS_RECORD_USER_179,
+ RTEMS_RECORD_USER_180,
+ RTEMS_RECORD_USER_181,
+ RTEMS_RECORD_USER_182,
+ RTEMS_RECORD_USER_183,
+ RTEMS_RECORD_USER_184,
+ RTEMS_RECORD_USER_185,
+ RTEMS_RECORD_USER_186,
+ RTEMS_RECORD_USER_187,
+ RTEMS_RECORD_USER_188,
+ RTEMS_RECORD_USER_189,
+ RTEMS_RECORD_USER_190,
+ RTEMS_RECORD_USER_191,
+ RTEMS_RECORD_USER_192,
+ RTEMS_RECORD_USER_193,
+ RTEMS_RECORD_USER_194,
+ RTEMS_RECORD_USER_195,
+ RTEMS_RECORD_USER_196,
+ RTEMS_RECORD_USER_197,
+ RTEMS_RECORD_USER_198,
+ RTEMS_RECORD_USER_199,
+ RTEMS_RECORD_USER_200,
+ RTEMS_RECORD_USER_201,
+ RTEMS_RECORD_USER_202,
+ RTEMS_RECORD_USER_203,
+ RTEMS_RECORD_USER_204,
+ RTEMS_RECORD_USER_205,
+ RTEMS_RECORD_USER_206,
+ RTEMS_RECORD_USER_207,
+ RTEMS_RECORD_USER_208,
+ RTEMS_RECORD_USER_209,
+ RTEMS_RECORD_USER_210,
+ RTEMS_RECORD_USER_211,
+ RTEMS_RECORD_USER_212,
+ RTEMS_RECORD_USER_213,
+ RTEMS_RECORD_USER_214,
+ RTEMS_RECORD_USER_215,
+ RTEMS_RECORD_USER_216,
+ RTEMS_RECORD_USER_217,
+ RTEMS_RECORD_USER_218,
+ RTEMS_RECORD_USER_219,
+ RTEMS_RECORD_USER_220,
+ RTEMS_RECORD_USER_221,
+ RTEMS_RECORD_USER_222,
+ RTEMS_RECORD_USER_223,
+ RTEMS_RECORD_USER_224,
+ RTEMS_RECORD_USER_225,
+ RTEMS_RECORD_USER_226,
+ RTEMS_RECORD_USER_227,
+ RTEMS_RECORD_USER_228,
+ RTEMS_RECORD_USER_229,
+ RTEMS_RECORD_USER_230,
+ RTEMS_RECORD_USER_231,
+ RTEMS_RECORD_USER_232,
+ RTEMS_RECORD_USER_233,
+ RTEMS_RECORD_USER_234,
+ RTEMS_RECORD_USER_235,
+ RTEMS_RECORD_USER_236,
+ RTEMS_RECORD_USER_237,
+ RTEMS_RECORD_USER_238,
+ RTEMS_RECORD_USER_239,
+ RTEMS_RECORD_USER_240,
+ RTEMS_RECORD_USER_241,
+ RTEMS_RECORD_USER_242,
+ RTEMS_RECORD_USER_243,
+ RTEMS_RECORD_USER_244,
+ RTEMS_RECORD_USER_245,
+ RTEMS_RECORD_USER_246,
+ RTEMS_RECORD_USER_247,
+ RTEMS_RECORD_USER_248,
+ RTEMS_RECORD_USER_249,
+ RTEMS_RECORD_USER_250,
+ RTEMS_RECORD_USER_251,
+ RTEMS_RECORD_USER_252,
+ RTEMS_RECORD_USER_253,
+ RTEMS_RECORD_USER_254,
+ RTEMS_RECORD_USER_255,
+ RTEMS_RECORD_USER_256,
+ RTEMS_RECORD_USER_257,
+ RTEMS_RECORD_USER_258,
+ RTEMS_RECORD_USER_259,
+ RTEMS_RECORD_USER_260,
+ RTEMS_RECORD_USER_261,
+ RTEMS_RECORD_USER_262,
+ RTEMS_RECORD_USER_263,
+ RTEMS_RECORD_USER_264,
+ RTEMS_RECORD_USER_265,
+ RTEMS_RECORD_USER_266,
+ RTEMS_RECORD_USER_267,
+ RTEMS_RECORD_USER_268,
+ RTEMS_RECORD_USER_269,
+ RTEMS_RECORD_USER_270,
+ RTEMS_RECORD_USER_271,
+ RTEMS_RECORD_USER_272,
+ RTEMS_RECORD_USER_273,
+ RTEMS_RECORD_USER_274,
+ RTEMS_RECORD_USER_275,
+ RTEMS_RECORD_USER_276,
+ RTEMS_RECORD_USER_277,
+ RTEMS_RECORD_USER_278,
+ RTEMS_RECORD_USER_279,
+ RTEMS_RECORD_USER_280,
+ RTEMS_RECORD_USER_281,
+ RTEMS_RECORD_USER_282,
+ RTEMS_RECORD_USER_283,
+ RTEMS_RECORD_USER_284,
+ RTEMS_RECORD_USER_285,
+ RTEMS_RECORD_USER_286,
+ RTEMS_RECORD_USER_287,
+ RTEMS_RECORD_USER_288,
+ RTEMS_RECORD_USER_289,
+ RTEMS_RECORD_USER_290,
+ RTEMS_RECORD_USER_291,
+ RTEMS_RECORD_USER_292,
+ RTEMS_RECORD_USER_293,
+ RTEMS_RECORD_USER_294,
+ RTEMS_RECORD_USER_295,
+ RTEMS_RECORD_USER_296,
+ RTEMS_RECORD_USER_297,
+ RTEMS_RECORD_USER_298,
+ RTEMS_RECORD_USER_299,
+ RTEMS_RECORD_USER_300,
+ RTEMS_RECORD_USER_301,
+ RTEMS_RECORD_USER_302,
+ RTEMS_RECORD_USER_303,
+ RTEMS_RECORD_USER_304,
+ RTEMS_RECORD_USER_305,
+ RTEMS_RECORD_USER_306,
+ RTEMS_RECORD_USER_307,
+ RTEMS_RECORD_USER_308,
+ RTEMS_RECORD_USER_309,
+ RTEMS_RECORD_USER_310,
+ RTEMS_RECORD_USER_311,
+ RTEMS_RECORD_USER_312,
+ RTEMS_RECORD_USER_313,
+ RTEMS_RECORD_USER_314,
+ RTEMS_RECORD_USER_315,
+ RTEMS_RECORD_USER_316,
+ RTEMS_RECORD_USER_317,
+ RTEMS_RECORD_USER_318,
+ RTEMS_RECORD_USER_319,
+ RTEMS_RECORD_USER_320,
+ RTEMS_RECORD_USER_321,
+ RTEMS_RECORD_USER_322,
+ RTEMS_RECORD_USER_323,
+ RTEMS_RECORD_USER_324,
+ RTEMS_RECORD_USER_325,
+ RTEMS_RECORD_USER_326,
+ RTEMS_RECORD_USER_327,
+ RTEMS_RECORD_USER_328,
+ RTEMS_RECORD_USER_329,
+ RTEMS_RECORD_USER_330,
+ RTEMS_RECORD_USER_331,
+ RTEMS_RECORD_USER_332,
+ RTEMS_RECORD_USER_333,
+ RTEMS_RECORD_USER_334,
+ RTEMS_RECORD_USER_335,
+ RTEMS_RECORD_USER_336,
+ RTEMS_RECORD_USER_337,
+ RTEMS_RECORD_USER_338,
+ RTEMS_RECORD_USER_339,
+ RTEMS_RECORD_USER_340,
+ RTEMS_RECORD_USER_341,
+ RTEMS_RECORD_USER_342,
+ RTEMS_RECORD_USER_343,
+ RTEMS_RECORD_USER_344,
+ RTEMS_RECORD_USER_345,
+ RTEMS_RECORD_USER_346,
+ RTEMS_RECORD_USER_347,
+ RTEMS_RECORD_USER_348,
+ RTEMS_RECORD_USER_349,
+ RTEMS_RECORD_USER_350,
+ RTEMS_RECORD_USER_351,
+ RTEMS_RECORD_USER_352,
+ RTEMS_RECORD_USER_353,
+ RTEMS_RECORD_USER_354,
+ RTEMS_RECORD_USER_355,
+ RTEMS_RECORD_USER_356,
+ RTEMS_RECORD_USER_357,
+ RTEMS_RECORD_USER_358,
+ RTEMS_RECORD_USER_359,
+ RTEMS_RECORD_USER_360,
+ RTEMS_RECORD_USER_361,
+ RTEMS_RECORD_USER_362,
+ RTEMS_RECORD_USER_363,
+ RTEMS_RECORD_USER_364,
+ RTEMS_RECORD_USER_365,
+ RTEMS_RECORD_USER_366,
+ RTEMS_RECORD_USER_367,
+ RTEMS_RECORD_USER_368,
+ RTEMS_RECORD_USER_369,
+ RTEMS_RECORD_USER_370,
+ RTEMS_RECORD_USER_371,
+ RTEMS_RECORD_USER_372,
+ RTEMS_RECORD_USER_373,
+ RTEMS_RECORD_USER_374,
+ RTEMS_RECORD_USER_375,
+ RTEMS_RECORD_USER_376,
+ RTEMS_RECORD_USER_377,
+ RTEMS_RECORD_USER_378,
+ RTEMS_RECORD_USER_379,
+ RTEMS_RECORD_USER_380,
+ RTEMS_RECORD_USER_381,
+ RTEMS_RECORD_USER_382,
+ RTEMS_RECORD_USER_383,
+ RTEMS_RECORD_USER_384,
+ RTEMS_RECORD_USER_385,
+ RTEMS_RECORD_USER_386,
+ RTEMS_RECORD_USER_387,
+ RTEMS_RECORD_USER_388,
+ RTEMS_RECORD_USER_389,
+ RTEMS_RECORD_USER_390,
+ RTEMS_RECORD_USER_391,
+ RTEMS_RECORD_USER_392,
+ RTEMS_RECORD_USER_393,
+ RTEMS_RECORD_USER_394,
+ RTEMS_RECORD_USER_395,
+ RTEMS_RECORD_USER_396,
+ RTEMS_RECORD_USER_397,
+ RTEMS_RECORD_USER_398,
+ RTEMS_RECORD_USER_399,
+ RTEMS_RECORD_USER_400,
+ RTEMS_RECORD_USER_401,
+ RTEMS_RECORD_USER_402,
+ RTEMS_RECORD_USER_403,
+ RTEMS_RECORD_USER_404,
+ RTEMS_RECORD_USER_405,
+ RTEMS_RECORD_USER_406,
+ RTEMS_RECORD_USER_407,
+ RTEMS_RECORD_USER_408,
+ RTEMS_RECORD_USER_409,
+ RTEMS_RECORD_USER_410,
+ RTEMS_RECORD_USER_411,
+ RTEMS_RECORD_USER_412,
+ RTEMS_RECORD_USER_413,
+ RTEMS_RECORD_USER_414,
+ RTEMS_RECORD_USER_415,
+ RTEMS_RECORD_USER_416,
+ RTEMS_RECORD_USER_417,
+ RTEMS_RECORD_USER_418,
+ RTEMS_RECORD_USER_419,
+ RTEMS_RECORD_USER_420,
+ RTEMS_RECORD_USER_421,
+ RTEMS_RECORD_USER_422,
+ RTEMS_RECORD_USER_423,
+ RTEMS_RECORD_USER_424,
+ RTEMS_RECORD_USER_425,
+ RTEMS_RECORD_USER_426,
+ RTEMS_RECORD_USER_427,
+ RTEMS_RECORD_USER_428,
+ RTEMS_RECORD_USER_429,
+ RTEMS_RECORD_USER_430,
+ RTEMS_RECORD_USER_431,
+ RTEMS_RECORD_USER_432,
+ RTEMS_RECORD_USER_433,
+ RTEMS_RECORD_USER_434,
+ RTEMS_RECORD_USER_435,
+ RTEMS_RECORD_USER_436,
+ RTEMS_RECORD_USER_437,
+ RTEMS_RECORD_USER_438,
+ RTEMS_RECORD_USER_439,
+ RTEMS_RECORD_USER_440,
+ RTEMS_RECORD_USER_441,
+ RTEMS_RECORD_USER_442,
+ RTEMS_RECORD_USER_443,
+ RTEMS_RECORD_USER_444,
+ RTEMS_RECORD_USER_445,
+ RTEMS_RECORD_USER_446,
+ RTEMS_RECORD_USER_447,
+ RTEMS_RECORD_USER_448,
+ RTEMS_RECORD_USER_449,
+ RTEMS_RECORD_USER_450,
+ RTEMS_RECORD_USER_451,
+ RTEMS_RECORD_USER_452,
+ RTEMS_RECORD_USER_453,
+ RTEMS_RECORD_USER_454,
+ RTEMS_RECORD_USER_455,
+ RTEMS_RECORD_USER_456,
+ RTEMS_RECORD_USER_457,
+ RTEMS_RECORD_USER_458,
+ RTEMS_RECORD_USER_459,
+ RTEMS_RECORD_USER_460,
+ RTEMS_RECORD_USER_461,
+ RTEMS_RECORD_USER_462,
+ RTEMS_RECORD_USER_463,
+ RTEMS_RECORD_USER_464,
+ RTEMS_RECORD_USER_465,
+ RTEMS_RECORD_USER_466,
+ RTEMS_RECORD_USER_467,
+ RTEMS_RECORD_USER_468,
+ RTEMS_RECORD_USER_469,
+ RTEMS_RECORD_USER_470,
+ RTEMS_RECORD_USER_471,
+ RTEMS_RECORD_USER_472,
+ RTEMS_RECORD_USER_473,
+ RTEMS_RECORD_USER_474,
+ RTEMS_RECORD_USER_475,
+ RTEMS_RECORD_USER_476,
+ RTEMS_RECORD_USER_477,
+ RTEMS_RECORD_USER_478,
+ RTEMS_RECORD_USER_479,
+ RTEMS_RECORD_USER_480,
+ RTEMS_RECORD_USER_481,
+ RTEMS_RECORD_USER_482,
+ RTEMS_RECORD_USER_483,
+ RTEMS_RECORD_USER_484,
+ RTEMS_RECORD_USER_485,
+ RTEMS_RECORD_USER_486,
+ RTEMS_RECORD_USER_487,
+ RTEMS_RECORD_USER_488,
+ RTEMS_RECORD_USER_489,
+ RTEMS_RECORD_USER_490,
+ RTEMS_RECORD_USER_491,
+ RTEMS_RECORD_USER_492,
+ RTEMS_RECORD_USER_493,
+ RTEMS_RECORD_USER_494,
+ RTEMS_RECORD_USER_495,
+ RTEMS_RECORD_USER_496,
+ RTEMS_RECORD_USER_497,
+ RTEMS_RECORD_USER_498,
+ RTEMS_RECORD_USER_499,
+ RTEMS_RECORD_USER_500,
+ RTEMS_RECORD_USER_501,
+ RTEMS_RECORD_USER_502,
+ RTEMS_RECORD_USER_503,
+ RTEMS_RECORD_USER_504,
+ RTEMS_RECORD_USER_505,
+ RTEMS_RECORD_USER_506,
+ RTEMS_RECORD_USER_507,
+ RTEMS_RECORD_USER_508,
+ RTEMS_RECORD_USER_509,
+ RTEMS_RECORD_USER_510,
+ RTEMS_RECORD_USER_511
+} rtems_record_event;
+
+#define RTEMS_RECORD_LAST RTEMS_RECORD_USER_511
+
+#define RTEMS_RECORD_USER( index ) ( RTEMS_RECORD_USER_0 + ( index ) )
+
+/**
+ * @brief Bits in the record item event member reserved for the actual event.
+ */
+#define RTEMS_RECORD_EVENT_BITS 10
+
+/**
+ * @brief Bits in the record item event member reserved for the time of the
+ * event.
+ */
+#define RTEMS_RECORD_TIME_BITS 22
+
+/**
+ * @brief Builds a time event for the specified time stamp and event.
+ *
+ * The events are stored in the record item with a time stamp. There are 22
+ * bits allocated to the time stamp and 10 bits allocated to the event. The 22
+ * bits are enough to get reliable time stamps on a system with a 4GHz CPU
+ * counter and a 1000Hz clock tick.
+ */
+#define RTEMS_RECORD_TIME_EVENT( time, event ) \
+ ( ( ( time ) << RTEMS_RECORD_EVENT_BITS ) | ( event ) )
+
+/**
+ * @brief Gets the time of a time event.
+ */
+#define RTEMS_RECORD_GET_TIME( time_event ) \
+ ( ( time_event ) >> RTEMS_RECORD_EVENT_BITS )
+
+/**
+ * @brief Gets the event of a time event.
+ */
+#define RTEMS_RECORD_GET_EVENT( time_event ) \
+ ( ( time_event ) & ( ( 1U << RTEMS_RECORD_EVENT_BITS ) - 1U ) )
+
+/**
+ * @brief The record data integer type.
+ *
+ * It is big enough to store 32-bit integers and pointers.
+ */
+typedef unsigned long rtems_record_data;
+
+/**
+ * @brief The native record item.
+ */
+typedef struct __attribute__((__packed__)) {
+ uint32_t event;
+ rtems_record_data data;
+} rtems_record_item;
+
+/**
+ * @brief The 32-bit format record item.
+ */
+typedef struct {
+ uint32_t event;
+ uint32_t data;
+} rtems_record_item_32;
+
+/**
+ * @brief The 64-bit format record item.
+ */
+typedef struct __attribute__((__packed__)) {
+ uint32_t event;
+ uint64_t data;
+} rtems_record_item_64;
+
+const char *rtems_record_event_text( rtems_record_event event );
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_RECORDDATA_H */
diff --git a/trace/wscript b/trace/wscript
new file mode 100644
index 0000000..53a1ab4
--- /dev/null
+++ b/trace/wscript
@@ -0,0 +1,103 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2014-2016 Chris Johns (chrisj@rtems.org)
+# Copyright 2019 embedded brains GmbH
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+#
+# RTEMS record build script.
+#
+
+def init(ctx):
+ pass
+
+def options(opt):
+ opt.load('compiler_c')
+ opt.load('compiler_cxx')
+
+def configure(conf):
+ conf.load('compiler_c')
+ conf.load('compiler_cxx')
+ try:
+ cppflags = conf.cmd_and_log(['llvm-config', '--cppflags'])
+ ldflags = conf.cmd_and_log(['llvm-config', '--ldflags'])
+ conf.env.append_value('CPPFLAGS', cppflags.split())
+ conf.env.append_value('LDFLAGS', ldflags.split())
+ except:
+ pass
+ if conf.check_cxx(lib = 'LLVM', mandatory=False):
+ conf.check(header_name='llvm/DebugInfo/Symbolize/Symbolize.h', features='cxx', mandatory=False)
+ if conf.check(header_name='zlib.h', features='cxx', mandatory=False):
+ conf.check_cxx(lib = 'z')
+ conf.check_cxx(lib = 'ws2_32', mandatory=False)
+ conf.check_cxx(cxxflags='-std=c++14', mandatory=False, define_name="HAVE_STD_CXX14")
+ conf.write_config_header('config.h')
+
+def build(bld):
+ #
+ # The local configuration.
+ #
+ conf = {}
+
+ #
+ # Build flags.
+ #
+ conf['includes'] = ['.', 'record', 'record/inih']
+ conf['warningflags'] = ['-Wall', '-Wextra', '-pedantic']
+ conf['optflags'] = bld.env.C_OPTS
+ conf['cflags'] = ['-pipe', '-g'] + conf['optflags']
+ cxxstd = '-std=c++11'
+ if bld.env.HAVE_STD_CXX14:
+ cxxstd = '-std=c++14'
+ conf['cxxflags'] = [cxxstd] + conf['cflags']
+ conf['linkflags'] = ['-g']
+ conf['lib'] = []
+ if bld.env.LIB_WS2_32:
+ conf['lib'].extend(bld.env.LIB_WS2_32)
+ if bld.env.LIB_LLVM:
+ conf['lib'].extend(bld.env.LIB_LLVM)
+ if bld.env.LIB_Z:
+ conf['lib'].extend(bld.env.LIB_Z)
+
+ #
+ # The list of defines
+ #
+ defines = ['HAVE_CONFIG_H',
+ 'RTEMS_VERSION=\"%s\"' % (bld.env.RTEMS_VERSION),
+ 'RTEMS_RELEASE=\"%s\"' % (bld.env.RTEMS_RELEASE)]
+
+ #
+ # Build rtems-record-lttng
+ #
+ bld.program(target = 'rtems-record-lttng',
+ source = ['record/record-client.c',
+ 'record/record-text.c',
+ 'record/record-client-base.cc',
+ 'record/record-filter-base64.cc',
+ 'record/record-filter-zlib.cc',
+ 'record/record-main-lttng.cc',
+ 'record/inih/ini.c'],
+ includes = conf['includes'],
+ defines = defines,
+ cflags = conf['cflags'] + conf['warningflags'],
+ cxxflags = conf['cxxflags'] + conf['warningflags'],
+ linkflags = conf['linkflags'],
+ lib = conf['lib'])
+
+def tags(ctx):
+ ctx.exec_command('etags $(find . -name \*.[sSch])', shell = True)
diff --git a/waf b/waf
index 0b71dc3..7ceee16 100755
--- a/waf
+++ b/waf
@@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE.
import os, sys, inspect
-VERSION="2.0.13"
-REVISION="4c5a17779813574907c253ab5418388d"
-GIT="f70b5d062efbceb89cc6452f7a7be4bdda854c41"
+VERSION="2.0.19"
+REVISION="1f3c580272b15a03d2566843c5fe872a"
+GIT="61ee22b598cf80e260beb64e475966f58b304d0d"
INSTALL=''
-C1='#.'
-C2='#-'
-C3='#+'
+C1='#6'
+C2='#.'
+C3='#%'
cwd = os.getcwd()
join = os.path.join
@@ -140,6 +140,9 @@ def find_lib():
if name.endswith('waf-light'):
w = test(base)
if w: return w
+ for dir in sys.path:
+ if test(dir):
+ return dir
err('waf-light requires waflib -> export WAFDIR=/folder')
dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
@@ -165,6 +168,6 @@ if __name__ == '__main__':
Scripting.waf_entry_point(cwd, VERSION, wafdir)
#==>
-#BZh91AY&SYqQ 0E(b#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+O>m7ۇT{RJKdzZMl;ٖ$o=ەk]d]urp"}sݽ*퓉@zrSfwke6;gy4/FV3{wJ﮻,=}ۼmLvuUw#+#+#+>ѠW#+}٠*ٗ` pӹGFÝP@:VGW9@pB@4*JQJ #.ԑ$P%Nۡ`1}vzl<i.4QB]]kSG{@6nWxw{z<o[fܽ{޷s﷭mfm޳d_|{s;G!լjvݚ Y#+YzW^i[kƎ"EE;yi@"R 4ٽ=y-ݻeE}ٻsQB}d^7v#-Y۽#+}wl.;GO]C:mfTS}RگiJx>=Wr5n\gwYw{Lq;kfz{ozWܡwTon{h7w{m>rmmi}*/gW^;m}ɛ9֯y1yNiNeN鼈#+]u^4hw\Tѥ#+E#.I+͜PPb[SwАo2vۨwuu.:JnmGBzn#+s#+#.ᄒ}V{3FD3o͇S(ݰg/||ٙ4D7}cɦ۟}}v*=Qwtq{oui轶@뮧c糾'p޷O9x3ۏor|Z{ٝݶcv饢 moa3e^ށ_lxa=Ru@ U]c<#-w{ǽO:}{W˰;[EP4ݬVgw{Wl7+۪5 w 8d^@WVϸ[>#+aJ(޽+#+ws޼ jKsvTPwm;gcM{zm{=ޖoug#cמ\|CM[Rhͼ6׽ig;uod}Gwv-]xz:|lD#+#+#+4 SO)OқP#-I&҂S@ @ &&T2hzHGi#+4#+#+@#+$"Ș&FSMSSєfL#-#+#+2#+#+OT!#-Da&<4)24zFh h#+#+#+#+#+2#+@jmOMIdMz#+#+#+#+#+ 5b&Lڧ750744#+#+|Uk"ڝ~V$&7ƫND ` "#.TT$ұ$G~esW{ZM7^Hx8Xi?f9ARRcqB? @'om2ۯ|X11,'!Tx#.ô]YkbZij#.Ay/J0uRf\imFZMcRIIET*:h#+#.H@ E@!aB6 B'#+,")B!TH,"j %ȁ$RlmDLB%MJQIh%-MFamiFhJR&($#+5,hLYSh%j"Z bZCf`33ԑmFIқ!PP&41SM͉,fd̄dKm4ԔlZl͙i2LPlFiE2(-QfR`4TH!`ؤfJb0lBQhdFIR&B4Cl !,fbD#-%llhL*EdҚ`RdQI&1 (%+ Ɖ"Q3)iS0MdlDf0RXV$$QAITBHh"D`L3 d0Be$Y55$I1$Y- 1eE#fIDٙ3bdE(͑&U4Đ)AfY Hlb#RY#bR"I&Jd 4!$Ԓ2&iESMMj(4FB1dȚ R%Ff҅&*#-12HH`DK$2b6I3*cl(#-H!Hزe4TE"I6H2l53ERͦ")4ɦd2,6(R))1"HQdR"(TRI$қFQHl ƂeFdMѱ2M&QLJlȥYBZ͛Y,I#-%#-QTD2H0RXYa(0ZҁES#- 3)1L2ɲ#)E,ѦXCPPLDԭ&P)2"+AK`RbH*lZ!lBFR04mtBT" 1QFɔ5&#--!j6MFDai1d)$[" -#%--4%ō Rdɕ&V[&YJfٱձbȍledJe6URdkdh Me-!Ta#-F*#.*VJ6**DZ&#lXF,V-X I T E 4c!iiZklhbV1lbH6ڲC!2k&*F$"i,VmRMT,6DkiT&fbВMe#-Keeb! HDX& D#- BѪ QC%M&M%@M)H6DVYFS1(RQL1Lͦb#.a@Ć1%bH#!4LQJdRP5BTQƘJT$&ѐ3j2lI(2,b"mԛ%h,e(hHf$Ѣ̦̤%c,dcR-F٭**SI,Ve@ LdblIRTC)6H)2)ME`0٤lfFCJ2m3Zȍ4*fK HAf4mF k4%1 4Z-Tj5bFfaB#cأ`KAYfd3(b!d2 h5FE U( Rj&cT#-X#-)Y#.($U2KD,EV(5&&e RC2Q%MDLTF*eEh˜J-%cjbdKd&)e*, H666J6MDb6IA2`BJl͉*eddi#.i&(ԕe$b142JԈieheji5 Q5%)Й̉5"1LֈتKѩ*YVMhcRlldU4Fѱ-3J!,V%b̨ڍJR1P#eFIQ52RVœlQ#-"IF+V)*Q`%M2J-ikYi*hemBڍ5bh(ё#-RjL͖TE1$I2##d2V2?dYRceu቞w#.}_Gv_G*h4i6|'<9n- ׻U6M#-.`Rĸ`rD6hz-hPW5YB!}v!ZE![iA,;V-;brwxj%lrrXnٳRpBє;KDٌcAd;&7]Wx,rG^~'Y2l>e Ś&U;1̠R!HA&&b{3(j|[FWAFXa1T#-or0#-%h|5kɣpvPR(b>+(w<Dz6"a#.m#.EJ9)+h hlO5Rg#.<f#-8ZA ÈA(>?鮺2َl(FG[ܺHsw^Qr車esnCQW6qb}h-_ط"_"#-֯|~uu e1dCm6WU\3}Hzeaۍu;GJ̔et9̅ο߿wu EH SC;]2qjłR~cj~OLHe%E&9`*8>x )C:R&oN P ;U6 ׽^6$Pfkݩp~7mx6Y}S9:^#)9:4d&/z]pfYX{CAUç~ny_C;7ktPL!dlBjnшl[p> F6K>)lSJE,DqwbUrzFɂ'Ջ4UKe#.W,j[Ll\ݡ1KF?oz"60L(UFxZX-&<U`2𮩃ߝQQE^7,jk;FyͯewîhF%2+y!AĦݹ]VPJE;4NWYJHJU.kؼǽzJT=E~ܷ^m}{=oشl|qcPi-]4Y_sy'|dYQƃ*9s(0&k[5gܒaQJV"#"Z6vW=NFy˦8CiZ¡CҏIuVV7SJ]+)/N<5_;W#. (ݢ4VYz\(E;(B*X@ENHQT}()AuNl#.']<I#V.!Fw/T c]9$ߤR֏&{|IIQALbVF()$&Oom#1GT! 11ڜas9@>T81 mjrs^4l"XܑDa #-n R|ȰO&]AKsS<xPD2;Sx+ L3gZ|K8ET,v1'J}|D"j~X#=#+pX0(~'A#5K~ؼB>G 9Ï<Fo9q{^NY<i6фDnx֏]QU#C;#l1GzUe#<l<2 XD*S2 k6߮zJ#4u茯H0, BT׾l#-)l> 7H`҃#- `8FW_nSr (ito)?ڦp)ˣ`Nѥcvd#-O 3aJF<!kYtBq/ydfD#p#{][(lZ<䤐=˝+nԗvBI$E*z#֛5J4/5OTG/8k3_THtqcTѣӤ `oUǼzbӻ+kn5hcmcUA1[/E_=yí_}7bd6CTEM~>}~u~}%"7^}n׮}-N#.EFh)ܿ+#.QR>W_uT5+'vߟ},ͿjңiTw!1i_9 Y$Okm_t:7pp:xI$`@bX+Ä\}`#-9hZ-d[!g#.?{~FT3[\Zup,Q9J}]b|L\P䋁x|;̴Ai ϲEE43qQHXM#<K}VEOg>h~;*/=CŧSb;:oQo[]˜4.&Ӊ-dO>|+6d2-K!ffD-Fsæpv^׍#IFg?9ױjn^ز,P}luziTYL*ʪC7#.AGTұF6-+ˣ;Y%Fk>ʪm>];Ef*vy5{f9#.J7e*DF/Jh|˕gDgkG#.lKOe(+vteFW=uv(Wؘ@D;Bǟ:h 3-F yS,ݏL_KeS#.oOe/`B<#I2-)pkhz 8^-u~9Ie1g&?#.]iT2.VVb8@cB?|49`UP6dX8E淥yCuv#Ra燐N4O|UBo|ܸ/ !*EޑR pP&|8sIwb8Q%Rfw|P9+*.s)E7WG(2oR1ewԦU㯒;eFÍ iJBD쪈+KBPq6UBDu ?*ۤĈYObKWTÙA>J9U9Ƥ|QEmy^/^V`{b**:`OA&v"z-G@^ %"5/Nb<صĶSbNʆ|gi<%(K q&wso)o} >:s|1G\ #-K<6:NDJdL`VYPzLot4ZR5QSGS)¡#-:\I)D~('1ΝKJlbhUlQKc|LQ1KWhI˳K|1~7KѸݜ߲8<գ`;c/9 YMh=SH'ht%W.7-oGoʼnl^Ag^V^z5}<(f`ň3obUTNXΪ"PeP/`x&^i,C#-$ e"4EC@fzՈci$hXz&B岦i gUI?Ǖ0fFfC}ᒁ"kdcڣ+|QKk:F,?:1$obzF.<8A~4?V]6kaҌ&eU WƂV"=4#.嚗8W"?X6ɠnO:'#+:Hb#rXClx֯_;q+v7 r9Uip#cݼζ=;:T#./x@;+[7ڎu{H~o#+CtTf(X&0bq .ṴŮ}.2YÎ,ުpIc VA2%8hYw"@OE5'aWǶZk=uˢRDݧ+ ³Bv3lj4XXh词5#-㙑v{~^tZQ48n#ܪX*&z`R5\mMf؏P2*BxNuJVTv;\\u]ru^}X Mo(Ae%kt]džp@$RI.IP)р1O#+zj{> #-hx=,U>A/ng(NB"[c?}I̜yҽ* ⧞p^16k{C#R6mNɡϲ `X#NӓVu3M%.lۖiSGtJ*Pg&XQh`2HjU2PWȰQ,ZQqLy)e8\](aqNxǝHL*ζѹQ1mfLmfI׵\|yMGm֨YF?7L+bj*tHTq(Ū)y{]XadYjo3I˸}WHv_GmMuʭҨ?wqxRUk4ʺC;D$V%,gڨ2sJUtWj[ZitYs4-@m-QuKq1>mnܸy0mR:$&+? a$uM}QI_#a3+˾0V:7ͰCrᆬݠfpOޯ|x=t2}MC!e߼Pj{81RAK]OlDdf| $LC)ܖ8L9˸"x s#+JB%dR%"iAGn gr/uos*]ˊLe09;^ӧ8X\0Yl}H>ƚOD1vlbݵ ϱPO4A|tQF6nS0|߹aޝ. ?/mǥe?n-#-ն`ە?PE D`|hQʝ#ACw:2aXs(HpWrĕ{y$f7.ʣ#+ƁI:Ѻc#-7=gX#-w4v_nM_ZUǜe(y;+ʎ3Ϫu:;b-F^&Wso>kכVnQ漪3դa8^D]GHEz9łC-rAG>BekdaMKou>4 !ʣ{f2x?] CAI7g^0vѾlvryE8E(1Xsk6hP#+fOVp|k{l\TXW8h[\E) sC_~_>3Oo#T49b\@(AZ#.?c(d1Kh쵀CC #.>ߍo :B_8SLoBgt#-tid$l#.<G$ ~NaEH w>N#+OBn3~$'#.B#.Z:lBA$ {|̓?L].t!ltCD(" nQ*5f#.QQxp~2Be! O1#+MYh2Q|qB":5˽[zӁAm3E+N]gQL\tTD{8a=4(/rن: q/L#+nLK#+Ϧ2xcsFGJ2>0E#+D 8g.toU6pDJ\9%${wt<`|d #+>בj>ER=S:s{xme<ysGen.ȍ)#Han]u-muC1Qsӷ$#-MkS=9,r#YU`<lk<1VB2!$7y%qHN?g&u;1-Xnx!'6U@*A١ڭ*~@,YA#+!UC+Rn#+ٜB~J:.8eUv41gLY.:׵M68VA3uE(C*43^1:l yǻKM#-.>(Ʉ-ehm$PrV(0,$_8#.B"x?uBsr#8I{3#+rک"#Q9:0Ұc`akЦn5$3Oa!_aUm-,[3ŽX?Ʃc)8c ~e>{ذ4צ[H䇭I $1𣤖%m<mˡbVfoY~_C&kpȈ7XA,BCGjlkLu#.Žx~ɮFx5#TiHsCYpS#+k*@ Iv~y|GE`û#p[fzly%v='M0цrP+?OʯXЉ1NmEPbAL3tGmO ]E;@X"ƣ0Dj#-~tޗ~aXp<M֢զ $񈍃ȨL>b.[D&Y+#.<=Bٲ&foSX4Ox"I8ئ2:9zJpSGa{}__%zH?Ӈ-( PKK #- *0pe7NFaעb(VÇRn0dy{}X0C( MuNZM"&i|ؠ*!f#f\]A#ZFNHDҟݘH{5׫3a]1","I3yCjE#+  )A(#.#+^J٦)X<E.-u&sWZ [iX!nA @/{>e^fv'c \3^!tQ~g`Ai$Υ-J\ Kkm7+;"z#-s3G+^w̦jf1s3#.Q}KL<!b\ESPZ#-|BY90v#.(PyC)((Fv0hhVKD"el^Jb#-6h,SBi*NVu̜[J,CsQCF7<M#.GH/k37(6RHtea^)±(z1Oߣ)vfz>=p9ץ&7hR8cMQϱ[yqx! C gts bM/#ЦSeo({Lz.cf&3<$$L"ʄp |A`Z;ToKM'OO㮆Mc^8x= Vccc2DIjdDlCdf1\ۖ%2m1|F4sʌ֞q O$5,)QQeU Y)cQ:ƆZb0EL`1#+؀=`N1806}>;5We#`ʣIE'i'tXu{]ş~D}bU*.Xaqp,zd1q<A}wɹR5DR]IoÞh`PPE8U&B@1Xqfo i-#.(kbr6]vɝhiD{#-*&εrRӰ8k'kζgraɚ՜?XǺ#7͚yx/c-i*hƂ/ Iљd;v=i5W#K4.9+ĐS HF4kqV(0id@#+奉c6PaF>Me8bL:H2T+LZҼ 6ƙbі@ђ,`]Q5C]Ӛꥍ 'XU0BLLnfw #dzRxUH`+Σ)gՂt9 [TZ_TxK6%"Z`Șaփ~d'xï_?Vݲsͱx D%(7v}#pnR{D݂iT<C56H7=:8dB1#+#>wFY*%[qFB+#cJX샒[asǗrșWZ86-3-fwIkR}3{ 4b;LA"A =$#Qai1γU@ZЉB/WlQ:je8i6;MԌa6P-nه_kB*Cx#+ 5\''dI,,st5ZJXBp8 4~;=RUMj( %kps,3;-~ҟJm+m\nP|lQkD{21M5(oPkH,@Pa;W&?낓E7e8//yQ9"=r[0J&d"!1Vv,G.L@2,*Fk$f@МYJEQo2@CeI7P}и EiB0喸aE$"QDF4Y#.07X&jסv#H5LD`0Y%||s34&#.)8CvӐ}mME1w.#._vtgD|@WѮ #=ꆈ#fɕ\l^&ʑsGG&fg_oM'x_kb1%Y`Y#.FCE.yϗ31D2lyr#Nj6N X7(It NnXkz#S#dpK+/L+2Zd|PNYַMB|56>|lSeF׷'g&pt.YP̕,e6"F&cQP{FjK>7IA0iI v|S2q!X<ũ|ڶAKĞZ5&t,܂>lkԢ\U:aю;^>m13g(ʔ C\%ۈ_nvj٘Q3ͷ.LI(Vj(bV.va!{eQG ߶8xQJ`r'ߏnSaD$/,W?yW32!Bt3HZ]V3eGY`I=9#-[9WӴfj5c%-Zd"5h $1[!PDo[H#-Yնpu=6ƎJ8̳Ll/rsN`R{Oi #+#̀#.#+-o2ó0^-`oz9w~haaΥنJ4th,~c-Y@P|-#y`,=u_<<w#-7?P }#+Obb v} !>-Xho[$n}R iVWX)蘡-ݏ+U$8hX-1Ie<ݟ.դDl1fwVfe<N{W/`m[OOfuŐo/T0l=Xi!f`w2I@l}Ӟ=CƼf,Ie@:FPA|_/6ĜE2#.U\Q\V,*^)i#-8j $h> ;?&/ZIOE PrWA*<8{=3"E@T،;ZmId& 3eK8MlUvݨYn]Zx%/J#-%{Ɩ-1xX"I[P?9eMEզv Y{(A6Rܷ `ߣ8?_lSUXdT>э'YGDaG,Mߵͣ_yi}ouҮwIy(,LCm$moy6zޮ뻲F0xlnm2Zpe J44? g?s &OMfө͋ԥ3]ZK`Jԛ]KۿpԬ~+,G/%@F(,>_*3O|.rȥQK*4aE&KQ#.<_#+*0QnM72(DK Wʿ-z8+PŌԘXsyDm!CA #.DO!#.@J"@0RqA|0v=^!v/+_ʞhr}9?\Q43ǔ]{ >24kzr|=ob? Ts" .AdoB]J|,YHr.q_y%G]<֛,k#.g6]KW;=kkmw:L5t0ݰH^Go* $Mp$|#+X`}KޡpվۭcMo#--@=_3'R#-נSA" ;;m} g=57)wׅ!Up-Ȁ.hyg$jYҖ~{(ꕒ)X8R}ypD`UIyO%A2yuuu@$7d!h&v'?\~[pp /;QIu~6ج[w_7tHt:IIi}Xdܪphм&>O]pCp}!8$:<c#-mL[rbBcB&8A{I#9#șx?"8!1`#+<~=SP_Qu$ItW~2aDaTdMtL{*ZCs߫S3KY('3 b<El_|̈g$d~ҧUλyv=zOF,r\,aIl#.R@%@":gv}J#.# .0xYtß_?z~lTG?-)Yexpl^4#-Y-{\jަE{'ݲ*g;u#- ^W[*%o?]mR>^/+&v818;]+V">yM?>7(X.h}zgُ4{/FtOW Q@2}gwO><)1_|M?{6HKD02b<w[(ml^趌z׾ndy6elDZxU>?'9ax#r׳DncYx[NsJlkBwt5zewNN6~_?TpP6rzRYYxpa~gG0ѼB.`s6!p#-9*s'pr|aݿv&cB%onűۯ;C9`x;t,d7p?sij!|8*? v{#-O|&VjSǷ ]:{&ҽya]GqSPdF#o#- +gi΍-a%oR%|d0?fvG/E_a@ehƘ~i8bv|{ar#+ʟgw6[qH~*6+~?UoЖk?n:΋;#.g޻}I/F^>CEe8{yM١A}ّ^8(#-Yܿ>=;w\lɄ_g{p`<px_#- ,x+Ͷ3gsqO>?nmUNӲ?Q#.?l7|MK=#.(F1lM!7??oV_E-{KXSW-19L9atYϧA?>ʞNW9==yD`wu˱Kywp"TF9NMCRVtN?z(3uX8htZ}]#+8ÛPm-΀k6#+P$AX߅:m3Vk#-ttw}90v1[D~_ľoB|C/ G㷀~KSw<ʭ+{#. #-l`0Nᙳ>#-.sxt~=_nX]>;;.~g~SY%ςv4Q<_Ȋ=ʣxf/ֆëGKDj`,*) sK#+#.1T'&DkpAyte-$%vq5^]#+ |Dt+ALDм:ǭ}6l+7^8ClRDu0`4#ԈpxM-.4qܻLI,u8KHSgF|k }!h; ->B) .j+J"sN3{}mXFBQ9\nZ䲛 c"1iySjua(h<PyB!Dg<W̺JpޙIUm6ԕ!9IVtg.)a4p4E\3psƏhǼ3xYL!$HSQlCk6ZT"ȰH#+AU_:gaˤQ>#-mҶEvujO'r9ާP@:o'0uof|v_]5÷?Ҥrvwi Df&L,|3a>.vo8`wFFÈO~<ڛSعh򿻏a55X@ԧI;6>bKc!cp]J0N/#+nń9ԒWC-gTO͒Nl"<}QJyG0#+g(뽦5Px3>b63'ʎSJ3 sr[#-A"80)zcZ#-*mTMAUZPqTEٻSjMYf:4" %M-AaFd&B)cAEeq:<J°Y7`RUT737ɡ䄍ОV6bwwȋŬJ#.s) f- [z9 ԅaD."UӇWctwwg#Fc$ (]-&2#-#.$jQQ.ʪUC,o[=W o/Í2)ܙp9F|i|8Z4Ofc9nˎ<~ǎouSt^F/}~\/C˟Pn;?g߇G`DCN"oh1VU&겷svPPaFO(HAÝIuh?oFFOqD +#+pjmpty1p~~wu~uw9\3ܮkN&5)o3K=^=\4ˁ6MYK &yGWXR>/xgTZHv&b!H+iC b뒞v#.hQ< 1#-6rٔl$ݻ氰ģ#.qU*2Eb" \YJgh8>J֡@Yw+x%tV.o7s0c] D܊#.)F il`,NWM11fD3#.ΕE> ;)ms8$|HADPQuk#+a#hq'Fk"#-LY"LpH`cQ!4sA(dU9Aڊ02묔b)]aIr\SJԷ:,ʫ+A)d,=%ʪr:3=Kay[̍H^0u3D?Z){z4f^=釳sQb2</(~Eaj#.Ϧ(&rrx xLbΝ-`{?V?6DDN5ꞪpoNZ3[pYe&~?^+"?ӹ_u9śꏢBoԄ ď}:MHneCGփB̺RI,ʪob Z@K6XmB>$C!y="-c>]-pO3mdKOmSdΌIYnFDy9NdH\S]y$$%kѫ?abH;7!OVA%A*ou6#c$#Ԉ#-DF]7hGb2#-إUg/Y_w{H*߈}</RɃ(@*b0F3\yH9Y~ i KCۦR6kZ$[W2ajViV3BF*F#-`pGfhPi12X_/^F&rdC7y#.ڴs:;|1z7BPd3Daʧs*c#if#-U`FȺ% jEfޭ:m6,deP)*(-&2ņ Ih-mcQ#+r0I$hu,C7V2dJ)6o2d9# aeV+ sF ҁDoNМ#xaAӊ7y=>PJ#+B "ޫCoh\^*x~;#.Dq1)DK߾La#.TqT~=1}{rԢymG;Py} 9#.fG->{~XNǏ"#,bŅUOJ `ӌw=سR:E#-(Ƒ*tώ#.Ԉ$dHCߕVvKi4=9E2C,;Q4ޝ#.T^Ѭ#-#+i-Z A8:plnV#sWJT"9m'f]5$<BPXY[`!<ޫz.068.Ox>LGxe^ڰ!)P!Xs >ٳA"h27tv>+` ˜PJ*Rrm0:1L$<)Ʃi].XW-pJ#qGG[UQHiWF㐅.ƕnJ,4';1ql %Z8& eXP! &b!`#.<fjX>[-#*m8V6>d4#CVKAغ&F ᦑѠ+4غՍDDՎ8/GU#.q, ,aXS>@8ج#-B#Ȥ$cY^x{4v'V&h!ێpG?:u1Pگ97x[ie4POaGtuΙXByjBׄ PtGt (K;63Vݵe1lrYCW #.C5@,\zo-<}颢#ҥMLsM٨ۓrEJRcmV0xP$;{-x@kb656k̓DHiɤ)08R ()#.Z;b/ڡ#-Ś<N=,9>{#/j˅ &Fl8SBLRr8;\;$3 #.|_am.6y:#lf=\?#.-9}GiYN0:61%8t7c|#y,#-XmP=3vtkw.Z_mwj]w^UuUo UL]Ft{8n"wQLp8 l@A@G!xLRx-n:>%!Dvx(6)vӽ#.6ڎ育o1,F-o#.OX#-.:1\c"tb39ٲPɚ x٪]:l' l,a9ƴ*l9IB\5]´mA<kRiA2=%Ƭ ~zٛ&8#.d`g;pײ#M:3#.q6<[e9`kz9rqB17׌ڶ54}DNDcbH:S[tOS TM^"R)ȋydp:\&0Dt蜬"eM]4N4:!e`s. ґEjR$x7&HLn,G;n|W|M2a2dx%2Mp+)UQ "V94/HL15l_3vODܳ,V~5#W'yDCTTo896Y<kIYNE6$֮_}Ҥy$hl!zY^f=M0Er3o>Rnz*}˳r!t)JL/X#-#H˼ebî̶GQQb;}ߣ(Z\r7LbOY0ݦ2Ҝ!5$Sma%1#-'t~juO]Xgq$q4{6Qx#(Hd1#.#.9CtۼEvLKqF}x!zA' ;@tOM"BiMGS[8/F G:ix;$0@S:Wqi\n.:|Oz&c.σ-8 _1$ 2އ,3ymDpRix3*S?5x($("B$#+O;ﻸh5Vdw#.5#+CB> T]–+ b,QIZ-p6,Cr`i ˘Ϩ9z"2év0ZЦh⃤֪xΎH.HZ.Vvڂ6Ӊd)J{ϿY`??䌧aW9vhp`v)q`Jgt#.:p82f*)(~x"ner(AFKn0mldv,rb]83SK>&Iع|jg}'H[*5WJB$*dYd蕈ll)Ң{ #+W{d?n̅.I,7tu߮:#b'2H۳fcT7Rx}z6wnLIC{ yQ72I,@v&I57#+e4Q&ow^I<^h,أ,#oDtv 3Í3>+)ښ'mFP-twi<jif#Y kهːƫҜZwO6c3c9ukR&[9a8c7KcfBed6} j+Q)7#-7BlBP, & m ii@'.s>3b8s&ZLHDa1AF1Dsay|p<3RtZ{#|7nsWA E7yCw==qB;lcG2ouK<@;69#-we1`ޚcrB-Nrz|xgdž8Gn,~JÃT/c3~4^~g0dDtKo1H%D<Z[z%nL9)9*.>urr7nùv,-uR~#^VzQ90mry~oX(P"*>mq۳2#.5N{Ci|=u=:mH3pMiPLEr5of.y30*+YQ_#-9YPEl&G>,G]$導 a7R#-V ]m|(x[K|t}tJŠBӵ)|T<WyՒ'lļpHUVoec3 rgrܣZsq}-nGcqb̸GE4}+X1Gy/'F>=} 8#+Dѭ2-<uCVqK-[r~<LѸٌBl$->'ni;l3'h\]Bf~#-鰌F &hւQnn>F9#L݊Y#.e(Go~#+{;!W)הLkGtz9wۜ9<oqǡ]QmֹsɾȣV;)渭ѧq|̘mI0RONlH@x;3Y 8i;fC~Am7 aIR ѥݙ بzi=B+ryRo“}z7ۘŹj٢J'#.#+ 's(<Y =]*4l4xAyB9,_sqgCQ6z)~!/;cjrlJ{0$v"W$;\TTSGOvIz#?q;4th1']J!KB2^fm2&Odxr6['3껶y}H|* I8Ť6I(uL#CcDOsߕ[r`Ȣ#-v"jp~!׽~|cWUAtӭ"Weg4%0/1a)y\";:U5Zl`ֵL#."ZɎ `7o'nka<Iũ+gǐo="i\>fҗs-,~;^PA2!xdֺ.lg@Jy +MqY0.UiEXR#Z7;CBY\k,!赮3hb&+0,U{0+5_2PEi~ahj0E! f-fnhcP(]:@S.8kzQZ`lcϕaF)emS +~JN*\l7I֯nݣ03׼+VjRCaYcX|l;T*5Ug6-)d^b#+[>Iz]k)مlrHOYgYؐ@)yWn|z#˖i0) l &61iodNF[|+{aR"b`GTlch} Ӥ SJeGeyT\/qu:0OYwbe'6JݫXη;OV4Efkz/T#-X}փMU$Œ.`U<EfI@u(+Cˋ;k0+ŒΠcgMgiy='{bh:,ls~D2xxڰ/rVzKi|(YLWeI{6{q8}%FiD$Y=DR4)vB%%(el]xQnvDsG_LvIޢ-Y',cd]n#5= [SG<=[7gt'P98ZX1->6NT*g[y>l`FC5. #+R:Lq rV9A3$ۉm)8FQa׬Nޤt@x' #\9?&{~8\E]^%jx]mx\ov*$ne@,%7ۚb(oZGNB3Ks߉S&<osvPvѣ:*cyL#.F7>˞a{m=tt֬xeKW^E$#.rlxNzf'nұOɼcU]4HKjXPLQ{}|0l@NM0Ӧ"I,uc9U^NLrlͥzms+rL`anLu~8 {f5f|1b#-20; NۄlRi߼NYX0 +ZV6A-IW{G)څ"$u‚VWҺ%ÿu,} ax«az576*E$ CAlBIm*ҡٕ45JjZ}xHZQ<QڶI*ȗ~1X~2x\01X ӥ}nW_9Lu߿=}&=}6S-un#-E]ϲ>hT3(;k7FMh6V1hfV&IN͢zKJt/)5zэ1:B1]bnpUP#.`^FAښ^{!vN֍{3(3QRTb<X0#+)nbOSXz#.{c,VȘ-cu8`6@^^)~)/ћ3TI*VDr'#.KP`?ޕr']i;=<ȫaB񰺬ʹzBDC k>;kAxp#d@:l]kBfH9۫tմtNgb߱#-<ȾNS8Eo[mX{<YEݮsg%ftVb>1R)ڦ3u}\p3bWw!yiJ#,8^ MCd.-e6Ox"DYH]n)e^튶OZ]nH"aH҅mgl6WogWU/ KM)U;,= xSg/'Ge kyt-U!Kc R^WX+ek'!r$FFzE\3#-U]!X9;x5 }Op#.ZԵ%l\6ic-/y|yLH.{,%U0g߲ &RA_V?G[RqG!"bf毪QWeӫP&t+~ՍoTty!MfZ4@˫t:QɌ¯ֆs#<pЩ՟0ML#.vrD ;cN>T#-Xf˭,F|mx]P{ #+#.*6b٪rAcVhlױVßMG(!܉GNkg†?EhrW>^Y`!.G?t iFI_-t^n{Y*BVt%]}㌖\{8WKN#+0gW<DUsLnN.(]e4tZ6 y:翓C!){qbSrն 3;qF'7qvx׋#-t癮D蹡bѱA;\d@V׾#->R~0PqQ/S=y|,:++e{PJ9C!9B+p,z2n@}+*qxb!)E|2tșF`Z*^<@Ԩ(T#.)3ƛ)%v,w:'B^nC`-.cYQJaźb^P|&B#+hCFΜ4/qwҼ!`*dl*;'_V9|=.Yw0=]Q+$!J.(}n}*%^Ԑx7&CbBMF`pI{ <#cP#-T2#.93ܨR7Q/XsRћ2*dz2'iI]*̚sw3ñmMH;f/ j K졤|a%O9)#IӦ̖87J'8)esxt<UFI#8{t趥#lfVno3׭XLȨ~W[ O񮙿 4t= dbu$!xώMM|F!~0bG0-LG)=b-#8YM_W9k7d#Z85J-J:Lg~k%<36$S:F.5o.}?1mcB-ո@͊`/ſĒκ.n#.,#-/ߌnW\R\Z^fg~A-w{@!28BV~v1:=c̢]X;am+_:E>Hا[< ٧*Tv"WdM*=Z WxKWcUb-[ 3=<66M1@{ݼ"JHr lx.R$xYG#.N:W[1j3nV>MJH~ЫWttn?-{-v|5Jc-]+ێ<nS7ֆo&$|J#-|a#-nND E-n {H0њnd0疡Zl\pv}Ӄ]Z'%,tF._\T Rz |'+mloE}\p-α=zZw#.x%vZd;RǛwCz&Ď9Ŀ#.-W#+$ԳI77,T܁MiLLlFq{ -79^rկ>o:sC@iߞP8Àm*Z{#+P=F6 kؙЄ84KKKZFoགྷNH>N2#+x¾eSSBMܶJ1B{\qi M̭3\Ð#a^Լ~h H[BҞ:3v0N)`(f<hhyQASę6tU_jmE#+K9K|x=IIG`Z|I!<j{]݈3qێe'? I2e%VjG&>Jl!`y8Fr;/}8})h^\J#+)('EskIh% 'Xy(\J[ T[|lO(B 4lJE6ɱڐPC٠y19-#. }ZPe#-#-0(#.'@w#.b-}X6+)kj0zϡ#tWųe'2?0ٰ}Vw;;#+}mhQ1j] a"Mpx~HwvZ)TmWe_?U0U)#.B*E~iTY?QBc@3B&`:'$䲏QK|wOdd',Yd TxT^Nɵg޺9xaod>`7kt,Sco&9#-R&oBSZio388ʠ 0A\>RB$y*8︐,=a!L$?22╚d2ykg!ñ)NmKN@ *2(>|zU\J %jY9B^KNظ;/h^Id/q? ꈇ2P7gxJ=Os^6[߯juD@@(Qʊ#." C!Q#-֠#-7t35׭(S!Vnp#.c!Xt I@ܺ`Cd!09mHQJ#+=wm`<C(ׯ's'*?ڧt،ğhi)+ *CFtFɷǻMwJ|z#M5ĽM.Xbʙ?˶rHC{vq =>g<Xڨ)&XAńSIƹ#-Щ|no:o ݨ U}뮡gCNER ~#+_{ ܤ#-׃S( > |:^n!-G/w,hp=?æGd{ƶgVP$*RBԬ#.~xͰh7(Ѭ` : fR@.Ğ&EN8፷̡ M kZ?#-bRf%:]E##-&؈g"#-ENI/xk:"p7\_N[5VYLiϯNxH鮩7ɄA=,ƈ[#.ǡ߰#.PIwD,݉5PgP6CP#+|P,v4 Xy&ec(>ybQsp?t܈;4JLm?ČT1>w5g$&QdT0RrMAh#.P8@߲Ry©,-ϫhNBGZg^]T C,c tbO =6hzh &0xy%NǣSRPvl |TG}1XuqGO̟O2p*u:X]`&#+(fQ*o9}&ywy<*O`{gH<L'#?lGT'NJ썼BN3$>q64Ҙ#-" k)!h˜&woĪ7bQTxCs:i8]m@_$ULfi#w<B˓Gi'LHteOSm(~dB{8<sxX0%VgqPAE@Z^[- 8c` G$%'Ex׹vCTvaP7M"KyT K|j=)ߑ<Cm s&vH;apG09Lp#.4}N{O/#oXE ܮPءaYuD\[Sm~zf IQMl^dό3'(P9cņؖEH5 #9$9>ȶ۫ڈJÛ%9!}I-u<G^#+z|*E8iqIIh 2+y6R'qCbBB#.\E&e.󹑵Vިqp#+P g^\ChuAQ4\*V"+nmor!xBq;;8]c5 ռL-8#+]~)TH``T) ⢮A4G)z贇jA{8mFk x !"Q5GA Ai@9-ƨS8Kxj߱ l⻯Ҕ bēXnR[iZ==lRpLn^|ä 9޼#-$$$vQ+F@j(aoa-3=+{bh7vk|N~sۡ(zbJ ADByqTd{wI f3HN%3z\#y1O}uSrvROhAl<g]#.3P82|5[҅N훍fƔEΜsXebv/mi'I!&5bnwu҂Vḥ_Fm?|/ ?.1mgn w,>ŜH@H0КŽlׁ a_$!2YgpA=eX* rMQi"4›N<GNra7N#-r3On Ijcqa-Aѯ(%=dLʈWI6!bS0B4#yV%TZIR=~s!.VC1iJݻF@?|y%#+k/]q~ڨL/!X+} YgAUO }K~_l0=U>V Aȋ##+?-?Nyh` ߷"mP ޷=#+WկBk\Kp#.RєʤA(!x/^ go*|ttcG1׭Q8/U4JH=ƶ__[W@*J!qTSlcfVy8ON=BU:Q[#{˼%G_SF:6g?ӉP?bc#.](ag1S@dnuEAR '^tM_~vem#.x| }|?uT/쳃/o>}snCS#.-0=gYm3`I lE1/v,R)E9d.J@聇H{XAȎMlPXU u_`0B&:ze#.!jRX̂ pv\v(ݠF:4 pjta~lFɱ(pec1tX#ÄaSR,%Pf&DAPC8Сm<Or5xAK1 0%T`9]Ba@3QO1w}XtU"HlX;kOE~BFa#-NP'NKI^3WM!+E>TslY%i<3MP}{-/2GG"~R&(`;"H;&hn%|7"onrjRKr'-GZw~fR댫{N?Fxզ]R*8 85b12S3SX&":0;5F(z?q|Zb2Wvεlo/0Keuh! A6`to(E뚦|lg,?5>Y/wRyHC {sɸıX%hɢAYi)TZfJbǽc@YET,۬n{Zkl#-ȸL0d} !{*@#<2B(SY4fn٠dDD068˂8DsZb#+bXbi*Hla54+͸t:݃\$"8`Ce 6z6ɤVHRZ8jQ#-u}*M1q9Sw+ :lιi#"zP-LBL6{23xXb+=4Ԧw'=F}+7eVsFֽJ6UmF4 H0LldD##+>OK&:)6VP03mC7f#+u$7I=i{Ԁfdy5(r.oE-FNO#=Nɉh=ɼp[W" Ҿ3q-Oxt3|nI'40U"2"ȱ2ڎ:A#- cf#.rdz Y{O#-,5`KEM=P\FfjtB^NâB*}ؠ%ͦjBU[[lmP;o}Zqch+^Fu!Z;8>2un#+$%HUV;0 jJ2Y,Co(#fT}f/$c<eCP,C)7ᶼ!^\< jDi߻쌍aDKJ]$*#-'=wwxo`bI iH@wS3KI1ImNrC™hDJwYP#-IBnjY;F[bXQ7]F<##+gGHKo pEM^$Q(PUyŃR%\3ח5])%.2ڍOT۞ِzbBǪHz7VXh09|4JIC#+D5Xc<uE@9x&ID0ß3_:h`o^a, ;Ηm0T&S+zG#-uBd!#-VSzfºZ/&96*)#.VdMhϧٚ=`1Q.ܑZ-/L_Ef6Ց#.B8EtPdK_o^ˎ˃^2u"H#- #-yf|w xc`=y}۶wkIdC7cSK)qI^/URU,E6,Ax0qϢApg)740وDf!Út0i;on&gi@(ZAXAT+p@iAFL`u ܳF#4}hy-:6O#.8V<4:UKb4'=2gBj\4Qv-.EPP`Ȣ h#:p`N^.vw:WjG;߾)\k̓o}単.^L5@,t6&"oLD|4$Zt1Q̪#+x7/03RdíYI;6e9>I`;f*h4g#Sq2GVZwe H{4䠢ŗ%d(ɭ#+-T7H' ׳d7#+9%úܔ #-cLBG!&ªqM~&'CڙHxGށvE&ҍlƖKQn.1 S.˩]Ma)UF[(8?BN ́ tNkfz`lz4t k1MV)YrrT !#-С-,>)fm!=ࣔ A2 !]7oR4Wj>'Wmv[L:!Isq&QP) QOi2e671kG:m:"$l=t?jC'!Ahq{a/o?0bj{hߜ$~/pyC'JZ&:yȤ#+=_FMQ p1Hw%<Na_ :6e}v.H'Qx#+lFYdĕsഥ,!s>ퟻߟByfPa欒)J3NXOOQ/ B(҈99B@Z+q.I'ct#+&Y#+& TZ^`89ZLWH)G<#+14cI~%C~*;}hzG|N2\cımf>~t}%Vj #-#+>Gi;owAW?#-bT !$d$K/lf01MY}b\Cak!)'G·\|>0Q#.:[R3InUWHX x~?Hlu)+%uEτTW/'I}rкng=Cq܎A" ˿0<6_KX6YX|{53ִsBf/ V_Eizu#+m'd*_ n?d/y (2i /g,y7yQ(gV_EK}+skNmb|ȱ-%+q3ļ_mHqQ?$Lw! p$X sZDi3^ A j'=#+Je}H-4%FGӮZG?pusj))#Y UݳDz#+[HZ([5l2R U@ŦcJ?C|e)0np?8C>uw{ =ɁJ2sX 큫 )TYQvO R -"%D41!x~'Q'(rXXmiԺ*RH#+vwm͇{l2i9M?֟a4C}$IJҡAJ~p臑*ר)' NZXV@Ok|eM~BX ّG0>o R9{r9?*%pdeҹQ~e,#+6/0X1y}rk6ֹ.'WfgmLJy'WGzuA|YW9{ J0yoý?gHS2#;våYY?T$9q>rX<k(`Q43<hyrX*W?QFc+շu<u>R|9=Ab P#`Cq?yc]ޟOЊ08ʌc/jiHxj'tL=""ڭt ЩMwheww}o3EGݛԨkVз+84?-*#+(<}vH{F'@٦ӹ,r3?#1\gi|yXuY#.KfAU A~X*pn`$s΃(̫$Q)?/6і(6'&#+_ڠO2]W C (0]6]RAY#.xO fqB|տ ZBI'._gcDoK˨QX CqQ-J#`1}A982ei(r׳7ƥ(PP02,*K.{hLdyY"#E虊w9:K+8'x<d?'>gw=:JC?26`3q9J:CwHE:Hw+%J#+*%v؅gIԱ{_P{.Ϋ3_!_GۂF]"~N?#+'hPz$>ct ȁQ[@t%c]j ~u VDը1َO#+Qv}a#+K}tH#@=矲Z=ϩix-({DŽ`k=2sB>?+^rq|z$C<GIחp!Ym*Q=rhXҬ18(8(ə"gnh9YXŭW`5(xwlUxpOV3<M%2WKz=m[&^|>褑w.?OkӢ^޵Ge_Yhbci3:5}Տ%lgHzlXuA_Sw6q-B/LIJ}Qn{a46A\)@vp#+*<4 ݾCHcjꡜ!'7VuYdܟɷs):Q#+(ū+U0($ߊ&*07hXИTfPsGypv66jⰌ#+Sq;D?eVǖ#.ߎ*Px4GO./Q#.W"ĆX55#+#.g-?xL)i]<q#z#-H^ :#.\M]Wh#.\Xcq[%h,a|6BI LqK:bۢ #+E|J^=wqA]l~S#.yu,Ϥ睴Vrr,rbw|CY(58qG^zg]nuOPc3N=| d99}Zb(vҌ8JϞ}w7UUnbNѰN]Pk4]#+0Mu+@_vҪ #.2f 4O~TRHPG`BMsRtR!/ !L Du{6v8szugP(5(I}b-uQsZ|^Qg3ڝD8AL$P/tӻn\plĢ.f=;^K?Lt]=<$3wBo{\lJu#.Dsw}%t$c/wG*p];z5S=y6~!S1 7qZgPrӘXw_gqf!|UȁL>߮cA*2F>W祾YK]שsa__SRv,A7#+w.^U#%Ql_#.~:WjvGNXE2h11BA8cF󯶲.SOpvX6k)F/{8J{~2K'o_lx+>>3^O'!<vr׷͕xY.EdjmhgPȯrpGL=,襁+o*#.dr6n?&~} "6k2,C5?g9Ԉ^n_$Q>`/Τ'tZATr+aN8B7볆 1bmD>3Դt)APꩨgG;hlDjEX3ySQɑ7G`%ϵ(V2Q{b,}A1^82\eoK갆N0ȚjОLRM)X|}W>C~DqC%q2c#.r@GG3#-(o;B,)&##-opPJ-&4J?}Zpig˭NOΚ[x|8HxpӞݹY0^#.9_8ð^x'"눻Ax1_RNq뢶uow|P`b5.[4>! R Bx<1q$s"ԞlBs$ឳ\PRE~OSԤ^BHuGC9BDfýhZOT/R`!m&~ PstX6esP+  Hz=V&IaNŠ )š#+#. `Q#+)Tk1 x܏#.ʬ@LCP#--Hywd/m*{@re`GT.X @;#.{#.z^v=a<h!x!<9#-cv89SŌ:#+1G>m=><,Ԯ؝y p=l/ B#+qP5zke$FE?)! {?E9$HR~ (>>~ˏ/MJgk-iFdgB<# PxCvrdm<oYyCO530Z?v<^!Iy]g} Ǐ?{ltj~vn|R9F7t|(d#. T_AzaG0 aR r,FI'X"~H-9"9>#+#+NGC}N !„0 ՝#-?V!FuKA"20F$[EΜL0@$AΞ|  uv)LU^Hr=O݂[qvlCq;.vO#.󗤗ע9ϿN?O3(HHMPef1A=b"8,=lJ^. #-5Sg<c>-"WY`OpBU~'|nCk2 #. #+6ȃ^$H4疗.'8F|vs(v$4#+=M<IESttN=]#-kݻ+/uf@k]fJ&kסT4ߒOm297ü7!??ŔGg7nmW]fa8quW S0;>P[:/ `Ic04eTԪ'C&y!vQBTG-c( )<1`*ꅉJT-OjhDpǙPM<Ԑ+7vm@H՗|xi#-x cE"rme*n2A x]spp$*%~jWΏ1 M*OZ&zT<YkO96huHrGj~}G>CrbRar?`aInljgܣ%?>GRemK{+*Ay׮,MlBh8CaE`x0y3}mNk]]!c+|]3R|ש%a0@~r~~v\/IϹsS05Y[nz]" 2`n";kht4:T{nNͣܜslYJi8BʇhLpA9.|b]xM~< .|q񐄎NͯopȭxyK0Rt#.z#̛}.=V%w\'(n6H&Y1NKt"y"%TZ&riqLGQua3. CnS\Z@ҰQ-/g-ei0:E#-2mWՂ-o9Wkn95}AVBo9%O%876ެ&I6Szn ꑔ)X*6)gREv@=҄d󄫚o9}}LuDtX{qWn|,aS4?NBDLuwiRܱf f }멑Jk)GK0'ިTs6kO6[C."!?(ݷ#-p!C90k5,xgD!;Ia[VC#+@==od [JoY6%PecD!ib?J̌>gek?[)̏(joY=>kHmim.3;ånR?ݫ!Z9m0wNCXю.c &-q7ݳŇ^Ċ##-$JL,rSO.u,*YwtS,w3YN#-<:tqZ=/%qulv< #-njkxXEE`>ϟLCLǃUcˋ˓NσL)%gUJA)S̞[~'˪RKaX}zBl,wl%%B6>=xe2U~$#+hx{-s 8_P n,ߓNCnQ35.$}Q"ܘF{&T؁׼& d0JD p᩻BHod#-]`N+bvF;/vmf[is$PT.nT[])txTR⒵B:CEؿ{PG6l]~X #-7X:읧9K:w_vsvT ^%0umॏQ;B fPA]@se<1dllDM~g0m6%6"Nk3D̢TuDÚNe8tF󱮱|gIۦ J=A[1ӱ\Dv\#.#-JUff"S#M<kdax5 #$yτ7w$~_+䂶@\,#.$#+V;fr*6 nh=#&tFж-u9;oȒIؑz?o{0OW$m#.D5(чDh2u9/oȈPA\'LTPTJբ.Y/bj<{C#+#(KPTk zs>|ꥄFeن]:EΊαhB]n;fu;bsp</=sH^L7t2-v#-U9)xc| v}x_`Vb#.#.ϟ#bI~*#E#-4+h:-(9Jdh6ޜ44m3r !Y*5/!*k VDCyp;"n[z@2b}ֽJ"Xvh{Xrf4Qo-\^EH/A_13.-"ЇTquL=d+6Prs%:w҄MG'zg\.e|J2t<#-cep:3s 9-  mhŻ]xiQƴXǔ8]T.k#^ ŸO/-R}ʱ#+c!N`$NY~Rҧq_6]~/\#.lqvH3R*#-65՜܀ax Q&5Gmƶ\ol૪&E5AҌ#]Z=A.G@gQAlDX,; < VgFđ”w`Q#-߀bzaϝm~ÁŲ̋z'wk<za}k媁3 Us9:9pmOKÿg0i{gќ^G:f8tPQ#-}B;:*pB>iT,*e#.4x#SD찯gqq׋_a8#+I~:ڞ'96`|6-AfwihщNs3Kw_Nny(~]``~pahofO3AN`tL#.29?pǵb߿/`uFmU;JE#.r [=?OCY#+5236j7H?亖A@mU@GhB;A(brE",#+B`v3 y #-pl[2rWb0}Sbfӵ.Atsvҡ2l $NhD+/Х*M fQS y5G{ڝa95Ak j;*(p&eta{?wB`6ҵ #+}2O@uA( "xwpNvO@gduZ%ZLqӞ Ś-XVO~&Yzz8JsA"xIį##\?W|mr0kmd#-@{*QNvox`nXp&=7in?u)2JK}K }ΖQ?%}HG2dD$jy;6Qh,B)Սpm#w#+#-@Q8S>V( CexN6y_φTmhϗhv:=s96ܴy&j6ԇ~fyX["K dc&X, Y.ׂҔ`B/X pw`Їl'#-ESVqgcuaia/PGi~AZsmyWM)'Ox#*rиxS*( l,h b+0>=Пc~9ـZ߮ $2P8,3X%,m!ב`kf$= 0!Kz8T,i* ٥[(E~C45EdO>~n<i)X`|=TU|-5#+mOJe5;aZ,OcB.Pkv<M`\js>/.|DDb>Y >ܗ"- PN}s)UHq)T* *D u77SHЧiceXk`24qY98& [I`*tJBeVIyn\=}μ.{ =tpj2 oᑔ6g35Mõ,'20=fH}%?a#.Ͳ#Wc'B'J_LV]Op_O#.Fk yl]^a2p~&y+ #.f!]#.;yX,juBZ#6*M*3pˣ\(2mg8Vm6#- bhp\Ysl HB6?O#+]Q"dpsx||hD4d&o=hm߳yE#-2+ϱM*v Hvwe%#.Bp 2P.#-d#+`8ȧ?ZRŇלU^2IpdEM40Iњc؋Mh~}cŁ0 &gI<7a8 F=LGD<yjf `h̺b4#-I$ĠoO}[~h ~#.ը?%q#.J/²~k]RoFJLv$6ϮY%FvKKF2 B&*iU!SiVȥJHKYfW)H8גI$5LT˭MIikmk1镘G^20txyCCZދnXd{uc@,YPJLLu#.X#.(E  ęa ԨـnCq 1w>>ɨj<DJ' #..00(E,nʸ"{a@OgvSLjt@ZiB[!6][+?io#- 5#-&喝DUw, D@9#.[(>0d:߷rhY+{IV؍$Aa(4bZ mӽiuqwoȧN)"|LKMz7Y̲un.wnO6F6~jnԦF7)ݶ~, #+#/vx!II@՝a?NTagcIp=$'N?+ڗ[r=xlvJZHBC\)n-(oRʅ`ĥtD_/Nmu߰h8afO'Wy! AE#-!i&Bؤ<bpBYfk ܬxw;_$#.6?D\YN?e #+?x~Տkǧt>Fc6n?]~7K +#-g,(h_qX0},z >\=:Wg6u4MdP"s}Ms9n|1 <;cTk럤ko~'#+q("Fsܠ4Q`@=<9'Ncz:x=;#.A7(C!` FB?XY_9POq@&6˞ 79Z>u:7~#-SP9{K!_!}~,ȟ)^:7* Q=g*- jL 1 F#$ObS>!l`h#+}>o֯I>S;B7ߜ<~!+{*iFt k3/nv|mҤ.OU@7¹W{*J6_pw!;[X_3I>t +:XCuۜ#.nr`9jwNfE#-" !*ȅDF)(d܉Y%ڪQ8ubN _㍱|DAgHf b{Dc *Rq|Dģ ŮnF=h80<s#-﯏6s"U?yl ,P-SL{S:K,jv"}1bJg>LO0i4o`v5 kx)m%6^痿/_k/tcz{?6?ƈEIBHhm[/#-**ΛH$n3jT#-TU0?7y]CLq9 fw=ǥY*wUU$r&~z6>CoP&3HNrqay;O12E#.ũS9h}"C}jhJ;).ٹɀuܞjWy d`So8t.X)J#. GF g+@֋hX|Y(OVxY`S[T+zzҌKҌJ̭@`K|0jj(fQ 6Mc& )1rAD<I)G(@3?FJp 蚁#-K_ۇh~mF,}ԭSXX#$ Xn#+)k#.I}mڞbSyLBY((9gj4 G\Cp'+Z@P¬?g턔#FMG #.4X̢3|ͯsU\#k]0Pmy]<xk# ]zr?8Tg`>]t3B#+'Y޴a#hysl!Wr YdF#.#+3 Pp9qD G%,-f'(vHIG.g ~ZZ^4F ٽ?T(p sQ@#-ٝld_sn]Ahsam!P4y<ggٓ[$>uI.{"~tJb(Y*H}LJחO>qEmǮC!~0mЉZZfi 5!xXFCzzIVI$}`ot38u֤4 4jbA̩hx9I$@=(#9哒ϧ^q!#+۵HT!6`HD~`P;~=#.3xv<&;:9ĥE$!?L>OgW˽_g?GҞAd##.?V; PRYD1dbtla#/gUqP|@%̏]S^;q @/bV9g#. 9Q߳eA~!ؠX+1Ec2 9&KN$$"KH!H?D\?S=gOW{KeRӭVJX,;#-!y#-uYPuo6G#+;+s#.FGdU0ud@=tp6FH[5Ljz,S<0s9`1N#+]DQV 0`'ԂݾQPnU#.&'M!{G]NwNs/ĵ6#./<gC't*ڥPR^ǞפT4S HL9lQr[uPq[u\?"XYm&<lYX"ʥR~rl(I-)ѩbekpG-C`y˧_>YX<Nwd{RE^]tOgBIU=G':3|#..pDGj\3>:ZA+T'2#+}Ӑv@<~Ͷi;6^`iC"xY1!H5x #)znGwCè; 80H%VEU-nj{aqA(GZIQλew_HA=I|\kd#.)rO vE#+S|;NUJ;D!^$PPn}3<@z~cp`<<sr@Ԩ}:/9m`D6(ϼs?#.SLE"(FY8 b-E_{[;aXM2ϐmoO]߭KӜ#+j $쬦#.#-HsV؎CPiIa#-x4BG;R폻W}nAcAd3BED]Q{p'72@t8fTw}?kq6OR(R-%dݡ?:>ۊwkʦ!Bpr0Gl[=İF!;04.\-#-OJ<zlU]IH]o;u+QA$?}oF #-۸F#}vn+[g*3n9 t]54 +)HR4G4ֆK딈aqElij(Ɔh{sQH*7e>I4<^C+>B'0hzbuWK{zϛ#2#-񌚯txG$㴨HII!\uC:XC!m?m:=dE=jQQEV"nI"aKDeȿ?f*8@FkMӓm\{cssbXD4F6J alU6]wKv2|޵JތWơ).DG xH|=kDgmg,+#+nw#ln=4t#.SL6 JD<N@+̬:U#0t_=W6C0z-.>)yʯaMffT3_tƱTK쇵6u^1&+ @*}.B4'KĈ`I KK VR::U]ȑ9-ooZJ7kIl#-Iw u#-Qsad̃rp<G._0~܊B|?$4% tDƌ?sA-sS7eyJwmayx.k^6rm)1Pa4a55|΄sCҡQX'B#-L-%L+%eO4MS43RfPl#.َAv#1Ai?QV.&I<2]pMc=Os5څķp0sAEVS@B:Sy,%:*؛udx #+Cٕ5^>D_juQFH+8Ebd#.9N7V g6cj7{;l-Ƣ>7kDAIhZKHN53]!cBuiTZ#+^9wq3)X#'Cssp[q#.K~g(NQwZ&?0B6:O<S{6_3(RñGa~,C_BU7x'校#+#+Ё7QWClSHYXKUzX_ܙ\z=vkfF6;A M7e/QH=ߗq<51K}S*dpNN@>;9~CFnI'tkțd`)4wx6Fiɰ03uM6m#+<5:M][) Sz%[Apt: xvqD!P~{)ֱX]0Z<M0#+AcAe ^q=<)1V6D=0h vzN^4#Zک6#-mz/ bR1T@ ^~o#h[#x_ΐ넪=dLeyEtx~*E8dh;ȹ LD#e#+ŝH3EDcB/Xl3vH{h<i >:cp{æ#.8qa=Y$nspo7? 4mYS?S#+rEA#.DA4ckT?! < dyE3Ks~IHQDq\\x9_G SH4T9TXYz%zrX7V<%-u2zwNx[Ts~>Z<T؂jk6@㖅Lηs-4u25/9w|ěvNLg5kf} :)0L]'ڵ՜2-ݟKH8iG|3\RA1Ko *Gr^8&9;nBn@']\4ٗTsͭNSKt0AXS4h-|zmG_#-Q9]_H̳:a׈DN?\Hd~*s0ݟ[3V t|P>/hKyގ#Q a톢G*v1Xȯ+=:k4wtAEXҢRojĊATsƅwɤ4>O3u6f)/?b%Np?X]bi~D ,#.F#+#-TDNo=]NM<R''7M9<#-綨 RF}9#+NJʟG֝=ʎ07눸KvpCyDh7/;t+07=7{Jn4>FnNI+ؘD)/RʠXP_7(0HTyH~<|Xm<s>)0D52}:E#QvЙ1K=v#+۠l܁;yقdW6 <2{3\p>6Xaq?e<vuy˸@x:RᆔXR#.E@*ԈSz+^JyR#o5GY#%R,#cqauY#ǘF y*H`صI#-<5(ad{DTR/ם fh,zb~OoMXX ^oG#4DiDs>WafQK#+~|*WcdD͔<To}O8CjzRrn2-骪a`]Lj5vHGh7&naaEۛ#"1k?;tJ^,I&2xks6`pd^ֻvBhdZ;ޱpnnQUM@ӗ{p#+gv#-<\'HNzK{RAxA+pX™?ClHRT5E6jN̆;Ͷ6p#Yn8a9(NoR߲г #.#+qNg[o{"<32uuE2&'#-}vRy#-VDrW&su5LloU-#-gA k A4##.Q3K@E#+;F:#-A E`J3,+GqljtH#h9V"i{m9çy9=Iۀ'ͳ#-3ą:vk'bz/tѿLFߴϦ;K( };j# "+FQk$#.V&Z%D#-Vq$.]mw-ějȼ3k{K5,_X^V6i莺]eONPLgwmٶ x2[o~|!A;krI3:wa=z+!6tџ˨W d3Z\CUgC6LXr8^Vڰ!O"Uv$Ȏ)wIl<@fGRu2[uAOl&%ہ9"i1 !T' {1<|u`~Uu}#+J`6&vv y5BZ΢NbWa\/#s;3-c8 ,8K#._7dCYd.^F<c̤Ҽ<uj7d<'gqх'1mjw߫;zq]7c3OFNNݜ 9f+mlohFq9-r޻q*6,=!v8["qGyCɅ2{rLx<Yת PMț#֙(`<`%l<Knaa4:U4kl38e3Ic0t~];cݯw-8;0!T<avצw3ĭM;J(//)169 h׀L_pieQB b֦H'pXOw#+CNsyAw D&\yZT\gfr('Z%+&{zyěl7<#.w@ %b[oa !E paJT8Ǖ:xI韡{fܽp=\X<1CaQ,Tv:p.w݌2 +:(aȠ\QÊ]\a=Z%T#+܋NǸͤA<Jvp@;313̏#-B*Xr{JN&7'`2Y|&CFM,Hpo:{I@@bciF Ѝ^1DžYnt`Ɨ_W̜c lw@D`#.ú2wvA$CFkwNKaʣR+q nMmS75sLd$EF#.R4-'BaExqèkc-̸3&\f5fY$<~Vֽ[8.W&j $ =dlm+#-bjgqn;`/A#-͖-_w26'yX\2Ǖֳ}11O5h3${7 kd٢AdL!#-Ls+[UG٘L`E+]s% >#D{%b@JTŢ ؃Oh+wdQjFfgI-}9c[6úv;#-gT؊qX]x==arHv4R-mhoaAb_ITTUBsTTplumU^KIA#soPBaT4;rb#-0&>]>K[>>73?h<4l-z~QaĎ閷V9gY-CZY:ͱ5B@-DF&X #+8"kVPr,aLlp"t+FQIəƪi愙qmviDf`F!gO>!Ppƛ#LyȨa$0^/Dj<S1׃G eBYd-#.0,#+R7c;77q68=6qNgP՘(2HСk{i ujOAΙ}rjB@fayهbd"p70n3]Bʑ4)Bq)(zʚ B(őR@ .7k5nG$|$"|&J&=1G& цCij zfݭe8g>>:1q 5ˊNWp C8T#+\b$D M6jMɥ+0zcdq4bu5aa׼Ύ2.jdjPڽbnr8<CT9*\&m*J %^ύfʂO|J{ƒDzst'˛H*"xx?CfnifPڒ+x^߫Ʈ]i4@b#+c}CA-~ئ#-dX#-(#. ?_J7d|Bc=k2K)WTbdc9: 4omFӴP=9]ݶKbg $8*pU9;Hv"GWNz_z7_MxsZ#.Vhay[(@$\$%C#jxDhBt(9@p*B@N)Z#.G~0sz?ЇeӀBVKw.#+VJ_ߛZu)E6IFvl㻻>Д#-Co?sK/JRj%R1@qTAi# z܁A4>_3Iʾh?/˽\bجRඕ]iTPK{cûBBHHIK^q(z%ڠTPi|o[Ԝe;l 0앣jI4Q7tLԁ$@Y[ErUCB@!#r`DToT̀<Hn#(#+@ha,`zľi̕^&/<ծ3;D]@jT̆@$Qb$.$ѬtF`"#-{{0Jҏn]vS@N]T_㘬;xEI b,D`5<AB~u>HƟJf@!h!7#+#-[]쁸F/5 rN0xgnE#+] b,@7t=j iRM@;,@@RD@ XhdEQN&9? ' p<d„m(ϰx bu XS r,!77`j/+}"_z^+RHxszbQA%Q[ͼ\1Ύ咲rKӭJ2 ʏ1 ;d!ߵ_: h@Գ"I#9#-y=~:%nEEtVJK,i獇Ai~=1c.ʲ${|pOȚ镰q<on\36EhR|MO&)J5`B8`(#kC== R<Yz+-Ùw9s.Tmw.@fĠSydBd U#rh$Ȍ?#+ eh㇖Vebh`*?7ÊR7!7<Oq?HXmjU{a .s-$%j!'z<S@?|ý]fȳ'4s}\rd$ZsE=H#G~0WuBUJ);~:MPJ;MK<JdE j#.";Yu4k*J[:BNڄF D"63_³fBR:1np()de1*#ro7jҕz"cmAmU15cI6Q%F+KjlZƴVFTi2K[Kkfj=GNVʓd8EI"2=~=7900`?!٫{`ÃK#.#-#-9P%D2 j*"`FCmk\v϶GESp٢mDӥ|nhi(ȴp|02LyuΛ =EݽRGpI;=:Mn-.tɈ2C~z;%HT*M:L@T H 0 :g"A&k-r5Ej+\l)S(f#-EI(E(( '3#-`CL72*$ #+JrQ꾼_71}bTG i 8DEs Ƽ爞#+|p$0#.@ 1$t>z<G?@D ҄d Yg/b*a #-4hA}Fl}V2]6J:4xւ oM.Sɜ"Z 8aB1vZOmbIhE %1DP.`DH<eݿ1;nxIx5STRrYs<:N]dAFN`wئ&HcuhTjL zz_gw/78'BQ*vɮS*IMs#+R02!>t+K>5/wӉ{r4#-K~_ȯ#+]#+3 su Y(lSiLBb86޴B5P:d.&wT gd٬8`R)+q(&mvAQ%S]#.@Ml+;,1B(#+⒢0y#+N"#.Sބ>=ujI/Bz.??4(5e5TTQ(?¨z wɱUPd&Dp&jQXl'f򝾌GVkAb7q#. }id0p>Ug] ;t1)bY@E&neY{e `6krQv~.ODCFOWrH8"=lpB@Ar _E5>xAQ"qV6Vɱ0hSs5a19AaGd`[|ܞZqt˰~쬽Yep@Bh SI .Hҹغ:qfaۗʖrR@A Μ\9pN͌<[w϶-NbfAX[M㿱wWr-Fy GFq͞/=.!_%`Qh{9y.nɍ7>w`gl.<q]eGx{g!a=Pjjxlkf__=#-ўqbEÑdx#.UB~@K=K>GN|>,b fH;HEm -NFtE"$Ҡ`Qk<kPQ#]4;Q Pz}awxςyLrF^fthgsbX4"X]kɒg.Mrvgs|GM.T.lNzEm,Ln. 9v9s=Aٜ(G%U4؁˱oف.ṸF6~=Ɔ4qh33ǙzDA 0Ly=&tW;t#-KMDXIs*FQ~NBwc[`Ļ aG9mT0^#+_c٬M|yC9P+CI:욚~k"N(D;%OЎ|LyJWVlu,jcss$շpFE6|޼_0v@ۓZP6'P0z))^GKU6V[, 56RwWSg"ѶD_E#BB X*2Е)#.0B.DG Fj\;K #+BXaz^Ӣ)J@E$MpAQ6rҳ?_#OQ)Q)$Ym%ɬ~:>{tC= @D7#.qP$ \g](0EwhH0ڙH%EocvZ)؝=aq:iDg Uj oE=mv@WݨyR5ܫ7#.tv4q'A:,NY 6DL""TdP!r7QlR AiPaJ,}&'5mSa<f_B}wݙXmvEhcsabEx4Wo9>y$&;^uқ>K?kuUz|#.׌E#+K:)kw JXlLڪ#`0D~9q߄{bv#񵗓6O1]mDFb$(@v1)un IEb45U+B=U !VmV0Yay̪áJ78vِ6b($X=h6<8~%w$xfʧM5!q}9?Q#.?Z^ W/!nRA#.Dp;֊AڶoIt=Ll3HBR6TyaL1\2u(mpbe$C|wvb.#tvAA /.!!hl5V6b_9#Q&P%,#-FA@P5iЗ,* ,8|;0Y%LH#-@*]zł4m,`md(kXq0DzZ'K)p2BPgz޶պC) %!рmn {u=쇪RqdqzʙDa ,g#.^YɔmuX<}F]22MOhBMb&us6(!PB,YoMI~Z~A=d#-C"a@#~ꖻbB)gr>r_NavdsĄ.ĩ;?K<t8PlQ@Dr~>gR#+nES5y^ܙ;>׬K>^?Cn#+xQ雕*94.*1>#+hz:/=\d9OQj#%N“cKtED@ja#h+8تSh(FK<)]& LIE#.lw[#+-1u#99k^i=*Bej&|CHF}M`Ŧ?=4ķQCƼlV}:GP!'uV+THCfa"&ywK␛Uh |3*#"f%0Snq_ml6Qǟ,Ϝ2%F>Fcr$Y/ T8'8PJVEgu_d1LYK1ALQRlI͘z5 [E"QA҇c#->*S5U#'7]&*"$dBsi`~F!yJj6X#.blFnh/}602p0JDziCr6#+=WNr`դ=Kr5꼌G>.j=d $Xhm 1uqJ\vOLf<Z [ S16&i6Bp㉹Vb;B"6BdL\Csm_+KeYJ9-UࢃԱ,\/ޢAْDmh[h= *΍:h3)"RգsA#.!MD&0$ٰvt##.97i8KfpC%i82!Qj ȅ0-"fKCt4q¡lYbXhnջ0osŻn/7WS&g*/&cVgPbf"QF̍;z#+\t*HҺIB䈕lʂP&Թ fmFJhjk#.nȢeR4ED$JjȜx#+bl uA;66x2g7EAҡc{gdi)kO]%5N1m[HC8,UtN8>X(w)IYsY']5g&0)B <exƺ59>+TVպ5-w@2#ٜHodXA7097G-y>Y42#+#-oN}MRkE J(J,@}!X}R@5X (p#-,"HUQ$.LhH+0$@ *lac =U#-.P#+Ɛ#-iӀ`*2H7r2L7rAyVŤ=4uPYf3~7v_l^ÚUH(lȷoVSy,̾'1x? y5lʛUC)N ~FK3.++57ctA@3\Bqg#+$JW<[qZ-j-[[mj5T HQ#- >s !3G/Tto2U#D:3<8aASM,$$$3)mL$&5ΤZ1Q4ԡ"P~&J%2hS&&i#0(ЊLJQ4>"6""SjdZTh (ɒ5Dd)24iLcIM\b#.Q--\U_CBݘ׿f̈~L"Y)>&^&1oM3݈όژCF7&yC#+93v6$F~%݋pY@Lo ć!kE)畆L*slD!'.lL"=?7([saY.Xr/zt&1Z#.26VJŊ$bH:uX5@|&Aߓ䢛9Oy0נ%3z55RE2 ?#-/Wޟ! 1UCSiNR3͞èpvGBm@7V!xGM r%>GZ! ;, E)DT$/rdǧhSȣ*1s˖ 8,}cǎDDf;*Ï16uCh fcBU\DIvy#n/NKsDrٯ#llm<qA[Ei0ua}xG,. &kWb2붣~K1-/YӇ:whׄ1 gFWhRnj"ÙxIJێ[4둹-{e5w !k4x)=P6a!ϭ (݄1#-ݻaD~kypnm">%_܇ʾ>g@W9N#.'r~oE;NoO-?_? 畧T!E5g_|6V_#-Ȗ/ъ DPښy3#-݂͊#+3z$(K93 oACJ=+>9& #+p$PW#+Ν~tvd=ϒ*0g0{Rf DODtdkWԌqx5dL,]4Of1csQtΣCL[l͌9B XgFE5(Ըk"aq-ɘSj*[Zm+JRZTKLEtXU9~Mq:/L:l~a 3U1XWS211#.|n0f3/W;<S S0 =tx3edwj0+6 5#- n 4I#UlV\/N\ #-b~c@1*KyKZ1=ƙGAϜ>#-Y5V\0>x t:F9]EV:W<:4,"du;m'Z/,<Y;mn:m!A5xk@s,b0m4Ԕ,wknSygkG#+,֔/#-r,)N4bтl-&<sC~d3ڈ42ˍ%flg&qz1\:޷LNvY͔#|Ok ݶ!>3ONf<x|yH#]xF,o5sʷ3#- PI=sXݮ2Pqu=QZgV4BL1- 4B,֯H4ݵi#-"0#B RKk dzr=#-"0qL,})y[6ۍtzڰɆ%+a[*ɾU쌦!2(ƪcfz9d;.[CE_TˤuF[pGfnHh1,Ahq0V^#.2]xpUrs)c<3aG8Ez%䦽dڨL\$lL>И%0q#M|fS*CP7vBEVU;"}޸7fg6vqfg(4y$;b[4,64f8h+Om@(*/i#-J %S"ML=Br#-Rg Ԥ6h{q#Y֍u=fYi%NGVTcw{R,AǦDRHњ;`#5&xIj^[M)<cT!,|DA#-[uofno3,.~[X\Iu0132C%39n!Mra\k(ְqvGC69QF72f23sTˍ\<|51Œ)Ewu5G55Ria ^\ˊK+r|ˢ]*3/xƽf:v$u73/+8dGc#rehl.u]t<uVXe1;!DklkT^Xw`>˫*8ƭ;&ʡ;uNl=FQÖZ<S3=ZqZIw#-NXa+#[0mp2 7K@4yͩz4 TڄE8($NV3L#It8£z9%+kfjWԱz4H)(TӖXkapn,zskm 0d"m)#-#+$ȥŽ\h"68YI()^XSb@-hJS;h ;8{IB8#+iv-? RAuHlb8c͜MC2\V%h!dQs@'pc%FWuBd<Xg5)ɅX݆XRM`th8!K@hZiٛjɔ"2diJfJEgLIB3g-H0!l=kL%2XŅ[Jakd9.F']Y#G`m1dԤn2ʱX!#.lvS̴6уM*tp#.R`N10M!NMM=[v\Nb~Hx}hn|JMVnS.گ8٘9|Kd99hXbdhg7[dՄjVL#.gPiɀ#-CRishCiFa2qYjqPia!bJHXywcw3.e(@n2W fd #-Q4.⹌&HR^(e)#.HZ@DbAV$k<&(l("<ędcl$6m./\ru-dwHHH]#-09jJ+o4P4 :g5AnT3P#-Tp@!r<6}i q ME5N4 GۙzغWU)Q#+lFL+ fs28sf0dtpJh r#+qL#+ͦJ3׏VI0\L4cwHsCf:&mЋ ]0l0Y3Tj6 r3inf:2j:܄عlJG8gten%V#+]"aifau[r`#.:C=;!`$FL՚BJ&D T B" U^J?g}nC`w+Rp4U0KRZ~3$F#+8CdOCnվ#.u]nf-#+`@]RIR[@ P煂INUI>[ĭ6qdpq}|'jlĉ`jM߬iv"ΰk*Í#|:fɔ֗0gG䁹 #PлC!FFF@~gC`XE^`۳#+E[@RCP" CCMcL"emJKmP,f"6ErX1H_D#-^z!&k!kkJJ2irdau7ur#+za/Tvc[˛ \+O/5=0<T=K;1#.<#+#+~"39!־!rPj(0)0]u2"?f{6#+`'-2"znHY8`,J@VX%/1ѯ44hƃ"EzS@#%˵&tqPLc#4"քj4w{4D5G0&6>ϺRZƱQFIDLM2Ѩ(łoƾ)cJSJSޞ d/=o |@| ?}݈ DY[oI^5KXM#-@m 'w=dZ:`wg{]ْ4eHm$?-Ev1#+tbқpcAE3Pa -GJ ∭&4֒,b{McB#.d-JYZW#-r9R&lc8V!o|"-&8&ܑAdm&  7!3Iۻ&afZP5sT2oYwC!f>v tG. ꔹP&@D; tVh2IQ#-1TqX֦M$#+"Y2Fl-$̞Q;1M."!hXbJ\[%6IuuA#.^*? . 9=t`%ǝL1f-iP? MH-8Q##.5[Dda2.>̣­A'wg-ulkjHG|bSXnDFSvz+ KXbkh#-MM.-W;!NzFg^ y8|lu9fZbCJZĭnUR'wwDߒ+t﹵+EeGp0~=,$RHwvql k 3RA>U R X^@#.+\VPfԻvastG,aض=H|oLhHV( 0@l,SFQuv3WDR@!˜U0ȀB.Phf`P I *BLP16qK5!F #.?@ B<u?P|'ɯ"ɟrM8Qb{(=Gt }L>oT/g\cWD'$X(aGʠ[i&QDy/$Q0LHVk,UY6ՊPCB !!|jNr.{p [֊_>+ԭ^quel-GEfaʴ6M#-gxi*Lʕ.)euI#.S'hK*J"hd60YdayK/Z˃ƶ>-IS1d)Te]%ȉ%br"L#-I"nd@/|)bQ}1{F✩YF4qӭ:cu8ebLZdL23:xkrW锦sB$#. c75+c`bw{0;.`,%#-֭Ҕa#+TI5.Kr=eERA.R#-- )`DbHpQ $,sKJO2U,0ܝqOG8hY=Q>Jvٮ^sח/$7BYGllРۡWm]#-һ+v2Ӵue^M#.Mn5FfS 8Rm5 ajA͌ʤf5S5ԵҴ5R>zy50X-*-J8yޠ~E6l@9&Ed0 (TM ؔDDCDAo;=Vxzll)a Bh^a'ň7P_@O,En\ )奭(t1kQ#-KNe-2v$i׍/Rm1JcJ! `T""@VB*̨FVT]u7Q[fK::Fa#-lY~7@!MiK#0yW ZL@DP2H, Eh#-xi#-Z{<#=4\Txnn?2sO^v2r "ˎ7G+#-ľXwEɀ5*Ѳ!*: gء.T?0qY'+KP,i!6V[pM6OB~ȀDA @W|޿#.{ϷǍ~qPe?ȸP/U[җdQdQ5%۪$5ȲZ&LkyV4V2lb"edQLRP)F~%e3#-C1$(TSD⍤UJmB_Vkvnݍ13kiD֤֔54cRm־"XݲMEX-[l$Mַ.i2&m6QL*jmogV-TG+6[ήIKjd-՚ bS o +5m0Uy*JcTJ9^öw>s'POe3$E^PɴJ>"!KCh'Ihܱb"0)HOúeim0#.۾Cɀ$QF"Ā"Q(S^n)|yJqdF@R*fJl)qJ) #+ j6b1-PjRjkMLɭJ(a@Dj+6ĴM)!Fl43F3F(,F2QM$FV-RAQR*R*RU!$mJ%hM !BJLX)2 IJjƨ$mE,Lړ$+Zk&MRKJm#.dHRK-Hi--tٳkRD D,%؀FK`UJڠAI:%w]C/Ou^\=n3=1c@ ='vR%DDpa7"X5?v4HCÏ=TVe}=z!:Ǥ"*ۆAdbX6ιų@f 5T߃nPRbJ&mЍXߜ@A탨p {BȠ(#.^`Q(! 6y&K/p`W^v=`(/MP`'̀ͻ.o6:ARWJ Thp")e~A>QfE(BEפKwڙ5k{f] bpESG̬Kc%*6P -ߙDۿȄcT T?#.Dg8m,3RiP:ը@խUՋRMX#$Fug'/мIwdH?J$4B@#+#++QmJ8{8VFȷk"vu輍mbuެU=_҅XdZ EIT4öMZ]ٍoM>_÷~?#+Ӡr.<χr8zylyw)#,{P< YZvH w['<B)|wI 2 .veMbmg5Q4 k!"da`VC"ku0( #rL3 ,Ҋcr8ٶ5 L5*2 *j vڤQؘdC`b`SRF[K#.dQI&#.L*cR⑿:T]#+#-}8;qFB!##-(SkhXzA>(ԃ˼҃4A)5Ia*c3nD6wǤ:+J]O<N\$w0'M;yk1Fx!V/Z(-AXK[ͪ۱rH|Aaq Tz\v:PsFʡc/Y LboI#.^NԱPכrHB^iBTbhtnm G-SJBC<#-X#%!%eLN77Ƈ=pc?*Hc0*їXq2dmcPWEȅTZg~#._J!؂ͬ/0NI{P+`2kL4Z,`H,$ƀMJJO&=DVCCpPc&P#Gggn;Ut:vE@mr!E<Zcm8(3Pd.F2 aZki;}w#."v|C̢URe%qxU#.[J#+@s2(^aoU#.NI!@eZ"fD-@3v^Q]Mx6ɒЬf!K#+2):Q0dEr?o15 -' T=Ȍh0mDN"I ϧ/M@krCk)6pXwXٲCfC NB3mvȔj2P@9*|^,8gwY\iwBf粌9ADh5#.$A/EӕWGměkIY$#N *!_ьw$PY:H4 PEx`PduQŢ^ᗮg8b˪*X91>jLo6.B WŎ/$TҩRlz*]5]뻺y݄Jy78\<Kyփ?L "#+$l`bR]̥0]!7 dJy=߳[êKQ";Dz[t7Ϸ^ߐNb:Lyb*I7^B*K#.mn|;C6QqϠP$o|yDSrZwe!]>l􌘀11dϰck,YT`#`rw0K:|=Q6G  ?KXZ^˗xoݑ31!;וUWI(Z @$0H#-,sV N2U˘씼ʪQҐ4lۯv1i#-1G|W]Mm]=s1[a!<ݏQ$,ʕg(2_[Rgs%_/K V20ROѡ)6ֽZ3VTIT\Жm#.+{ihE\64jW]6rbK#bmsWzZVY66֛fkҭ'GLM.p+ b4,}ek3 (Kd ɫ,C oԂM2PrLf7ڢ\$8(2#LZ"`#.ՙ(i9l5ex$IECbM^.U_/ \RUE\L1`P5AWTR$JE# #+!D%(#+&۾Wt2DeuYp#.f4ɜYdp\JN][yP"i2oE9`9"7J Hа7'Ỉ&q4g8&p.E.*MƒFB#+T%%r7[ZlpTe(#+"Aa {`w YM_gɏJ_7# ($h(3VMA3`=x(C?/A@TTвό"HC:L}_2f5ߛP78r2!u@8Aτ))Y}!zC )V*wmrl, TT#+ 3G6,R!"YEic8E;̸  ]+Enk2$(aD93=~_~ڬ}C֐bnOJ{R9<MYH<:êI H (`CKd;#dHi)KȢCNawU}=  zRe#.׾|6ĉ;K[ҾR #-9eݗE@Ȑfaq+եIO#.(HoR)GkL[h5P**Q3n) ^J2#+vC#.- 2С aK.U֪&46sb;I6ȕPrDiW;gLAG г(ɑk4"0#HƄD`3Lm(GƂڬThiX(#PkQ m#lva[T#.B , Y)GbboSPDh#KE kټlW{kҬ%%&ضB`HT9Q&KKX-.jtl1,+ET=eXwY mC7U66#.9 ܶ.[FһErr7Iz뭾,m1W9ᜭCgI1NXa0#ѝHLq.#.4w7Dےbn@ 8ZԬ;MS,I !(HAK2孃N! uP27PimO=I &ԅϱF}qMToyۯ&[pƆtb|,F:d:en'.^ןż<4tGZm#.~Ϻ#-#.?>PGtj7/+cRQf۪%%bV5bl"Dbd@`-eAn*Z$A$Tɼ@Sa܉"H}oY`6#.)g?_w+O`c/h!t I(BQqP2IXPKH phg]͔H#.f!)'- L6!AU$U`/T4Y6[κRʮk-}[/N+XlTt,WUQkW޼zvZּ5yL)%&ڤ6Kߪ4C#+*${acY0J?S #-"mW#.2 h/@01DKq @tXU#+'%#+^s0#q q-qG9K)"XOj:h7;L@z>LZ&r%c.~GO6D;6±1psA,9rι(i,:pp5s!#+Jv܈{'̖Ez i*KJT3iy]4SԮh#4VD!!8#+n;xX1.OY|rJ ! WA{6wͳ`6dt=h.%͛Ng؄.4]n3%nO1 @a: C5)Η#-$F YKXXbhD4@ꠃLGrbFvQ0hI%P 1x{=g|`vI$ 4GFNCp7q:<(C@@e\ F|.v[^vjox`G &Ύb?z:rMv@4hZGf$>^q 5,Kj(Zn D#+%](ҺiHDPAb$%%ʙQM >ٶgzs !jC<kV:?#-qTOMJ@5<#u;7c(iӠ4#+:(|!qBs}7QE -II3ԛёVWDhS-,jfj"B̬QTımT6Lm$J"3f4M5M--m6{WiGL"i9omƉA@%bSj57o0 HSa ;G@OUWs(% #+쑈~r #z⾽LFtY]5U 'ё3WHOࣷyvKąsC$N("J؁!N6bI&$ ERT_A/j[ qObfLa7aq\I#+ C`TgHHҔal҂K$|(_)14.k#.|oTDcL"Wvo:V!86'a'jo#+A7w2^'*(nF@XO7a3PQR+#- $22QEROϚ)G.ueS040߫'g;E7>dB6OrNKa7j \i#-uB^8%͡E<ݛ-<B&C}7Dc@RMR#N#.))e% h0@P0|>^jJnJEDc,C*1T uR!a鮆d@E,g;k)*R߃G7-T x&*<p"D "P5AUK-I@ "aB$̄ ҥ"!O}ż{ej%&z*f1wy%#+(S"P#-C-Hve3j2 о9#+A=g?Zl/ٰ$RL$/`FJ@\Z@NBrxV%DFyM͒@ EN=D ҀI2dD"(D)94pRD3I\UGAyvs?g~>Tozv ֏ UG'?$.MPaCnГ@4`u#+.ۺlPY(8xHCRa"lx1梃#-p>OJ!L#.BZthثYIDdGR玓yr) VNƄE#[PH͇Y=q"!Qz]2%ـG95@6J~D1=\SE#-ђ68ɘ ̉fJؖ؉#۶? aQR/qᡶT6˕,E#+oZOỳS#+>Cf_##.&=1\OI 30,D je 5VWE[$FBNxg /i3z$ qYN49Pj&amA LF#-idgH%,Jt@S)]j5TB#.98V (` '*ƿNN"5#-P[*y,#+$6#- 42"Z E**C#-nyA岙=#+#+v xzg֨Sݾȃmµ#+{1ٻ7}nD28s6vz̃*,"ryhQN(4Zj,7#+pn~P{}l2livq6!Nfy'Sj!tI#+A#. U1 F5 PJDl[MڤieFaR" %(3q;td?BZ\nQU -^刭!1Z#CU-00#.fjtxoXSnm묬nZeƨ!)B\K`"L[P(I`*@DÔٚ7slt=#WIx*.q b`ǥ Sz VPCi% FmH$lx0@ ryݸSw6C@e3lb8wo_d>s%3BcmwA .:22"d#-G.G.dwb^D?˽fLf8>{N(C z /lS̬YQ2ѲZM[5̫i2T6[)#.BEH I8~8ܰ\ѵL~DEXz{fC6QJrurv{ːKCA0iPXȺsGq#.>z$>Fk`Ԟn6I6MAs&;})Ll/UBFGL'Rb\qљ2Da"uІ{tr6,"U1H #-Cfe IAPJە7yLK3Jj >#.^wն-ωB1{nδp9ܖ.GfG_Ak)ӻz<1閫`Mzoi)EG݂!p6=m#.N0o4m1SCKbĦCheᯔD,MF¥g"hiO#.jj)'$O_\#-'.9vC<!b:lP#.) iD6MFww{_*f @)$PAaET$#.#!|r\%( "69!7{v^#.1n{ 1??Y~+JJOxkηu޺Q"ĐH0nmHJ*D"4"'I"!08#C@n|]g 'C:vzN *=J(%Ub-jڭN{}E;8ipqpkTJ#R̡lV,9!I2'Jy-cMAp\!%؋&F|0t߮H[z7!ˑYc#+CGp346z4T(XQRQZ$hD#D1@mM*mWRlMKjeݥo6QaJͭZ=Obޔ5R3t[ÁX[#$`e&:&]~We8.(L}9I65#-ALq9mRoͩ9~ Vuѕ֢FpPFXpsjΦ$RYJ cOGkǪ 2 7μ_x2@8]G`#.\܏օuz y蹃#+7BNy<`)8mcEXjTj't\k@?„DQ]/YDdP<Io-Ow>m,9R#XwH\Ė.H(#-klL/H~fHU30ֿU\!^HɠP؟; <];`RÖ$0>1hm0zvI oKkKNØF\M`$0;_;$f[2R}žW51pّ5‘ abk#+&HlPmW0!M&7b0<M.|B搉#.eИ\ %&LHaw}~0a?ٓ#.$`<]F\Y3.f0WD/nED#[5hJXhi ,#O htk;@FBk q.aP0Obϟu!ddc&ϒ@Ys>mr#+bAa{ T0'L#+}p #+ZmtՍ6mƍcTX֯USVBu6/w]UK hF"LZ0!Pn _.'sno߆ry僧MӺH؂Ugb3ߎuSp=4#-mFmL/<#-A1쓸?_d>g`i>0y<!B)H(6 uBnyMBeV#I_@MD 9lvH?3q˿=RFN*B#-˭rƓս6KBjlҋL"%py(1A՚zPI#-m2|bȄqPzdIht@?$BX pauJ\_P[s$l?Tm+iPf$n|׺fpd'T23o H6eTh#.?/k)s?94l$r, X!'=+ݜ`X*B{l%XrF6aFV+bczQGXfx(uUE׷|;pub1|\XLPUQɏ'}&yA}vy}~~^G#-yuzB=t6Sv:VGS?}Ry]P4T#-u2UBrlRlW#- GIKQEP2"eDPm#.vTTPN#.#-4#.B!CE]u[#+Kx,၍ܐBcEAԡMA M- srXhlkFTQH&5"Y~0HhLmo߸!dL" /PTEDaDPL;li*I#<vPAH!\n8+j1h(bĢT`e !aeg5 aH]aXyWZlFTLlbԒEldl̓#[)iz]W&ܹ$n4״7M*ZFxc"1R]*i/0.( U]1#.ZkfX<'Z4Av<OOVc[e7g5--0e0k쪵$X-h{$qy&i@r4i(fC{ 1iK0K(kJ(ùY-|zmwYdĤ\0b-f%bA!fBX"JfWȊ1n0-&ceDZ#.JpΈuPFwqr"5cxb[FlfdjqڡR,!14̸7[ZͨLaB#-%7fޔxku@잭]4Iڙ4#-/>vq>V%<5fуδ.i\6-PbU6xR!2$}FބH-#-C2F4Äf#-nd{>'eb;vO.y_-&e\V[rbF5 7\i`ZJ+7=S|qSƲCBL:*hٵ#-\"9lTMOF/@/ =G5D#-(]5"=D[̪lc~bnnQi#.hH9)DT DAA#--m! &"0#+a tX$!KݧDQ %Ħ:tt=An!@ۃifY'4E"/ۮ.YiayG0;kE$>ۿe[M~l.pE5"k6hxkF)o=ۺ)doowIrr5xzs"U/ )КhwlfߵBI['QyQ]3Ǒ 0l;4p">r;|^8Q`T1aLDrXM$]W"ܬp*|w3oi!7\(]I|_A4ɅrtDɏ.17{;:M#.Kfٵ]sV#.yݕ2eCsO˿T^k!#-2}ٱ5 "l*53H'=A=_dǥJZޑh TՋYfˆZ6K#+}P$M#P,&BI5,ՉU!Oq7:Ton[Ij*Ư:Wknmݹj)KRqWwURm|UW^2jnnۯ<1X[Fկx(0vnnaI}3TP"# (1E#.BiU"0ոfewhj k D $p!yoRaʺ]ֽEER_uQMZf&iJbةZck2hэ3T%D̘MF̛ fƶ^v}*"g ~F"M6#-כvw;_̘"9]ǡޡX`6 : ȿƎx@EOsSM%2oWPEI+[xzѦyiZRFhmFۺJVez٬Qv7K1݊pFB2)$#+&"4HLA"#.b͋7dT$&tD.!mB8W9h #+D̸vS]ECU )ݩ_?&odeQ6a쪏4iڦf361I0A0ftpI<ܛ}SKk`%#ټAmu+F!Ɋge[KØT#-l2}scewo0<&1dg0#=6Ʉ%~kFYmv%sS,/ `RɆwM#.'m#.$Y<р ܬ7^WDڦecivႠrb[3,T*BxQGB#-4ZlѪKo/ZjLi5<ԛ{m[[[=:Ulm#+1#+q/# {{#?@$1siHSqmztKiOn,Zc#+ [ \fFP!|d5T.=S#~A 0V#B +B22,֮i_l^wUOe#+",S ,;N`Y$c!` 842vdfQ0mѶjԥ8UPL2!,Q`!%TBBbeG,B*4!0h 91z^2#.Lb QJݾ;KgP~&s3n#-ڱv۶H9F1Ԗv=3ߢ3^mVTUk^#+@vqĀ;LBw7s??E4z8(~AD^G904ފGtǵ;}/s񗥍9>#xjZt7az3Zhٵ#+1QfgG*%!(Dī#-4ω4#+m9W {BRƄ5*#-%pQ@6.XU#+I'}T"!Easl,oQ/9e<(~oFMpCݑр*9g 'zT#+ޟ>;jg0x*TL#+EYnfZ#+p#.PG#1/q}Iī:egek/,~d1dY1n#-]Q-\4ؔ^n6,*Znl,RRl`h06smwM]vsrt<co'\̱ۼM<k%jRӭl&ɬtΚT*.uFZjmFVa,=p &H\qbh#+;!A)PN"X[^?h'j_qTA>nx@H,@9f #.ikiZ}'Bޟn;j] je34zm~ PD ?,2#.,` jײƶMXZD2"zdoA z BJsN:YXRc 0OWrÉBFc@<oĊztɒfb#-;AB4x!nhH#.5֫պnl򧴺۵n̤Z Aptz&AJ}#.)YמO=XQ1HAcQ l=agY*mK6kY*@łGKv16ٍd#KL+l#.wt@p3I-3MmJڌրB."353SXUAI3ءM#+?~5I6zw3w?OvtO g򙁴C#+aapVkNEV"#-#.Td$nӞ`h5HOu`i1E!$-81%CdB0cއCX^-qak6$cC5$Kd!DJkߺuĵgd]$m֒lZBuHJB$kLK- RH AL R$Љ! d0eIvlUWy ky#-λD'pwBHF #m.@zfY%zL4KS#.BJ-r"؂ TPFH4vђit6rLUԤ߿xm PMʉ C[9M$AՉ`=aPP&i@iQR4BiSh?#-|PDa$#-NNhTinMm5^b4f׫N؃P@$"YՉ3@E=7iNn0"ȧDM}_^ 6,L[D M)tgCAPqߒyzkԹJNJ&K&I#.FWYƦcm!+;(V/f3g#QYxUm 'm:CfGhE'Vhw؃#.Qڍ#+wOT@GiM2?"Ȉ!I`J!V!!QN٢΢;_#+h7#-7vb~F0i Lѧk_>Ϛ9#+?h_\O.,̯}ut]jPW2P"=%ptS,z.a_k(hf?Zce4J*; lPC-94vZ[Ln(Jef?܄;*ɤ͋3d#-#4xON'8"#-%j#+3 &4hȉi N`^)Nu?7޶Q2ش#.bC;g-ƞ`.x7bN:EmzӖIavG#.sf`IECpJ|;[1L}Yfշ [B=R4Θ4SRiSήiY l1 &(0ܧ^E&#.11SA'w '4>}rks$gi"0tí:[i-N->9k9/_``WWI7`;5gې{v>lF4s7WBz&/,:F0DiiufD!o!4 "#-Pvx)r{]2"8nbF#-TrDjiEx4q롔^GG{LZ#+&p=<#+Nڡr{3o~C^$#mL#+%M)P o`cQxF;g8(V }sQyx+Q%D-Tve;q`1[VNw4D(J(U:6@µUVUVq|k4hROl\0Jd%DJ`SX*2xRZ^3z=-]'|.Bi0l5+V\Ϋ8z+wpN¥îÄǻ/F;i5mv4$U*v/:G`d@88Oެl!Q `%@mR&.[#-#Zgj\ XyًXZv=#.9#>}*U1N1ӬCY$u:A.U12vER+1]#+Jϟ8A:#3P)E&!3e#+Do0t9̽#.S8`ĂHI#+$PS!G/;lZI66|u`>ff)wiŌz;Hv=. I7Q2-2t=GNqԏ#+,>age /gm#+#.qe&C&Y&"+ȠQ#-<.T9]BI {/!p!xX#+G !&J:ܭiGwN*rcq>6p"w#+)afu ܱI¿ch%-Xa 8-k3+)"ScEmZ cݷ2JDe#-B`y #溝WIsoE4k3>Vg\6O+*G/#.uB(wBFJ q'n) OffVS|?趽#-YŠT퐜]\FI^fHؼX 1 ȨVz`rԨUG{};a#$$|@vw'#.U1ȹI#rxW8KhB`ӂzcGC],8A!BIg#E/ڷ˹gtɏӽwh!@:嗆Œ##.NmigL?mBF1$8xej(} dtiߖc#%vrvM[lk44%SDYۿmVĒ @1c4~čDP*-_~ԟy fQ$FYiJ&)1 1Q)2lM#.BM&•c!Iˋ?M<YOf&Û}.)I&ͻfPncl4&L4aK7tzh%&꫘?ޅ0imLTB"U9vŐގ&D +fCw&cz\/Ն[]4>n܏1L 9pbj66gecFۯcX}3%+0i#-R#-#.kk5I"/ɝg#.Rim#+F@k-TJRN4&m4#-e!`<hUr뒛k\]ei c@qjR+ %3! f@)Pw2D^o]no?{-jXԚ*W].Ynmkf|]ٕiP)@#bLZҥRG*2Yp!=D_#.^ ;#/a#-a.$C<<KlAa*h+#.qTRCYBи] 6Z#+Ąz#.jB];mܲ,zHTlFahiB0/%4i)T#-YmӉc")65IMX#-nJE*:R\+@(mUš:‰WT7 BV[F4hl6Rʖ J0 Lx7V#-*jhMɰo:<TRQw=E\U.֠4 XM=-.5џ12V#-0jAb"V,!Y$vQV5aia$o*u e v6" 8$Nu=7jdtE#6pf3fIy5(79#.Tq1- `i>x^J0R;ê0Si8Ң "ȮA&liMH3c#-&(L|b/ʶ77|{XI_.8`RS,JW:L0l4n#.C RF0ԁl$ITr F4opAU@i*H M8<AH f!CBS"EJ)FH|3D`#-1. X?m2)i 4R#-\{Edzy[[: OIATJYʌMu…}08w*L!K oXET#$C82@V[m2"ijcNۈ`RpQR ar#.J2((!BQ!bJ*YXCB$-n`@Y6$PɐTFMw#-;˦5FK]몄QHCTEI;QmBٴ͖LA (=R8^#.4J;(Q(=<dv]}>RDF9*.mڒIhM3Y#+wPmtHڔxWM O00ҠW!P':Dg<%f9Pdu.y.דzXb1)K#.41U)9<uM:FF'HX(575ݘrf*]dڜ~XDl,6fڛgy,k'^zU,my쵧T-#A=R q ^#L_Hpm:Og^P5";7$`b\"g ;F7rt$<ؘfr}-/)B eY:S]UOf!)3ײG;2<(?l1 ͕}8z8|l&67~f[~#$,^0";̃"a#-&`CH5B#.h9n N`Cr[h'z#-l靎5({ Q[55X(@<U:mdY'@D;kR+k _ W{%~IUFWαUVXi3OaHj樤0mE#|"r:q5tlh,") r+da  l(Dah) 2AIn]ݭ\UƎnmtۦmsY5˖f6ɵ(Ii%a8̲Á9cu=#-_g@QV J=Is$/dwlgHI#.#-#+Zo@r"H߿WW6PrxU@_^(.z!u f褖>0)"U`>zS#.FBT`JF Ɛ$}&ĄWdj`#+2 EZMMo"]PKT]"km[T&FyHAA(Gz_!IneB`A/CE!+ʕ=gq K&e<ASnJɺg^#.ԁC{6l\ dDɮߓm]m_e-L͒IJ61idi6,ڒōɴhR-QVmEKJ5MFTjlm 4+ cnAM/]zyH+C)`G po;i*niqB2Hkc!&$*+hlc",H-a"#.@`i KQ#.(h40@E@ ]T$%!DbD:bC>u!hFۦݘvWkbD"HAlb#hv  c~pF}0~o}+KZ(QJH@T$ ۇllsTE&0qu8]n\yhJ2rbxFߎ0#.!H0~J z&bA#.S6U#^=;Ta I<kx.+R3(_wj梭E9RB؀%agC$V=1T=fYPmYg o=Ka}~xTٲ-=qg-:Vj KVͧ<i*^B+P5fL#-j&j*Eֆ샫Ћ%Q~|ٜ)O-VG|Pӥ Y{b/}uA.{E1}<ߟyhFr<<v㾴gMOm՞U/@:G~)g/>;X͆x*m_7q`-8o.\ўǦeVr+®x?8I#=t~Qts٥'Q#+3!O"eBGVH#'53Z#-PRn sCXmw4#.XM>W`qH}5pb)eH{i9 2.5!!/׏_8a9ftS:fGB.[Vw=+?uXZh(la SaCsE^ZBH/ Vs(ƻ%$bN;:8㚦9!-cJ70΂+"sr gp\=ӟ<wIL|S5FJc'k8Y#-VیxY\[NDn+Jxml2a$3@#-xzk}"r?^)!㱑vkTjVmFo9xUftXڋa8h@㑵3lSmiCZmZv V\)9gU #+~o#.']Ŵf+2 ڧ+]4Rw[A[i3ޕlK\nuI<34ѼqDi/I_\w"D:];Τ92֓Q{>TdL1͔*QPh}0zJUW{1];+^+\q%ue3Z'yO¨w2B"2Z(ʖVx˜|yg3Zx^p<qҤm*ޞy'Y\qѹ/HwS ܪ2:#.8IbP#-Nخ&=,, d&1/%`v{r;,a8,cnpӦT{fu;#-Bx^#ɶSoQe7-TGhdrrk@2UЗ.LrF0璣f|q=ŔQ  xzr#+t/bDvii)qAq0w^v"`ظBzQ G4U%+N;Hݩ@͸Uf:60}6D+hcR˗^ttj]mL$ h"Ie#-рb@L؝ka =!Ɉ~B@P0kP a-Hp8b9nܲKfXρeNz%v׀ B@旹pߢedBV%˪PrU2 PvWywFV4ٱXE4 `%A<bx`8D{&r +Lnp][ev5MZC-z"#'HQB(J"ji߃MEܱ6:aŲm=`.܇j 7}=F*x b~3WC[HP٣g"l ;߉hYO)+VF#Kû}s;)h#I9x)l b}V-_E=wX `֕f#1n`C4qlU)Xɚ,|E疌+WVrç?<M\v3"Ý-:5n&ϝoc:o _^=ss2̇.nT]-9|`QGTw' A#M8`qy3}7C-dZxINL1%~ )bR ՉiPtz#-Kc#.[bͅ3Ȧdn/)d Nxpa-ƪl2*-oFNۙu3%އpurLBCPSjY;K\r.7 )sf\\=#׃f4(w5<ȁ(XYqDQ[*CC^#.<igHtȳJô+ )2kA:SFb9T{#WCKiot4602I@5VH(#-A/xͦLXj<7)nVf&E.)-B ]h…q}p>:)w/UCOӮlIG4<}^:ϤRI(zs+|Ulzrju"z"p&\ߢfFfCccm\T($ekhd֓hQؔcT! 4L*y Q`YF94"DƐ^ۥemy&Ub$$!"QRC(S@#-`QC#.#." (T $<{+T#-Cq;S/#-'I VD/7mȹ65~eUUjUI%F#-2#3l9,OuZ-Nbc4BQHrڍ,ہn֌t:6b"#.0eq2 U>D:kN4`f#X^3MAC*#Hz2Ƴ,"Fe#.bf6!Qm,jHlM4ƈa#+ȁVLAOH$*z}5+Z3x#7T`&56cr3RXQ6Reh;Yf#54QHj*=6S &5IZqsVdbCȆ548/6UdZƵ9'f`,+!wYג$pLhC<TP24Ftq-EȀ۬!Hʧ+&JV[1LL`ΐ4݀=Zґ'aKf54Ӟ.TqcD4< {Oz駭&ƧG9j]P#-3+j5)H*P FSC ^5hŷ%WoNvO2+D6EJWwVJCZ#DQFfewAa3`P`p3D_e<,*FeǮ)U[UN$*7Įt4M&3?.F4R+(eJV4U5{-#-#.j0XF3B։g&)fsmQ7.1Ӽ XS|To0z7Cg[m"h{Sh+Q3FPG QG$u)4cՈshx鷋 fRq @ifwMh#.B1<;>Գf㥙ۍ P&#BCޖL5-քD`%C5Ɣ4w5c#QL>B4!BmLNE rW\bfw{ʲg{;ܽك*BBa!I%W|q]uJdYd?=JrJJRD=K<tć2ST[i T2۳J $UT@)WžlՕ.1pȫA_a!H@5b,YIH(܁١<@(M5Xwa N[956p~/ՏchjpyrGM1='wra9;fƪ7KsːQ7#.]/e#tE 3:pFm =8*BA7b}J71-LKˮjƶƵo.#+!#+o5y/׺ׯ4XZS-m6H#&1@JeXŲjKj%bJɣ#.)h3hSMHғdl҈IQj%"Z2B6E*0#-f)BIZ_o+0<{N ;ռ&u>rt6~O"HHa$:^]/#.pi X8XPQ*ːvۆdɃ/T0BuBF+ |ӞL #-7}#.b@5~;#->Tj7ZS64j-v93#+#-F$ɴDVD5Ik}\E&F+b3ݫVZmmU#K d]\DHA"T~܋fn,[#. %#.(#-3F!qtx:TQMIE0rA"9dѲ@2B4'2Ԗ4b i5RvI{y4c){JD#.2 BRJoA1%4E ; ,ib!G(aPԥAzer'̕dY\tI-M솑Ru_686Rf%paٌHBg{sR*:8ȰзAЏW"9DCBw&$#.fm66Vb@d#o3$$G#+*lPCï|@ F:7,$UkOK~<2;V= gQ=zHFB/~쉓9f%|҉}:Dy %BɄbcm+kS4`=:q+cA2s {Qhxyx~ UrvK|<qELB6x{=2~K*G]&tyնzz>8[#߻G<X3IqGpfs2I(Ԣ-7bԺג=VAB/{,"tSm}pmh}thb#+BERî[]hzRo]mDd3VD XTU*NGR3bL{w$ 6v `CY|{BBMyՏ,FEk~t45P̘0H=#+r{2d"" 5ԾľTCRGႬwnj|o])s<!6em 1쩮#+zs_{Ds+>8V}%#+}>D~Qk*0K%tyJWDB/ĨXTq'kQjʗb]!nur #d+}د-al"RCxqr53pX5lb/P,#+#.0PHglFdLP'7UbX2!**-0'*.ܚѽ08I`$PIV\0H+y_țh^f#+P0 }H̸lS\6$@n2i˗3fH7Av "T#-#y6 *\IPڥHcC!A@F&j`pGI,v!Lj1Adfh-6N׳Ta4!2A'W$E#%pzF1#. c(9iC $0j /P`5W>^=w\p㝘7GIL#.FpaL juĴQ29&UrۈΪfW<nLg>c,8$QII%]'ֹ[y4p ;_ hA) FIq5j CFB%a+& "&zҎ ۙT tZV{nJx7nՍrJ!R<:qH+:X#e\$=#.,dob9Έ#-a]V xlt~K n3iޙ"5F<XN|}"D[N#-#.PK;z"ƣ66 AmL\#Ё<b3-)+|Th̹c#-.DpñiO|8djCltvckrF)KE M,vL.\{՗[>!mi嬹FiyL.®:Du[[iTFϢ#eY,9 "yM"ֳJ0k%LMb.Mr݉]d\ɸDjV:h5e .W,=fv) tHC2TH[:Շ"ߐ#.͍;uw*4Bg#\&b(nH&lv.MK|`iR:aWҀzX7Ј7$" a>nph~YtN!ac@PΚm#+5`+aB`ޥԭY[3"L֗ ٘ |bI6RQլ5K`ِ#.b1HmQ4,TyrsOC,N2hlG[/e$M)KaLKk;~ditgvae\ØGSws=D#$o}K4 ƠrjLc.Y祦)F17×y| +d׳F~2IA 1)~[`0}aL h<w쨄oDE8B`:O7NDMbvv˴qUr^[EjY (K(b5LJY0o )DhK/vY#l"03kYM7G4z[dQ;r9Mgd#+a5)3əc{>jrcZ 3K,8ybp\ܘps&oK qs3CZ#- ]_~laRC!ΔxXHH0d4lN#.kHd@nvW]ΑӖl#+A[C F#+͈`ȫn@׍&0 0C"检RX#.CsB]E٪hLD`ȹ9fDV1#--SWHhM%6Y* n3,UDw<J-uL\vL$QhYPlD YdE lY2iC}ՅK*lN!@0N#+\Um#.40aR]""b:C0#.hmM4`G!,J("oiDPcZE vbJK~ſ_d*4QL!"ߔz&ݧf!! &*!s>[#+1<WoU2\?/IYLϤ i[2`iDNeC8xIh#WNQD( 2㶪)r%d.FϚY략rUPzD$9M\0}U "DJw^%J#.)8vP'n'ҫ*&GyrN CEV>,LwA)V_IL;vcb9M]nyNs3P!T}5Uktr6^0ۚ>;;YlQd\y#}fPI_g*2JάHVg]mz}uߓY%#.{!$yy^u'vt@tR7p#.1kݭx UHLl%&X 2Bdw P#-4b4b C%&bΕddPiۘnI%j004Vfȳ"88OGgl}`<6kZC'f; =M8t!mi N<w0BCEbWdd#-`m#-`8PrZ((7'&u2#.ocms}l#-:&LXL~^OA2 |C8~!ֱg"ݖT5#>2XCq- Ϧ4t7rpN(QCj{w`.䐁#.ӕ&2z*Y~^H#+MbDR?IskҌҺ@}Q_!Pc-Z͵iHJnXEVBEGEVQ<0cUƄe5RJ0E "@X!U#+v$eʊB!t0 M#-9VcN.ovd XAY$!#+v^^?bX18 " #+H*,銞\}#ythg@=cw{k78<@hFM2Xl+%ʫEEDLNAF5Upy[rlYՂB 9)噝d£2S5Wz6ȳ֟rwr=<ia 1V=?9f᪘eBzQ?:PĪC'*!,HH#+5@% E{Rς14]@Q֡L%ERDhAz&dZLi`;LHW3qOnjkq6ZhIEB"6ZEJz 81#.H ,?^Rk3_N4&=m(<ES숉Xb#-45ԗ/Ob@OH"9y\$F"b$Բ0&(1ԛ~Ej9fͮվ7O2z]Y~x(ÜD#+肹'k8d͟I'T>{}2")Cş $9=^\}ZY}X}#.KLY5Jh |Ezrl-<aI"N-#+StwqAq)\&#-\ )-c#-:,5U,6ơ9nݫ!:#.M )kR9F!-dKBe[{K6ڝ#r#+c%B#-6&ֆ銜C2!lZ4AL TkAԭh`f-V_y`3٭c"XYC;\.ex,(#+A.D! !a:|:ؗL#.D(0"0!`*Ss=wKD#.#.u='P`bv#UHQVhQ'>#+߳(b #-(C)@K#-wmڷevc>t:G2%%jA-e2[w=2>hOhnE%Fiuhp}3`qf'(hcyEC#.}1MXP.𓺂p!,#. Q&3#lDhK #-Wa{79瞻g#-Sd5Qy7s]Uv!]ݼ[+'6fH4J;TOؑMCq?q(+`JS 4oADb,d%|؂t$B 62)E̕MWQޫi!Tm)JZ5- #+-5Ѩ1ȸ0DF›ꄄ+֛ȦưbcfF+X[,m&*mJmkrhE@!D?'a"I⯈A58=<-1TrB~<p q I!=6`8g:Iy{h$N3t &n{`Lo!P(b1H~pMz./SU!JD)= A#.RϖZ7IZړ@@T l@nlm`U6(V^TYY^]=޺G˦4H ^0^C?} gfq-)Ƅ#M #+'4#-yLגW-|lcbCvznfhڛcވ?=?ȯ6-/-SLJdj)lbQTl)hߪ~&aB# *Is쳲Ym@#!XC$$b!R412aGhwj#+J^z_)> lv*AGZՍj1mԡ4%YZƭFik+*} to{iUJHk!B#-}Q)qB)\#.jK4O&E[h+J;gxxK-{fXҎ #V6`7Q$-,@Ph%&TWm;wexכT,ޕrT[fnʮj*jl6.imwuwu*JwVMyΤISa!H6bcTj7."SKdԙe^W^yƣYFS-e]ۢ[.PRB9,28xbB4N *Ѝr|s$#|/>}{WKѳ8R#+#-}4Khe /|1$HC6Jsצ %E_""=<؋t,#.׌ fn0)R`Tkq.$.E<x>_ZdbLz45U*qt'FuL#.k4LQ-30S QrFq/ }z_{Fl!RM{1Yw~ Ii٠pvq9䬻u' @#.:0–#-qR1=d|Ai 4ZzBfiP Q#-tȽ=v(IJ֮^ύ]c:GG>iD 7$5i:[E!cq1&Cf0}z n [Lڳ#+/Kct+mw|* DHL0A՚W9l>ya‹_WwNP?+vJ ͍:9x8<\պX6h= 5":mSێP2~.Z{j읊UPn#.4#-vwQFnaUӽ8twBq :[##-0gTCeޖE%2D #HHs<rSpdlA9qњc 9X$${ͅ+t9B!Ep{g3{2 ps2V#-ߓ]b}!Q'ۈ ,@U#+X7P/Z%ݔPUOvt#+Cۖ^ύ/} P~*O_};O}Z~O?_r^~WԿ???/'?~]g_=/E@GΩb3p&H&_#)fUDS#.pl=blȸhh,**@3>om}jYHVz!٦#-qrBE3qX?78VX;G8nLM͐m'/2αy$C-_; <bǰΠV2zz0ve@Y7|<{P'‡eҕt4`t x!2w?nrj͊'ǹ.ɫK.؍aғjfW``ڕ5$C[#^in8i*`l˃FE2wo[x祃O^X)٪faYZ40#.G͙MGj#-[vZt]+,GPf.{% %ܐ`f#->Xku:l_ z*: pGS5##-+$IHb=qy 1C ۉeN~%5mZPosi64kt6b;7bwJ#-Se!<;l,vj+%/&60 a.I8#BxR<0ƼJI3?vs+F=3PEAT ҒB7upcS00$d]p&V"#+ P<Q HT#+ IPM<)Oke@x|]te&a~]Vw#0 խ*.#-#-9֢s 74B񸚉CPmViLlFf$XQ5ݵv\"|Hp"@ U7E[)lY:@eĦX@pI44R{}Tqk5ULF" W?>MQđpr]G]6Ϙ{#9cq딝MIk#+@qMҝt'gLljM3}$5a.{od\nZ>&h2Lm,hon񨴁[kG1mhIh5Wk\-2ZՠvuE`v4FI@!xo`NRAp'`Qsp]fO^BCq\,~U%ƒH|=C~英xk$Y#+1J;>Qv>ޡ|ErPѨ(b܎b4IJ %35[,MW5@OI E$P9_b"<9`zgea]\B[,A<~??A#.kM1AEa`Bh4o#.@#.ѱ#+&-?g/G ʽ1ˁ:Oxê|Gp<eoGNۇ`q_ ٔ!9ۄT#.>R{tA)I+tK3عBGHpePB_iԤb6QQ#-һxULqs8iӶeecxFa,o4N*%+i$w@gXE㽕fKd#./V!퐖M!6#.T K;j1$(A<hB#+w$S g`
+#BZh91AY&SY9 \DPm(¬#%00e(b/mЀ#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%zmmkZ旲U#.[iھ:'kd7{o۶ֵK]v'OgvsN)#.Q+ٽpޝ]/{m<{guҋ@{vo;;vi44l{|U9ݟݚyx#%#%@(}h#%` ﲀ=wf:4[aӹyA껴4=d+֨)f0#6 *{Q$#%#6#%YJzV%}jaj3TTf˦Iv]s}6u7z:#.xW_=z֭Z;ۻ}sy{izr>^}azz{x-z̀ݳֲ4fs`uF"EE7w#%(J*OC#%l=n{Ywd >}[vG`>EmkMW`Ly^u#%}at;o}|{mOvpǼ΀o*WƘܶ2>ϟw];j9Qt׷Zt>ܮu|}ܬ&Nq\:.^yaCӶۛХ&nCz|ilͶв[ۺ5koXqؗnl/`{w>PJPTTjB7uv&iv]vݛ:kkUUܘ{x)HЃɯfw#%7ZI#%#%;^3F-mzݙ_U.us[ v]]|H,q{c;vJ{+l+Wg`;{n#]}ww{xv#.mEf"sﯼl$/gєx<V6EǑޮGxO{>w޴#%*;F7;g<uoSkݭӭm9.ݬ ݾwwwtv:#.[.wc ӂhgu^nu/=4#%cM{JU){xW`{VU[}IzovuH*u$I֪nc[נ&ƻ}ac| Gͼ&:}6;vRw}-[nh &#%& CBa0#.#%PTzК#%{SA)B @@$zzI 4#%#%#%#%#%#%#%D!M5TMoU<zT()mG#%#%ѣ@#%#%'RD!4dOD$OFM#.@#.Sj#%#%#%#%#%#%$!#%&@bh&M#%M#.4Si4 #%h#%#%#%I L#A&=&m{SIh#%d4#%#%?Uir\QWwkZvhʃ>5Zu!LA ,LQ*")PcZ5OӦ Jtđ8WoT"]^S_3os2 lDs4;mqE6M`Vd]j◘5o# UI7wDKU/~oBE"SU5k3khȋ 7#%H*-E :$H& "HP !#%h#%$@d P@mfLCLd@M$Rj63525)FSm&‰$J2ZQh[Fi,iZ!#.F)iMF #.1eMDRlZSMhYi#. (cRFQ&JlBjcI@hHRF"[MUifLM& mM65%)-53fZL1fdlB!QfR`4TH!`ؤfJb0lBXhdFIR&B4Cl &)BFRf$@YY5ccE#6dK) -) %&ED2hhɉIF($@VAMEfR` ؉1M ͂ab6V$$PRD%Q0EaIL# &RJMEXHjHb iM$"H[b2F̒3bdE(͑&U4Đ)AQ`,i"5+%6(R"I&aLcI &Qi3H("hkP,&YA2%&DM2*4f6)16A! "1F4, &T՘l(#.H!HСYM$QF&JFhb22iD`JM52i!)#.1Sb5*R)#.2)H(HR"E$M)i5DFh&TiD#.,eDYJbD!f k6lmd$4Ld5EPZ XR$cc1 2RXYJ&EM(ZhII$2ɲ#)E,ѥ15e"$4M`BjfF&RLȈ6,&%T٢-L-bC؈RD#HJm~ka͢lVƶ*6L4R44F5Yc(S*HD[%QIKQ&D5acR#6e2eI*4TB+d"TT›63,YLS,JJSLl)%2RZ!գ%AEVMdjEHkD$mhŊl2ѵ0U@iR F2&ƍ&di ժ* KY4IR1!MlY"Y1Jm*RYaZDFK"e5MMk,Xlk+,l  Db4!-T%KQY&6LJ,&l#."+,E1Jl4 AE$&Fm6F6ScE%bH#!4LQJdRP5BT4 JBj-#636)F̤!Hbki4F5&a $YP-4) Ě4Yda, jE54ECF)6bfQj4  2A&ĕ%I1Rcda)ME`0٤lfECJ2ZfhT2Hl0D2hihl,LDRBAh[ QQ-IM#6LR#b-fC26*2%,ѴkTI5E$ԚPdjkA4!E$Ih5EJ"Fd̡jQHfJ$(j#V#.2,Y*6)42V-&)+)DMTRccdd&#HRLɃA#6)6&"ƨY)&RV-ɈF42JԈieheji5 Q5%)Й̌ƚL&kDlU%hԕ,&Lڐ1lj cc$UE2X61mIQ f-F+iQQJVf*B (4X"J&,ʒ+I[M+FbbIE[lR&T*&$1P"CFi&%Xش[F542Y6!mIME26QAjMIʈBLI&RLd"-тnԥC)B51jTqe+(a(h! b LL$hlNz9SUIP?[lȟYLsȠ;L(0\:8pI d*߱r*3ZI(R+g,$b/0-npz%ZM8wBd~ ՋN؜Z,3cRR(lrrXݳfVc`I"lLhcz瓓G]޹w^\/uu\LP"W4LvcK@JAF&ēMb3(]enY74h6 m `ٗ(c2@szo(7p c#6Gv),`rR[R}dzgsNVQL0ФY;d8$ϣ؞j(%?rõ Jxkcx-2Kw%CUyyܖw^Qr車esnCQW6qllO5Z4EA?_W-ȦWȶIu?Go*y|m<b#4L9cuPEX#.t@#6A!##.)Hz^+\Ѩ8wcr˙(ˮ$s5ZAhJr.YlC,0!M te#6R79,פFtxV@Gd%l p<k9c|Ǚ#tE]H P0T[؃_#zrHȦwԊW7mxVY? )rvjoQwEme#.dԂ׶bN!e &u/K`nnT)"*#6d9 #c*w&bۆfR6z:'.k,YL#6N*}y>㊝U"֍UOpi)8+ Ӭ,j[vlQa1KFS7mZ&#6*NP-#6A@*{iX<ZW(EEEnY5͵w>S6ߋMfhF%2+zmW!bSQIܮuV(i%"@X'+&D)J墶/]^"Rkb_-׻*|{=ol|14> ݌>LȲ"4~"UrPaLֶjϹ$![z۲FlWW=4DalAHPŤ''mBCGE2R]U"wլ*Х²SE#6lQ MZH"JuJ *Ó#6ILe„S$+ bN<kH](#6<*oЗAJ /;qap^Ro^Xj"_g<TCsyXq&qK\:05aeF1PXiM3g{O4urgko3 )#i'־ʣwr8;++oHm^Lq#.1Ir%nCM}ז6qv~5.|bA#S<*Jt\D%*9<}/"#.L3v+T-,g)D!;SŐa=3DKA]( HvR߁#v/6PE#Dž8qΰK(q<>:+#%(\Hp6疴zm*'ܩ1?_k#.VFyeo$Á?jfAv߾ڜNz$!.fܡ\i̩#.#E0F:E&}ٞAg]p?X7d1\NB=}ILƘ#6P#6;g="<R:I2\q.V)gɅ,8`=jPqëZ#.,nMB88׵ۺ1il m׺|5=Z鵦Lu $(w0WX uBdN>ܸIiEw+^{5ZU{)cT @^ǡ=4 Zۍo4^1É -onU>iK|eJ/}m}w*IeQ92e@ hQAA9CrOuu5&JF}w/šjj}( +|~TGR{ES>,߻jj܇Y+$.h>nj]Pꩨ ׶{gmk4Ok{єEˣ;ndmAyQ#6ӫfͲX8TNL\P苁1 63Y yUM3#.X|TW7ΊD^jER,)9߹ȷ)ьuO~$_s4?Zu>]1ykEX)>Om8ŝOz\ڸ;R1~FS_t“Rn(PbRfWYʡcPBYׅDQQe0x~{*nf wiXT)6-;ˣw$3}T|-z85`Bhҡ~J΀Z+9rlVx#6h6Dx0)AX*0_#.ǷzeH,5Aޟ{e7S&yY񯥳LSœ?ك-y69^}pBIiK&nǨsιߎe,&2:L[xHǮ*U91 DL5;i傄QT nUّ`ҟ#oJ41Sv馫a離M4O|I$ {Ur0;0*zG/7+ #%Qb]K\.) I9qa#.5Кw*8A'<:b2xQn:,öTl8:<y9CM\"#.JvX=w%gC͕PGhaH)U.ۙA=s ȫsH]#.$8.\ӳMޛ TTaM} P ȥ>?kborR#R/*3Ѧ4uocȐ#.vnVp)rZ,.O}zwln.LQy@EHiw=Qq"%V2&0+Vqw=Pu7V^-EuQQ'U0D}ޙNi$'1ΝKU=_lS#60D1zǾ+dpڈZ?wx\`<+fJ~WKոݝ8n$zG<c/Y vfz3Lcz7k%EΛY-J`]BvP\Hm2LMwr4:Œ`\Խu}<(f`ňͽUQ9c:O+%PiP/<H`6,I<Ye"4EC@fzbFGB>qz< #?}IL_K'B#+|mתǒ7"nb#sh~?~V]6k)0 UBU,'@5+lX6ɠn.{p}:ybsf19%Z##6 ,&ў3|#6y".$#. #.u. ד*{gdevKfQο7N hÄe hP"1bL`Mm"-X]#6=)#.2¿+]tg:00YUDƊHI#L͒6\j}#.ۻ6=U Sޚt4s ٺ"g.t(:qc1^n*fbfۊ;Xh1as>=N#.(qنfT`al~28r!:Y@e jNec;8`ij%cW?DM@0L\'kKNϧcV*F֘9Oǻ*5,<:ؒ|EI$$G4{S#%beTQ!Pve6^\FfX= *n>Q/nNQ#64E^~`G'0\PDL7؃KuohsyQ*Ss;5XvPf8u[˜Sϒ4s?'ݭi ;n)tYG.K|M6Au8'Uҍ*seܙL)e8|W#%}O`|A>bmf\sIO##y3B#.⊠}աnrCDb+` 왭n>Oqh59b f~~V#+b^Ug:^xTq(Ū1y{TzԲ?[s 5Ygk qjWXv_WmMuZ3Q_{=㳗(5#<ID93^y%0Ij[J3NMT nPG4_o=BKJͿﹹoa^ݶxe@e8GHGƊ"ް w~Sav4LUz+fy޿#./wWd^PĴYKAq:;G2jT wC{Ck5Pur0sl+3۬#8]ŋT:$Kr0(\>C ]m9H X1Ƚ7ٟd-KOqDˋs#%$̐@%"iGn gsos+-m%b"moffm{pYpoÆZ̸ 4bx^SuzVlbݵ ϰPO:Zn(#{7)׹L σYӡ"6zXbcG-7J-YSՖ#.[w_#6}P$RF/:HCCo:T=49yzÆrĕ{R4Iom3]Fm;uctloz`(]􁿍5!.ZUǤe(<V+Ύ3Ӳu:b-F|WS] طM(W:u:jڏ̣HEѣ8`k 7c<ΒW=Sq45#.-ڃ߾E3crĞL^3r"M}1(gD%PI`CȲ-G%nG5lV(#6QEcrͬ١C8z`Cu.7Y`D,U GRvpOg[\]EŚ@Bcv8]ѷ7Bov?U?o;2>^R F\ެCtFX 8,;hXj$8Yxp6p ݤ$A7c%#%%"A/c9X]5GY"E5#/*9/Q?Bujm-&;z|ըl@'(*i|GSөK!7mV>9&u#% ջ-v޻ #.cc D@?4#6o轶j0**ŽsÇ#."M\"#%gޜ4yRb(>@tơR]eѷsvӠn(+VpE+'6N3[(ZaɻI{ax=9"1[Jl 1H#.%O#%#%cmaI#.5ׯ_~?OHGTUMy5ц6cjb$|_Cu0^~c_d "w#%>בj>ER=Ss|5mfտ.4|j]:;n>:n@ )fw#.l0}(WoNs {G4|0t9bb?Gk_f "@kX/E;fLq58d*{/D4tɚotgSw>XB<IMmGu9>zs'^͡c7B +Å3r\po_P=T֓4ZMI:ٜ[+Yȍ{Qho`#67]Rd1U[d"b$#6Jk!g۪Lk $,̣[P&UU1#;&bA1dy%* "/Z7cuBsr-$~mL:zXU Dq#6ߩs <=U~L R5} VI魳Lgav_EF -G8?mRG=bSfq7Ai+s"|;O$#Iai׮[H[#.uƎX[ȗJ<T޺Qj@J.#%W&9oD@#.cA,BCG:lkLu#6ž ?; k#.qvʐ3:^LUk4#6`#.~H5ٝ>}[hyѺBJ2\~de#/a74YUCMs{cB'$;ǩ8#.{,jwe#.r^¾<0fx5&Ī6PcQǘ"5UE7yl)p7!$8>{K2ryUD#lwz=wʋ#6Dn}^})DЊ]uq۳2\S{SXY% qnA#U]=@tj&»5&fpK Sd&vE}zGÝ*B#6 Q>Gpvۅm}M *0p|AvB0;k[#6h"# _ z32&Bх56ܦ2,,|زR"QY ҃ LkG>'j1*uiS1u\SdIiTO6Ƙڱᝑzl_g8hxMX0-#sK5?#%yx)7y#%2w Pc  #%t`=!#.fN%[^v'kQŖ`z)nu#?.IpJ[iB*, =`QpgcIezC,f{2ѪRu 7ӝy_>vt|9VKR;#6(<wC)((Fv02hЭD#0+C9z]>lJYbT-žbFrp*(~sꆄ4uy!j#8sy#.bn=>tzh\bPY:—nT `턱ֽnz1DR+GobѣU$2#t^mO8,}YEω{^IfxOpLG#.pYE>vKU} gاڧ^ϭ+#%[a:9ޚ9d+B~e.wqalwE#%{j,d*1wy]OFnsnZ*4xm1|F4sʌ֞{Ζ,l D {#%DhMD)$CTVʍ}14LT.%" #%l@'D188#.A$^0Ʊ\h${ޮ#.NUzJ);I?$J? v#.TTaƻE5vblyA}ɹRC)K.(sN\0pS"o*!WTCyp2b2/mkYKn9J:ܭ\{oMh]ŠfXn#6ۯd ә/Q؅EKNW== ^u?כʓ5+8~яZF0oћ48@_*c0Z4JqF=t%lnb昦J(Bwy.BjܘxHQT84c#6|md@#%奉c6PaFĈ`]w&q22GQۚ1R^+LZyn@m2(ʼn-eFK`(euD 8IwN`;4,:$VbV66PEBD 2NY;6}r=k#6_S0*Byy,SX.9u@+#%)|@zX"%F #%s&;zi|&툝0t651L=õH 9v R4{mnp+=S!t#6ogctnt,luVFƔ#.%Ʒ_Ns*\8r&AڨN6KY#Zԟ:w9Uf j  0F+Iu,<HF;]=ci?k]wXNړC$#."oo+w6<rgln{/#%ƯۡωzY].Lkk#.Ϸg/!5J R%KAdz8vk#.#xEȿȕv`Ejܡֈ%#d{s1܁Qe&rj; 5w1#.)r i/:'D\lm#.2!1M&F)dXUK@md2,tE4'F5F:Ep[̐RM0o5"HJ)#.#6)$դo.؅)ڊ"0ѥbQѸ&b̴cu^،j5 F=3#. M C]o/~aƄDb'YCB&gLF[oQLi 4˃Gd78H]g(q5L;ERi\Dyb9iȦa<LwͣBM"1ȥYi]\b/]Ns p#f z܈F߉  .I۵#.oQvyيxq=LXM7E+_9x:OF#T`ܳqCjm}#6;dJH!MSxL#6\*"e‡;ߎx#.q#֧i>NcUGE^ۤɎ4$;>)͙n$"ȵ7͖Ѡf1(vhC5ל` {ԠR#7(HTZ'cJ:7 Zf6NGQ8@sIۤ܎j#%;qnwj٘Q3ai(Vj(bV\M0kD>UzpNzb#.:8J}COv(O 68H^&ץӳ?ty#%#6]V3eGY`I·% | HsϺEH:u) ,Fj0sVY#W6щ(4"4Yadt#6#}ܶ`5Mffmj (#.r V;'\S)=Ǹ#%'܀#%z\n@|ʼm6 7ǿe)vaҡHFXw?!џj̞xOuw4r }7>(8_]`#v#%H=UQ0rmH%x; .?VI|gxq[$}PT=]#6 Lk+ρKd9r_bDA( #6#fcBt~jl3~inn>y{~] n`"1{Y.HGs$<\G<Dߧ=~Crqy!^HU$2n cb3Aa.Gc@#6\Q\R9U)uۯqV RVܰJ#%Gh#%f,Q߷vKx#% #6wEBcϗj<}z4:C폝7dBb3h"%D͕,J/14iW vevRIKRIXj_^V&.2SQui]!C^~7^4snu*䔩5uw?%`ѩjRD>=>u>i>:##69dc|Vnm/~J:3#'$!PY]oRm$Z&֯SQUQF@#a}Cuib)fy&D*n&NSӰ%$T^%\wbIlI{VTejŋ:1&WR>2#.#6q4~X30~>{P?F(,>_#63~#6 0\rf#6C j1T,jq#66Od'@h'$K媯1 † ː=r2WPŌw1P(ډPΔVRh!0VJ@-I[\T(#6N=>$o-#o}~?S>}+Sy-[O?8[FDctz/~7|6`V˹G8hqp"#.$`{zEVsbGL<OeпIG=}},k<v}#.i{+p߳0@{u#uwDan'@pTc#POR1@y^P8jenvaAcU&d#6V]#GS " Kw-4Pd9p.Q~9ra#.xR^ seHq(>9{ϬQEVp2/Xu|]c=!U%i?C CcXPRvą24q`am; ۾_\PW2ī> ~>=TM@jj*B''%=-uHk8ܪpXؼ&>]Cp}!8BvZ>-꘶*ĄǸ$Mp9@IHg2q#.SuFf3yXx{P-2ѰoIO$'ѿƊ4 Cc 5yo|z1QK,,$o_Cd́bWά ZV3XCE.}<t֚vW:N#Ka<XZ0:44n')c*(Z1F Ip[ ]E}zԉ@x#Muaͧ{yo6lTG76}Y]~z)`]$Ƃ=\YS"#o}?=;~cvφ\X!kC~8ED]mR>^&_#68pboח;vدZSqiojgmysrGt{vǃh`G7mSpa:H~ Rj9\RA~^]%="x h^LGe#.s-я3ٍ̝|%n|jWuz}Fp̜S1LSy}{`--z9%[5v2;'D?/?؃}f]wT(9=E)#.]YYxpaE3ƣ0ѼF%.lA>2.sU?F6e2w?.ſ=nn;"^N]i=[<JsCác!-96GM\RD8;DXhk1j|G#efzu?|{?b죩]<q2yW4sf_Ida2#푷#.+΍-al޿(/#.l<t+{t3./ڠ2@cL|>y8bwc|{ar#gwv[q$RS>]wtf֞O{<Kjtc ]yDZFդpzA:}"p+QՇ_^/m&<7;(|]oO%~W2lS7|r,vENJ^[#=6{}¶um`~۟]n)mϪ#6GFq^F74?w(?1脌`O)[" ƎH#%"2 P~kZ#%RE)#%:O2<~s?u%WwP4I~%Iiӝto򚐺qiA@gjav{>u.?Bʹtg*hDA SyGbe>_)n8h>#.[gj ;0v=>14x gP#hp_Y;|PVQ=?~umA#6`Ǚ+Ժ%#.Ó Swt/vn#Ͷ?!bpv l;̙/|S9? "(H;G*t<&/ֆëGKDj`9K#%#61T'&D񪤽[-2+`IBfH&ڒ~;A΋wŏ8,ȿ{#Y^tM ώ7},Ep#.JR#4Lh+w.X%qh !~I}A7w!h; ->B) J҈ǁ>;ZZ;|%9dCPSxsZgDswr+)1/1B+Sk<kQ}=:ѧBx&"8K.iYk BIeWR͠z4%fj|x΃0Ϩ]Rbo<<rga^Ǽ;y-Y&aI$": 8wu\vioMե|Y1x{]2^aP-zם4-]l_3T*|)8:aΓ>_!(F+~4sgWM/ݎ߶N3hl3-m=Op>#&Q0/S3>mMw\\} $wyR$|_ID'1uluLׄ[=8'x3A#%/SCI%yb1FɥG*bGѺC IO4|Hc cY|u>S#`{Bfe̍cv9G?GF54iTlT Ԣn؀7#6҃,tڛH}=3֤On `a-AȱaILA"hhn)"4[VWc'#.Ss6]&6)":[֡ɠ\6B{qZ8du["?9gBPP}S< =a5rA]EcXw\1ac0]WoOS\V##E4DCH@bi71lXTQ&FCV6ƛfJj*PYO9qߛrӛܙpAFތi|-'lDjUuwu_fWVl:e1ջ(LS>ܬ9H<jUm}Z bvU&겷[GmCfj#6#$pw]uG<~ӧ٣OZx#%8g#65U6;>aL~Yw?VPwu~Uw\3ܮk.&5)n|?>&nnM>MwL[% "B<ëά#6)_@\S|8ћ^F+rEh[J%clX*5gtu^y9fRɢ(([\0ģ#6iȢ chbȣ44W VRңFR\mFAKTQ eBlB#.Q tMy3w%r(*`2shفX;g6JhTCh% 8+:-!itu,#.!@AFY٬uDm#.Q$I8 ~UI#,D֖%\ȪrF(3*®ʌp#R1lbJx3!U$IsJCCID&A;/N} -LH^@u3D>z#.QUhzOT=gx$gSP-Ίj#6ףLPL*fԧ!3F9j~hxc""'NOU876 VY}NO#j@T9s vA#%_${ӫԎV6T=~{ml_/ٵBč'pqC9ˋ!yI<쩫.V|!檣#6uyC'>ܯAe;6LǨ49CHQH2t2wT9"+1r_uLUvhFꑀwf\F>[QZ%T#6լM[he$#܂#.Dl˦mʊBYQYCFF7__{HUu;wOɓA󭬺!BAT1sa虑s,]Wh&6p41-6nH)1=jo\AyY=Zﶊ#6gApE30BHV*h/mM}RdC7y#6ڴsjd$ 8Amb{&o* H86AUrYbZէhCZۅ2SSD#.H#6J lXdL1a4KD#.G3u#.1llX -m(Uzhɐ1(UX0ؔDlp#.i@OlշtS'30<slPt⢲o#6!B#6-o.O6ں<Alqgwff4b}SuT044V\w:{yзc(~Qɨ<ޠOͫчJtzA'ku#%Ǐ"#,,6aiUSҪ4]v,DjGHҌiO|UdNG#"DfV5[D*#.m4&W(_t53MСA!yK#.TU:]*EPT巗dkkm0TJ 5l$2~?sg:?.\`<<@6>\GJ`CT<2+riM -+ATcz^ǶG=4R뫏65atHNj0 FG[qN𮋳  <6ӈ!#6^"X<(Мk]o3H5KKpL,+&,ʁp#.#Za*V>#%h8p͆K9e.lm8V6>d4#C୶Aܺ&F KbV4 =ݠDn$DNAPӌ+gbX̑FP2-c+P)22+*vw?zw_9[ߙoinAyb'j3Xt1m,3tz~a#.6uqMۉq9g,;͝S(v-`kb6V۾ϱ)`f=NRAUHtFE1OSxWxejhSiyif6ܑRn1v6DE qT'wπ.gh#i^Mh3`Qq&ri3haԔ9bXAO}`PwU3~o_FŚ<N=,9}'pF_smA#6#.:M-ɰz! 1I(D8Mp@Τ2l+"Vq*t;$io;n3ن-όu#.pQboyN?uL#.}#.IM$ x)Ϯvюn@:k#.KiccndԪAj[ɕP]K)pU7U"wW; ߒ!r7 JQoŲmZ\IAƆQ#6:26#.];C⍶볺 K%cTnqQ峺m!X kַF=ˤVvNSus'&vl4rb<vjN19b"0NrcZ6W$dB\֊wl #6Өc.ld6&Dm<͎c/r5ߠՁo:ٛD$o}䫂y<YhՊ@μKaNWGi/_ym,qGr0GܜFDdV׆dIF6$,5\m4b-TM^"R".f哽wuRM|an!ԥAlK<F)FP6,-e4RoNYy\#6wƷŀn,ۧGtJLL k8!Lm?oxta^4@en8¹bUֵim;'yhz7,+ɫyDCTTo89IY<\`%dn;,B68u67'}^YtNWd;=/fRͲc pFiUsk[377И{1F.L4<n#%SGFӃ9fiCzү,6bi#6am7Of#.5`fzץqau [A(gzdGߨrt5:'cvP$۳Ftx]L@VA^yw17sGqAكdf_ģ7nh'8u,.y0tO`DT#6 3 ɤ@jP3<#.FR/(?7G:ix{D#%y#8=l{}r\́> 2䀹iߥCKWnר>pbx_9WΎ@TBъZB얔b>_cOVz|ymx#6fǼYJ- 0#6@wwvOӮ!xnpFQ#%jǷe#1TRV1(340 cpTAϨ9z"3łN<ycМrv>ZQ:%Д~ND|Eɴ%< &فq4 )Uy_u«޼?zg n;q;҄&LS1eyg)IV\.Bm0J[qkdŰ#-`Rb]83cO]B$K|\ț|jg}'$jaȵ<4=6=&|6]OvpX=TV*yum+1e#gDt,DFQiwqlޟ#.س^O.Ϫ-I]AQ72I8xLjoLk.ˎMDz=t&:o$48;!Cpa}j bݛ94g|3߽4#.O%ԍء:<4E#.543RTW-kycUN-<#.'ik;!:-D7)%#.M;$h.ؖCpJi5Z9#%7CpC #rӬGrɧ嶖9EJ͕aՅ`xf-0%k2#x#.|^:+Њ S9lVoaz3!m <yCo{v|edQ茌FTĽ~;wjl .r<0X1`1߹i1йKjӧ\;d|:Oeѡcsc↛Z<LQE-a H 0N3\JܘrSs t U>\}[crXZ.,#Ώfi?'D`@#6o~h0*:֠hKCi|__.71u"rFxô⠘#Lҹky>Q9ȳ#%h(+fLmh}uڎBN[R fJ{2APjפ/t那Y$yP֖뢕@)է)O~U$BO3ud1/5<kUxduta>y65NQpNHe>D.*Edn2,Y(迃f/sқ DҋؗЦwNI#.PBbVu]Un=El㋷>JE":ǔ̝[~ڋ&Oĭ)%&UrfIJNgqaCFpzPv߷SC:l#:=ɪ/6<0DUF9R3}m6y)87տQe#$\Ƨ^q1J=f3k$L{=Jm&@׆v|^m"X<cQRHyr#6̪\hlτ#6Qɺ*h952g(eG=!B-F= *\[=4ʅܧImЊFo%*ags7s>:$qCntl Œ=DO@};_+'绠EFD_@ʴ/H(%ۏ~!ʨsMG4E8yKڄc66vvl#.gt>";mu,vut=;2h!0H(ލ}s^G ap%N=cQ<dZfW=y[g|̉ÿi2L7#.nF0r I8Ť6D$M/#6o@ HܡYeVrDD_MptCy:1_G h.:zӫ&~b qMN6kmc99ļkgX3sStc/+F!2xX?]BM#%R3. s)3F~~! i98ItgHCBʠ@FEɁt xbT{IZkΟr[J(7Zפґ齥4%ngWUp‰zƚ. DcF\sTfW#6/9=jѴSeEB#%Xu֊N,&6#6B=p]ٝ#6f|:DaF/+6|Շ,2'7;5m̩]`x2k\U_vX),{wY%q\j|x,`RWnưM0<X5Uo6<-)d^b#%ڰ[>Iz]lS#6eh$'Hf#6^UЃt..k#.п46 zXPť\91mRt}yUQ}[;os-<TvZHsĕ寻&?ߙf}>W޳Owbe'qt_I&5q"gj2E"*[Wn2AM*aFkm0*e"벰I@.q|%hygt>G0xQZQ]~Lf9hj'YtX?PTQj?o]YQf[DԳGtP?eϤ;m\4x5K@Y ([u`"%K%b5j!#TR,Ы`A5w,u1K#6u+;zEko.V߇>(XV9Cg u<w]7+#6~Jo8#!PJ#%)W&89/o7vv- 6mC+WAz)zV{u|NKҏ tܹ"E/séWaƺ\T:#%̹ovJ$oe@tLE#. HЭxiz @pyM>gV0P/WF꠪gR0y\ +nSGJՖ^IY}轄BYC;-|CDfVs#%5\a? vs>_5Fypfuʝ#-/\Tz>#dq K{e"ϽF@}#6#6pi1Ic1 |Im'T\&Ilgp/j~?5v ܲ5b7fnN3[al@}sم_ XFۄ'm6)k4', #6֕KdUC#6F6HDpJWSRs~ axƫaz57m.m*E$ CAlBIm*ҡ˳545Jj9}xH5ZQ<WNVDhU|f+<v ^-~]BA`nW>WB(P%D7K#..mLj+,#e3m{wl4/}}YsEC9H2tflkAM1C,`lرkOIiN}#6n#%A^(l!.o7B[8fXHڀWz2s@ɓjcoN=,qkPx١˷qX8~ =ޡOϒӯtq8=žxE@#%mpONaj eTI*xzԢ4lGGޏtОq͇5KVxd*W6XSm$H/᦯FS<?z̈GP-;yZOD5˷giT 2 ry~NKGkgv|W痿~xLη1\8} ӮW6jS;TǯOmuW<ߍ38z%~rĢ3̦ۤq^Ng'uk//u65>Q{he!w(Ϛ]ȗc!OZ_y1L6P>eza_j~Z^iJtQa[›9-Qz$oL saHR1T *au|bȳē{S-p5cާws_LG偎c} 5IjJ#fعM$rKLgs9a--Jɖ,㾛`|r]x+n/՜ELmb3O]a(!^\C-(r"FgRҸnXم꣥1+/!ƚ6d#6P dITHS{b31棆N \`- xf<3#6  g |c$S@g"Ġ:9.mچ|[6jg5s͢ٽK=W#%U)AMU7/EVb>|sDD#6T2y qв#6k1~6,%gK"hi2K;e#6 fT X]ӦhvְNZPqWPI| gi ㍢\Ҫaa8ibPJrkL0v\q!ytؠ:ܵo<g=o6IޏblHCZn7Fndʿ_քVMc:v+F;ܬ)&#.M'#.bYw:$= Z' &hhmZX\,ʊ9C!90r =޵m3[ ZWT+(a 19ł<搡vFx~GBUU(;}n޹zo]4B:=.at$eJ`r=q`iC JD#.BY`fYBAy U /v/C3$sk:!r=.Y8=}[SdІ*M(RԐx5&GԆ)pI{ .xGtơ',iAxdtY7Fs<dWߣl`)N}͙fKH}C"qQ/Dqh%YNuaf6O}_+s$;zm$$LLzb˞*'%$i:tٙcysQi':-'(`b6<-8.um]{gj9 ^J'o}҇D-W8g{mqnkZ99֍, C`N`[™y#6Z=s'W'd\U|M۵ъ{zrEntwi0J:m O z㍉-|o)#XW>s=o[DЂ un2`1NA;o$x<EqK/ߐbK/ath$&c%nl#'s(Tiاۤ^v*1.)F<eZxA)NT(AБ?>?N_X#6bo1QbDzҸnBhtRߌM~޹h>(= 66M1@|]f.s۞(::lzjOk)pVzקkj3pV>If$J?LV2e*Xp平n(#2Yzk09<-wu͈lnIv("`Taߦ:bdBzGm#6yh3儌#.v=Bj+8;>N8dݺu]:D~/Ү*zpIs>ӾĶ7|Џ.Co<Ozې5*Ќ}&Exъ!SbGeFI-U#%$ԳIٙlM,01*Fe` S~S G>2@~3 Z/sۤzk.x|~D#%鮈u*Z#.nP>uxiԨ&'a S92)igkH|`ܡiS{`'+U<:I,7_ǍV5O/I~!ZRpqy^Km$?ru;46CR;,VҤu'/0?5{is"Vu jO3KONw/6 .#6XrpoH~!RKz%dn()!ߞnȀrlGܟ#DΎN5wP$~tԟNBekM n 17_޹?5;{Ӻ'ό{q(#%`@)(Ԋ9Mv#(JL+G;I@zxԾ(H)gxTWƮ] oEk\lIi?KWؔRmc-5L>(2͛bFmpߋg~:@4>$_#p$ Ő"AY &ׇz:-&OySg]ܞlf^ʓAo?xG57BH\N91IY[Gt#%qHTi*7+?YCxa(z~[_ ]>Nt"rNK(zKd!ڇEtV}nCi]Y??znN:*F`6$t%5Ɛ󳃊\#%|ӹ1?/Y8K,H|"~Щj!#.Kwk|Y Y=v&S7}fhP#6̷ Ϩ:_B>s<7EaVNK](Q/~w+E@L0\"XB!?_G7Lz߄m>+emf c(QƊ#6"u9#.P?KO L09]#%n?@/T#%tx"RKTgLJæhZ\j +r Lt#"#%o(~#%cmfU#%@ER،ğxiBa`8&H6PXci K$q4a-)>N99‰5v8:je`a"eOX2~ZhCq yWiƺ(Tۿol|H,z(`L${9gWR|1{՟@yi<ހ$ LJS(OgIեOSI#zco*(Bv?t,xT[]3X(~R#6`UMK;6(ܣ؊y?= а섀> aI\}V$42q me#.fqpikZԷ(bRf%ER1#p2\4SywE9g+m!#耠(&]#%iuQ:vٮJ5I4n2mp諡nDA|],ʈ[#6y`#6PIow,lN"c;Y:=Z&W#6fY`38Ȋ7K-T"EvFQ`\P.Dp1Cfoe_}gc'KT֨`XD$\#6P8tbOIan0a֐L bB#. ]\BET d(so)g #.4F̷e`1L`yJIG¥#?YzK2 #.:7ߍaNQi2}}Joȝ.L_fs@v#6ĥ7;_{ɞFy"]yXm~,@yNr+(kخ3zL;," 썼BN3$>q6׿yð]|'<@HZ [*sX@U-0e{3ӓTi7qIy΅'V?rH!d=1#3te~AN{4N Ӈ I5(tD# uՙ`QP+&֗`KHZh! A (e<dem5^ޢ)?̸皖6`{kdVԝ2S;7N@`wd@`s59_EOR _zqKs^Gl<\BÖBdF&!\[S~zd'$6٢ɟ6NX |A(噘r;Xq-rI{@{"{Lź5y\JE㛏%9!}CI(s=>X.~Xp^{ka%&⒒Ww,/mzN5ǎ/mK$j +K6L8~1o'\8`-#!C<׮ l28XNeRb/!E75pdpBq;8]c5 ٽ= L=7#%X%3#6,4"uo8K<׬(E;UZ oX:IMejyyBBT#6=q<,; q̷L-rj~Ă=%u:Rcx+#.Ve(Jź Cצm"1I2*ܼF2?0k˳ߔ?\Cc{ >儯p/UM<g;e#6,ۡ(zbJ :7㠜lG1џt|<X'M1I /T])o9_Urb,OL{S_mTܶ㝳ZY:T9oƚ(ul;g}m@ߑYCfMjd<#.x:O)m/4@[Z; hhhP ;w҂V_Fm?2_P|M9ܩ|r]ko {<xrdLf,z7g>Px,l5 =J{CTjhbcDi:L$eRItt)ߙ~t#.p}w)@)) 2dt`!. 3Yb(%D#%tdb#7Bʩ'ثYcCgMRa2[4 ]gbҕwoNݫ,.1DXT<5'.?*biW-c@UT #%AJ#%? >[{}aEaE(ώ?Nx` ߷"lP ޷G?`l= j—\4ʤAP(A&0Ax2EMr2I#6Q0D%90(^Cg~ەRT:Fgw}}vT$/d*>Q4CިوVTńKv#.[T_l6q :A-E. x42#.ދ|¥qڡKj#32r2Ț#6<7Y1֑$h>GTx~2@*%39hZ[`tgؒjcz! ,fǭٔd"@2 v0k(!h[YR |"h/#.( (00 Mm>+dʈDTKGE@2. &cu8 #a j6MIFE.3+;b$K*ْTHBn|4D3#6ٽ3${O٫H i"Q`UKs;$>5P({lp?),rb"ZKAƒ;5Wژ O~~ON{Zc#1P'O)ϭΣM!+E>T9sΚ? #%`cnh?+BGG%om_} MQ' wGύwE&hn%pm"&w&$)rup)\uUd{Kq E]R*8#.6lTx2iDLj22JˑuP3Tj :Ŧ%tCA0۳=f[/0MksvLoTw e1 C{R_qZkc>afоlJBBr';<[Kufȫ%3Ja1b{=G\ULj#ERX城ncsrsVm;X;MV ]#.Zm2$ g-$Fñ L%._L6QSxݚ#.I!M#.FϪMCvB#%! qI*tmơt:C\$@0!#6%BeV7vdcA֩FvtFem6ۨ_^q3_évjᙎ:7T%#.a 3#D<d!'=Ws= "{r3xXL#{F0񜓹<t'(vU\s[%kUZslUʮ@K!E T$Uh^eeq0Qj6x$AICtM4i@\!{!xt,cߓx ltHh<y̯wy|3p+4 )I HE@;4['H!ljQ^58u9^Si|E_PEܚ4ixjRdF[I)-|݇E#.BJ}ؠS0+Zc8VrQ5TQ[u-*gu^24ytKkv_\hG}{NߨV I tUb*ڧVKf,#l swB1Q"ݐ N]S}|x׭?=,%xo:!I*soɋ1J:򤴡eB#.#%,$9gnyϨ7#%&u$36;7R71)T$#6!HTZaH $"%;QҬ߃(Z"(MPB0ˎw@Ne{]=d[rnVoÔR@<$D DG׶-"+joaeSUX'҂B,ںKԸg/7vjRK]#.e+|xzp®ބERǓPig>,PC{Ɩ򽘽Xܩ_=qAg6ОO_*P!6E387Xd]#6m2M#%Fk#U@Ty3o#f, ;Ηm0T&&45I$p~41r/#6jwXh:nxf|LJgXCPt/yҟr#6\(<UTȵ\[#%^H#.}qVFX+1DӭB#..rmȽ͗Y]ZI8LA5N0$юm#6#.Xa.\H܎9Tw_vEF`0HʡE%x+2<<-#.}ϤyT[%)740O:dXoYt{{U;R %D P0Y3#%*V q@iAFL`'!#AV#(RBVc3Ea )X`_ɝ XhD;e􉌎'4QJ#6 Q`(T"@ K `nƴ Z:uYƏtF-RTfsK>BoH~YusPѿ 9:Y&ۘ: cU:ٛBr0>^i[d6p;6 O#.c>U o*&L:.$Lx㤴~9'tld!1#.@u*n&HQT*C.졉=9"E.Ԝ+ueHA$}{;~J\MDF`aGnJBP=#.ݱcLB"YYj@=<q!ɋflc\1GVl.o6linD CD K)Uf[(8?B^f] tJM/BC]郯׻i^|E7u[dgZ;K` 3;`n 節qi8׏OB!2CP;{]Gʂ0QvD9Y=sEB4:vAC2.\|iovZFQ4ZOi2e671kk/#.]6@4x3telG͆ђ@R])NueVL5).s7@4/:U]uC<R\w'UW:QvbFH RS,w=uYV\<wWa"wߤ # q.#8%vωIu-?8>2RMģ8{x]zB<bcz:yfGhbVbZًU%Q>V'׿_S26_g#%:ɩ_xy#%Dه~J$?~~8f>=G/e%A7q,[Yob΢od݊4Cę?a?o$6aH*2AHe,-3W>|opgrjntK;߭%JMYTzQ<u`XXCWKq_A?oMW*ˤ,/70o/8qI&eU՝ωkR2XIlW+YsC?۠GeݞYg |#%$ǡh[<cOgNk¼Mbf"\ 0{P?oG)>p?y?=BVsɼcNG8(5YyPYξjv1}+sk6dص2y$ܡi)_?_3C6?LG_ȯ5Mu=!~*+LOk2b2V 7DDa{';(#j%3tiץAQǎ2Yhqߞw/Z7#6uiMZ0XL!9zzYϿhnHr`f5N$f"Vޱe>?.3/>K6M(Ÿ9O K#6=mcᄡ'J1EwgO R " m"@D__q4|`R쎉+j`Q(L&‡ПK#.FM=33ISإcҚ ȹ~>qU-^uH19aY?NNѯ]#.y `2/ÈDϐ>#6m*H<\2=|.mcw~w`PM9^j=XŽfulDR,BpRH݉)_Ůߦ.|I MJî EO"aOA$F5xqFwd;zС.'g|9lrl:vGi2]  @  3#M.;G9BG8\D=h^=k<RDD` _"xKyy+75]*:p#%"t-8j Ri4i><z%LQ>\3pӰv1go cj^\:Hr!i ~ZTװ-(Lr'@ٷU:k+cJ ]8<rɹ#6ȄԘ^c՝gqoTL#6$8=<1c4+?Gx#.\Zs ܟF)Ehs.`.z0ʹQR~m+0._˗Cn5utWv#60蔪Z.0𷵍SFNON#.Ee32J|;MmQ vjxrJ~Ѐpr)c~;woj1sTh#0!RUW}hb ex "|PH[ǧ:J/qDCIuG_z8XzgϗN:ӿxaFӤ̺["ejMۙAA&6)BЁ;XG꧕('ꢵIB(~ a{p5. 55.>#b=]oGTmü#%AP1@zsnl?]ުzxk@2 e*+h۝:wݩΆ~ TJ^9 ͑4hB&#.xbP;Ԣ^}؀K}!I+@=@- )"-a7&#.J+ǢLp#.uڅ,f5(TMB9l05Ւ:sV6z=or,Ư~\ݿmW#Lv#.{PP6).zE{an!@\N*}W3S+Phucm Ӈ]<y'ztW{ڳ`̫- XLm# FuZ侵9S9}8+n^n$dE['}tѵKQm﷓msVhU_@6oOCUsV:JM,tnO伎r'JhϓW* IJTy`,xPL*3(#.s(5.w~>>j; A^ZՊ0Mǿ*vaF7#%g}l-az3ur#.?̶#6!8P-l]EǮZ[RӇºH535qݥ(*SvAkܴiXE%qan_#%Ee.g}H#lk>.ԯ-(2}T|p= m.C:)]ݐy 07aYј Eg'>ELNz1(\xΩ wiYئe0wԇ%<GzZ aFK^Ԝ%zdM5:Rcw{WhOoly$z 0mCgpPh UA| !=yyDzs9UI#I¹#6Wegz8V)tFBXfG|o.l&)pMSӟ5#.2sQ"w"ju5N7}uKfs=}؊z_CdlOr%#|*gsg~D\͈zw~h鬻{xHgP68ߍt[i(NIБSmwts:'?g6 z)=nЯ[J DqD QJh<^gP%mn=3td2ïATd^soRj=\,c-#%óx; WxzX\3] K^G1ժćk{1BϧY]~<%HmƔ2 U\ E\D#6=E̒㶉rǑY%f=M޳KtZ'l88{6TF=E|"#.UGVmϑiY'. n(h\jdWMأGX|^uS& ]^]s}GPWɵ]>~+} "6U =c8j~>?ss"Cqv? N=3 ύDs\=y-&*J[w bDfNHZZJQb|vꚊz(VtAq#.6r#\g},#6NjBp~ÇBY7;O߫* =\Fv̉RMeE\G)Lŭ<];8iƣ ] Y\6g#%U{[.I<:g#6Q.AQ7##.h;!]X0mgݚ19mH_HGYӼ?NwQ:)$/Kyi6+vJEB@ƉPz,miKeAx1_S4`wٍN{PTXPD]*DCEYc^FOz]cpUʏU@,#%pHmUWa*|HΈ}u'2ԤGC9BSu&35h=3m"|\!XP.{yH[BJ+GPS#.O2,,<ӿf;rM`)@9b\m@@@]ئD #_KcR(?#QQpF#%:U#%"m݄#.4(^(EJB녋2 3W\p4J#.q&zB/!<9#.lcwMzME׮#.CEEE#%<shypR#6O}E=8{q! =ܝƢ۩)C?s_]#6T?@-!/MJk覱{Z-@?U빙G-ǟ@{cnLQ-eFAk93xx/4PH Dү z@Ux#%o22iz k$~c5o}RH1 4}K LvJjJ%}AbArQ1oڷX#66H.>q|$T"~$#.YqrA@f#%W@C:Њi&s7&BOCf+uj$ߜzEO$>hntH!#*1".N;]\xVd^ w|gO6;J0&5#%}8Ur/$߅3>x,Cq;@v/#%@]C9Pgr(9 v k$9T4h ~m*8꘻` #%X} lJ^. #.JCVg5t^7!߆ ee#%~zT)AY`N U)>S׋rYXP\!&N0#6zY*Mt tl,tgg2jbON@q7&F)=(({;P'+wV_?tf@nX.3Q%x]]v[kסT4%e^ROkl(FX8g&w!?g#.#Yj~ieu'ݘe;gTLq1 #(#,ptA5+ldw{v偐;mP'@}o=p2H#6O G_0P&J#%TO\jhDpɓêКy9Srx-hvd#.Y|PmS_V?L_"'(PvR6$Z'x=#.A='TP)%}jW\Ҩ3ʡ̸|Z~cF0V(i+t|~!s#.@Zxa%GK1*p'=6qK/ x ҽdRl/UdAr#6+G&InBs\g" _K?Ma{4ÝCɸ~܅zN}˝9)JIƀf<w=,&M!.pVNteزӦp1ИzrŝXw+8Soy1,#.0#йB9:-{~1[r[/{"V*&dqY-.pO5w;- Jff ǻçH|Wɡ!I1eEֿ9AtP_kWHV#6%嬭1^#0)Eg*mŲ VBo^E4yְ. Fz4`(٧W'fJb;:Z;ne,5_==:8*_oz|q5v]Qs.2#6Ӝ~*Eۑ"c|T=<##6`-P#.L6 Ѻ GTt6k^mur%D>ڇQM~p݌,&_AgNa{'##6ʰYȇ#s\-~mɱ*6iI@PUG/G$YYzR0gC~6SP^Ӥ#.zkHmim.3xK~%"+G;Lnc53>pxqd} =ӷU Avrv94>LpgL{t6{<]^ޝ X \ ALo8P6;Ak#.njxmXE- ">/ѣ5`[R6q./#%Pr9TX8H^ѬP~ZS~!<פUO'U0°*!5`Y$D#%<珟 A8yF*$PIe#%Z;d>{/ n\A8*#vw3*5`,r5#./܎4WiZG魟!u&(_qDg3$ga:,M{k!R [#.Mi a6`3"N+bvd뗊ZO9$-]ۖ"fpELiUHEe.CgA6ygÏ]wU })#.A`lAd9j*݆ 渻,h ܣdw.dy r %(seFzeڭX#.KiC'~Z6# sYJaLJ #%39FQ"^D6{PSS3yL'n's*zcT#. 2!r(PhP37nPF;E6#%-}]Rk >t[ڍٯkY,oŝѲZf'̆F"_v8<[8X˫ɝ;ru06fڹ$H__B'sW$m#6E]}#.~j4a٣ Mf \=Jq΂{O jATP}1%"͢1#6HsMǡQ5̽9>uR#}3vagvmZU#qVXH2 #.֛@$7c<!"2. !҇5Lڶ慙`Vb#6#6ϧ(݋a'9#6`4Z >P4i~q\KA!nȂ)YɼL6޼44m3r !Z-*5/!*k VDK;"n[z2b}ֽJ"Xvh{XfbZ辴+Au~Oڰqmބ:Ca,&q\șA̶n<l4(WΔ"o?wzg\.f]%kْt<#.ctgߛr#%s-  mhŻ]N4(Zsc.x*sgi5JI@od/(&_XCZOV?0d<֑ĉ0斕;bzUKd3 ʗ h@ـQmc`Y N18ؔ+n5#F]"RO]WQ5#./٪~a#% (-+Gc`|^D]Ĝ#6El\q`!z I<)MG~oxE߿F'Qn#%xTL9-4Oxp8Yx@o\DNuWL7COZ#6iY|VA]&[_5i>sE/#6%5/N(l9s7hgTHYg}_A0VSNĂHQ'U GJB3qdZ gЇqq׋_a8#%I~Jڞ796`|6/ Yrh ]3K⻳|3|cuyb3f Cx-X3PXt?Q L#628~s;{|%UVv,pgYT*Br)S0ep>V_b?BrC9%_$TIUPYEZAa#^ؔX81#%h] 0;5 gYv|vvގc˫_wNxA3Caڗ :]۸xn4{3 HYTBTCal<;`xG{ڝ{a{.A4A#.P=+Y;]N5%х\K59=} FB t}:$A7߾]M D|_*U./,!nP=`d_&\B7d{ A qP#%)Xu##\>G+[q7b<*w=OGH|=NA LRzH?yCԔV>!0н^.46#.?ipv7jѠX%.S7pMb64yCaΚ#.̷Qv &P<#6vT} XҥW`_ }9w[wmZ?C` t{s?OqVȨ7R.Ȓ(H0p#%o{x-)F,=ٲ"Ý̄䗰9Nw.icvL0*z}pAZsmy^RONY GT%X|B54 T(X# ee=]H#%wZH_6lw0%=i'`w%#{;r6,#.,ćzdx`9pCYTe*iD-& H<>Rg#65Y$?.xRR=TU}@1ME~ kmmG&Fefj~GM2YAwY #%y Mo#̉lB@"$e |5:IF~"->x"#.?#6[p4* *D usmos!fM*fo5йdƿ=1Vmk86l\gbݒK!UU;I#6lJf86#uoz <:YhyfΙSlK6d.SA~!#6|N3;Oy K~İSf=#.vd?IU6[~@@X#6`/{> #6Q@lΦ\)%P7`ׅC.up`gV}|F1bcC2˜0}"Ao#. '#%h&F7yG2M:D(3_}HlqE#.2+ϭM039!F9-E V#6 `dxk #%O-=fq/ʃ*`k|X}y^E!l<FDYA TD )#3S :iq`wr$+">,q$DL#63kNk^!rCi 93C<2.#.Y$࠸oXw^#%rUvA+RWVoqvތz$ceXj1-.Xf]dmffT! }Ŵ5N*nl+CEגI$5LT˭MIikmjzef׌: '^kAַfuGV:eA)2gY(:#6X#64YH0 AP~_bjTm2sxqnaX;ڟ%tG^^Ȟ˼I~}~ռQR}q#%!>^Cwl9;6Iݷ;d]d>a؈ZjH M)QlЪVY$ rPRm82d?ۍc;xzZU#AIE5&Wf^%S-5Rkӄ]Z̿19l=1IAJ~uE4+.^%6 fg -ԦɽI"H~WN3v7()sp*K;϶3dc_dDv~Q![~1mؾ!t)lH3+:K<QRR"LٰLsMw` ieB@0bRK:Rd|~n|WnG ݽ}8_<'aVQ5o\W^0'h72u%Fm f0/i@P.b+?NTPDs#%>!@Y68x\ż.?-Q*DjIa-zRϢ*%Q<r0}y,z >[v`#.&2(Ab:69v`9_>ʧ86x'7u'{;HA5twPI(?xyX"DN}+ysFY3m;0#6J#%aAD<Y( .pWapls8?~3 PGpC :3yC?P/id+?GɬV7LUQX4=2ݖ|* kCԦjI'> 5UA4d>/ЯHWb)Z]CZ{_\0GLrupZG}6\ҍT᠞20Uʒ8MA#.7; {tc$:F,!::`bq esdQPNJC2U Z*>RQИeLHc;vbNTmT&tKPtUH%HYW>nC-mur6E+n}|=i'2)E^S<a$Ed@$ZЫF)76,BĤJ(~'QvVbJN|=<OZ'Ih<ް#%`s#.dU6c-3#%M_/_k/qcu{??#%#49'J<JĆÔ5m᥈8`:m _ \ͩRJJQkuOI'l~3Gb=hj=u$!Lw!(TCD~Cϡ\ৠTT!S䧊r>2Y"C}iJ;).ٹ h_籄w!,#6|N}C=)BBh"3애kE,e'?`v\>(}M|)k`a]˕aEh#F%LV#%I0% Z\Bah%R2`lHvE6(\Dd󈸂HH"8ERG5֠AC D55-~}#.>(#.<1Z*'Qbr#%*I}kOH~)۴LAGQbY()9gb4 G~s3ܓfHz3J`V߶P+_>q#64X̢3|ͯrҸn[]0Pmy]<(hW?f3M A};`%f #%'YH[#hqk6CH&j Y1#(#.DB2`JhLE(Ҏ*XZO/';+ÓxBJ<9/;3^-i^C#6@գwܟ}RN5 7n9D#%pHo( u}J6+pi:<s[cے. 49 <<gۃ; =a]l#.)#6(߽d`~YIIRJ9\8#66zC|>4~1؊&IlLatɆ,#Z4|kf_ K\ U0$UOqHhG:#Y9!|r=H͊@p2 B#t*KHٷZ<C$"4?}z3ԟx'M'o#%;)B0o%dL,6f(ߤJu.[<r!JS_#6 #%PC2??fY1?I#6z:h"Vwg#6 )ʏT0=iڠώBi0“% gp c%U!11*D\?S:PzΟ æ˗HJMRd1?;c.<b;nN[ ݞ{Cz!R. S#6 =,>zDZN􁷕NV2K.*~ GX5!/KۅةӀyzK*ü`An߉l/g*|[BUBP 4ń8'Pf,͊dď1k;Ծ#ՄQRQ{mzIIJ0ĉÖd1 *0%v_η?nTt#l1绸UgP=`*II8'ʨFP A_NAJ`}ÿ|/O>9X<|z`7VtdH^mQkKH.pDGb\3>:ZA*=^W< e|9 p]-hẙN{qbB:8jAK F#6#}띸O`yJ <÷ ]ȐJʪ%[9_;#.H.҂POܶܒQμ8*ex _X(A=I\,R <nn3'_gUJW(X7><? =>cp`Av}ϰO"<R{8r?OsoS~4j0g+d(Z@A}lrޟ̤]אݮ#%5I#%M$ee5hPm؇5oLpxoJ#.0I ,6BXZPw*o6`>Ϲ#.,~謙!" "? `]Q{jNo.˾0,bd%pAk:.zh*;ϻk'u9V/N|%bdϡ?*=ym+Pw!d#.[*p aln7^9Lйp4#6}>|{Vu-M)mvnEQ3t6nٺn]C9Qp]q#.v˴[Q̚DF-hja^BVͶl؁#%PXV0PPӮ: nzgxDeb>yk>z{h}s1AdH>fX}Ծ͞#8&`w6pFBBM#6I#6~랠H7V%/xn=@E=jQQEV$[jUUD:z⤊^*eVؔ!&&rl|D\Xi?Xi%x< zfSo[֨ s4'cyPvS0"# ͑~{D_*9ոS^t(Mg#r=})NVA>?e P/#.Yl{d+V=p0tԹ20{Qnwv8Kۨzk02#%#.qTK뇱+WUшxɰ *(D!Q4HT,P@X$Y5eHr;lllWTKw{ZĚ?N=g\31Il݃'0`+m@#.Ycy#/#%B._0j%/9Qr#6W=r(#6`1M3-Sm)s [!DTLYߨ虎hq9"T"#6+AᩅqڟYfg (kf:،C#.XaŨklmYq3~73.%Q: v-r 0#6lH`}gtL|~Gnv<! #.wʟ,ð*U0BA_Kҡ(I:*M?ͮ5=N}N\׳&?zФY-*~#%S Qk#%Zz#%W;τm`%#%&U1~{qT{,eM"b!֟͏"qzN|w1eɻD@ y#6#2;₾%2zTXA#%28*5@'nlV#6~\bHQ^螿,[>G;H=iPJޝb$ L.`4m(4^?bS*dp7fM ?A7D$˗t̛d>o#%d*6<<=͸_yaa Lf9mxjt3]]~N.5U4K.wͅGQފi}kCe.Z8=6tYr#6%7"sPDL1ܱ^'y=;*Slbl{*R{ \2@kPᬅDG#6@<0Gz|~HzNcG]bcy҅!!?o?TQ)ލQ6_7ۃ#.ڊp7?v~AJ'b,z@^@q(M9T*.vmº9ψY t"&;:<݇L0{R/$3#6?>_u,6捚slsl˩0躨sG#.uggmPkl$XEvQ ?[IGGHNwS -\x9s{`J:^B4?zf)#.!a1K2_[{lAnLL#6#. q͡S'[oGf(`{Jw$Ru:g<] 3dCp֓48óe2vw]L0Wk8h[, {}/l\_GH#6^'l#6 .rw 0~->]sws&e]]234ٗUt[mjp9-@Ab#6AklԕoTq4g3,Θw<v/ñ3#639ߊ\a 7g< I$ #%!|_1w痣6t3k'6e>1~&#+Uܿ}.ƳӦK7wMTD_Ur{RniU!ȸ>mi?]sy:#63P[~O.p?h^Uf| 7 DN&6iQ-9z-bw{K4d@uL:؀r}Ӷ8\4ݜ&dOxtՊ1oSt}FZ|iw\E^q.y߳#Fכcr 1J?yU`< z 9=(V#%ۻ#%Y]NIr+0Շ#%)aN^/X#$ 2OaA~eoa#%aNͿGη8$Ks{tJ^eQ(L̐#.!~=.gm iij]bM6E@v`waY<<;%3!.I353差N (냝6c9vD[4;ȘQT 4ƒR.aV&BկNhFz *QHj$fU"67U<yb ןb T#.ZdK9̑?TRq߯:YEzhO'}ߏϯ!hRg8_+v4xD]g#.ZgP};lȰIKlpHN#?bp~#%$!!#%ܕUl?zSl]~aĭɘk jk#6v]#.HIH{?;uM·YMQ[qɃ[N&unWx0籈p4-P\I7\܂Bǽ9t<#%<3#.2sk!(*]A+hX™?v)i0h~~[v[a,͐wMN0|<Д'Q{jW[ 9`MP (y9qEXs!GWQ-g4'io/R PkȎB;"ks_}5fcfS.냀2 `DSI0&b1"J5ih>fRHNs8 FeqWzd5օ!;x>eeB-!؝m0M."7Nb>}X3:N7[Saߦ#Hwo̳۴!;Z-"zbS #6ՒAApieTA @h=Bˌf?5q&}rd^W?k{K5,_{/+Yt1Q!%*d`ڟ@R9AF"ݷf۞Pw|}h {g =׷HER!A˴,?=ȪDy_7x<,+.ehX؉+VBG=݊V =R7.rT'(fhV7ʑsWВ&QN!MpqZ%dn;'%1LhUQ#6l}i "e<B|3<<tR`6&vv y8`pBDdkOVR\:+;#sLŀx rk.̇xԩ4gu"0!we*t5:&e%Ds.5Ĭ^r)g[b.;a3X8:^ӷ&s^wG$f>Ww}wλw̸Ŷ՚Y$6a74E#v8omfC'amH|M4!vk!VB: ]&@[1#.s3/MC4X#6E)n؝'a[\f@n|?_wݨϦYl2K?*#%a8xI׾=sArfA#6 GMo#.F*wQT`7F4kA#.20hXTZߴb =H<XoA#.!rqg#%[:ylTsT#q`Z͓^!|Bl=1#8έ$'`Q.%+&{zy5 my,T6$pd(Q13(%&ŤBlaHr#.cΝ<Bɤ޻[ZPXwouM̪Li]C0=Xu]$8d;u!@0PTaxofvfWl׀ GઈQp@MqG{t&Sl* z39=Ny"w'Uq5v/24@l'lȳw@'gyvkҢ d̈́4Υ|#65tM]<#6 i5{WB<ᤜ16; D`#6ɈN^DYuQXhgLm2xNKaʣR+q (X8LoEh.JhBHN_C]Y.u-,\@ +ǯ53,UeǙ2ffX12%X<cjl$0n&z 'qQ*WFz>7w&C›9#%!zlP?V[=s֙&w"TK=k9zpm)i֚3=FChYCS8G+}95Gsid%(#6nBihIwP%,h ^4{w+#6]nԍJ,!25.!bΒ[sHƶg6|kP奕e#.dӒCG)AkotcC~[^#%DBI(׼U6U;}= |#%֫լ7:UC#6S;\6FjU*F7`rtXȂ@v^G1)^4N)LvY`']L5m3}Y5B@- F&)l۵m,r,aH}3V%O2(J(Hk2fdqDy"N5۹]#.70 :06쪻BR$cHJ$tU&WD9ς*!b<B#.#.EFyhaE 38a-'/JZ*Vi4`hRq!]#6Ρ0Qe._B"ɥ#:+R*Fq=r@_9!/MHYLӑgq.t=v7]Xu&I(Cyj4K d;Ģڅ#.J$E1Φ1dk[&ᨡrGX!"|&4уIr`<4MkWg7&6nS./ްF2N;@Df⓻U%Nn_~L DHf |oRo85IF+:5_`d{uȦ\\g#_-7{"#YCi#.W7R!E*.I#6wעp$Nj$#.xy|FPLZ#.(̗nm&C#.F"뼡%z9jě60oHCǚ7L!3 0q#%;{[@o $VI6-p%A<O'PMu#%I|E>8qD-0Ƙ:=&-OgƠ]+<GHk%٢*>KM>i#.$w_ܟь$g\W3nWVGMOUy#.Y7#.ﷻ=h=BRg_t=V[(@2N. c !H>((Q67*=eHor.#6\HzyC?eBVK<`PA>B*!>y5T.(f(ε<Uv|Gzl!g"N`~aCD#6F [ۺ7n6R[ P!PE.x5 #.+i} 2AAw R@HA)Ң+4#6 fkك:oNq$$$byp$@( ާpm`<lr#.YW,$ ]CP;?8Dv#6#bB-h I{ĐB"@|\*X4GZA#.ʛÙx{i#.Pd ##%=A`|x_ CR4ԣ`$kB1K#6Xƨ1tIʝ{m|#%P580.HD[ꏒ{-ύ0~6#.ۜuHKȬ;+(0#6A3%_\$͕>&@!h!7#%#.[]쁸F/5"i^Udwn\q/ p<` b鷤5e6}cj墩i,AE`d;;` U4W٬c z9$u[}nS(G" uM#%²/lM` 3  IBŅܣPb 0*z*RIzM7Mv%^uy;%ewh䉽#.Eh% Ԧ>6|,}l@$~#%=$.4eEY $$g񝁸|o'&_xre{ڃq#/9xsLXDo7 zF%A\Gvg}hKmBx#6I!ybh*O{hV[sU>2f00$Yӑ·+?㐖#%OP?UHjgy诨f#. rk,CǕs(ldXT #.-T``0f( C.h_ <rXz.[9WlD~++yKD5ۭ=氇_uZIt i!,h~aC#.vߓ!@#.W8G䴮M[q'5cyΉ\sU1,;n)ȣDl{#6ERNϤ:oNEbz5RnJ\E k}Gp05+"Nz|JNqz$Mk/Hx8S{Qy"؛}+<1#k*h()de1*##{<#6B~0PPaVd6jƒlJV++-MZI[[YQ @AdD8ZUG]WHs"zdH2#% ug"V7Ձ6?e늣5 hXdi#.,_*i{W BI*@<&k(}HtM#.%hkI/ҲfIrw*(w*RZ(x՘N{up&ݨ$[j]8lJ*ʽmuA}C˕##%QM: *$g3tBE P'H!-Ej+ܦl)S(f#.EI)Oh!&8@#% HAdANUԣ>|zՋr7Hda&fA=/Qywv) #%H}0!!?o6*9}H $g/b5B^! i >ЌhFD"#誨d|M׽NV2]Q5J:4x(.QHMn]E>,xC6th/ɏ{hDh8_ē+A#.Zb i0"$qݻ1;nxIx5S2#.Ou *}9,1KE垭')#%B H싶0=I;B q@li8"d3!x#.AR:pxԁ$S'CߣslQap:rS^uz놳dT$֏` >D=N3%cxIʼiă=I#.-3}r#%4vvu#%NvnBq Y(lSiLc&b3Ox#.k#6P2C+LjAX9pqfzblS-&+XRka\rnj`X#% ⒢ ;xh}6u:1[w~ ?7(iHPjj4QPuQt?_bɱ*w(QaDM Ebyf:e8C8PX{il[I~#6!OLc욾@#9OVvøX79#@AV/5MP40#.Bgk7ÿ^ο}(=c~'!Dv^#6@ZXz}fT.R>ޙSڶ8cs(:>z ,>c1t*UGL c3Zo޵㥷;WecpqF;jt,٤m*a\|k/оhDP$S x7m"[lmp`h8x}7:דlAÐ|v#6w;60n1ljm>;Qcn$[9ڌ5)܋QaH$h8f{xl._+bsaצ#%YbdSdƁ#.ps{Il0R&A#6BHjPA)HTL3 #%!+RϢVAHJcv>8}h2|!#%;v+"x%c@x@ 8`ŶoS'+96*v񑭚l6ywP/fW #.W# dn;vFl[ w6%9MpZdK[?i`WXAԄ}{n#.eA=B: h#$R"!>|8f#.(SܾcC|6RGdL}=H)G&r 0zs:wd-㡧"$3!~ I$߿4!#( ] kl1v.ŽsϺ&]CjJm7ڴAh)(rVTu55#h~=Yk"N(DK%OԎLzJ"^q\ѕX0&aŐ?ճhFE5|eܼ_ '_&P5' ;`VSS *Us43d\I]Mښ[]TR5 0`X%JBi)dpnDpfݳ XVh55fʑE$MꝠ!ģc#6l1gk~cginu[P #6+dmJO}g#%#%C#6t|lv+" S)Dr 4I#gmSPp㲵maآumw`WvC0_#%bn*F[fUۦ<ސNHON'H٥#6#.meQFdQB$J`D*XR AiPbB>&'5mSaP#.3U_\ƃnXڀItAjrir%^[p!1XZڧTYfK`^B:"^%GP߭:D-nD\iK6!E$H#%;+:(gw'`v2OAڡbb$(cB6H &:[Bn6LY&ooagZ4S$.v}^Gwx?LD(:lڣ|^ys* oo{o)4"E֊"gьf:JM.8g8$Ie7!F9u VWTvgN^ׁt) c=#j7kgKm&ܹ5.Sɸk4 JApN:leƭm2L?]YءªCC<sLTCvA />!!hl5\ЙJ\ ץ dHd43d=BH#pK5Qz 5Zt%^EA@@Ȝ_e{> < w+s, "5#%BPiu+554o.#*H)BF`Ș`Yzɘj8M!KI} A#.``(3Uԍ&MlUj0%ԥ-_烞;^Q=\)I#6wH`|FmA;8ck{ {44 Jܙπ}LPCyBMdYbyOĒ䵒~ɹ#.C"I<;Ջ{Finl~}|B{F d#6!!1134ct'HC-&#6hvWqDAt@QD [! Rs't-T#%g雕*YKtF"(UV#60&(&T0}zOQj#%Nn(VMBs bLcC@!.t1'?]& i(DyZ*BejMdFFtHF}Z0"'?h;dYC*;C PCt_#%2NdH<%qHMI\u ΠLĦpM<#.8%fLQ&ј\*01xaj;{<(pю =Hy!wD0ǕAc&b.5sSI1j2EH,k=2E (ʽ :9ZkO<שMF!Lc`03s@}YN#6late<p0J"DzyCr6#%=l+j0 jO|f>-=q:¿ע1 ^P\aaOr@^u4} xc4uXgds;",_MltKV7Lš8 9T(5TH p_;KeYJ9-UAXI.#.#b lImUdA)jѹMF4mj!6I&̓&1>!m.[R>]]+.]xjaJ-#.#6%dAafȭ[&fVI.۹9ML5##|[Au_kA:QB3e/F ws+-+p.QS&h%mKO֨fö Ҋ:yG`$F"|hP;#.aH4K_k@ip[66y3xpBFfL[6knk׫TݥʗN 2I+Fmk>)4+.b$馬˶?mS>^O㍳*0h'~;j0պ5-NeF9HGIr,=4!{n8n ~:ggЃ`-@T/?D/اB !DDE%#.&3R60ܠu*:h$M4h@I&M/WX(,fh#6l#%S`X nyzڹiRJkVr2L7krEERJɷwXU :c7weڊ(ALoܝX-=1&XL9! }IǾ\y$WG~E4mS_ G#%svYBog?~A%*jڹۈ,#6ѵTjն5mͶ-V6vC.'Q E5bG9;#.Y4(u;kۜ+4ֿD4$S&e3J,)I)5EAΤZ1Q4ԡ"P~ܡZLY%[4)4̑hEQCMDFY)PT%ңELFhL)T&%!MAALcH`4S&ge/#6b|[|X&A~lU_SBݘf̈;708Y!,mfvs9` 8#.4czKj\ 0]iټ (5zmn.֛,mDs0xxN;Z-_K# }YJһ0te^fw~A{*񌎃bm d Z^o_bc5#fV*(i`!Fa><90ݽX #6"tXa3ٟyNUMaYb†+NbC 4}0_[/cyOW}#k#6xn4O3=GQP+|ɠV_fw&}z^ p{"r>MG]eUY#.q E)AT$/rdǥoK}eG2XiK􈣬%x4mcǗ$I*#18--ߣ8#+gǏd C֓Im{N`g"PREΨ)xcB?+#6cguVˬ!EPӊ#6+G0'#66[G8 pI5,nm_&ɂ2Wc.["^7L<1YCu-4'ѯ$41 g OS䇽^RA虚O?M" CqdNIDBxK'٪ յގgd6E)Fm2Ib=Oi,(5X@\{w7g FHC:ڷש']z vBx}I\wvϷӖ_? 畧ЪX@w ߜ5#6)~.wzD nGkBkjl8gm]%}; f.IG^fPs3 j5'B;Cq #6;뀛<r &\?#EC8(p+x#.j_N9ph>?/>50Y7Q6ȣ0Ħa iFELŒu530W>ًө<4̵<3t,ņq4hQSRJb5D(EVL›WUޜŵ (%?Kľ$.1^{S{eL:"a *cuo\ a\MLhzIƴlV#6'S;ٌ;WGv8 acg9t!^ Ѵd5+6 5#.,0&bNJ:~g]`ɐ/,7#6R[&*_4To#.?02!n6#.ΩсĐ4&:JF9]Ѥ]Ҭuay:0j2w67LL$g}n@upB텴ց=,Y?9@íRPɬ@ZlAs;oNqbg6fl-&<F!OB|VvDDr/,ڋz"VnfrgSqH·:Ӻ6=V_-eǮƱmOt)0ej=1F1WVգi7)Rw_<0^Pd>llg:5%GQa3:;1d9LJn5͚']ߗQt\P##.'yJX5\Fָ2L/F.ZX]G(jS }5#6jVͶ]*XTqdFSBc\ll^s9*N띡,ߞ]̕O,K+Thu(v[=eƙCAib uS"q0V^ ZkkO+gX]Ne6_y#nukct9)Y69Znl8\e#A#M|fS*CP7vBEVU;"֓X.\[DY\('>;8ghh:yYz&YNڋ0,iQ!XlhpNVNځ]#. H.q:RIFȡb<Pԙ)>4=#6YE&)+a0[3A@bXQ*1X1qIN~.2ߩh7)nepOVRQ9B&P7sinc7ۃuU6vublwsaevfcy59ݬe|[u`z ۙ L>8O/ R=:%)ǒ:{ZQF72f2"t[LLLb|ZFcE8S7GQgrܨ;翩1W i#\Z%pUX6s2B9H4q:jn21 O)&PmV\[lf5,yULb0vB&ҪCr# SH^i!ʻI>v8Y٪WaEZNχ+a'gj0U☁rӊ*L#6$ 3v{"aN+(0e#6Bu R`hÀp PI,#. fF#6ڰڸcM_{Rƣe\1f2R;Zaf%ñb"zͷ9V2#.#@DEuBI@as#]hP h}uz1U tͮNzzOQ(TwqhR`pZPL;7CN]"̎̀#6.s׫ZÒ4' ^le,Tmt 쉌SR 3 @0ty\)&#.:b8!K@]Nc4܂@re53Al2ȈbiA-RrvֺS%#6# i"23SdtvNI-"S#.['2`Rqf d5eR#.XsL%qiL§n2c*<.G (Ncim T&pL*[#%X*tvmB $I3/ObYDw\#.dx/#.Ӊ\l#6e47U3}/lpNZ)jx#uMXNRҬΠ|`5`p3Pf8&7Pn°j;'CS_&ӌPiI+]#%OD,ze̲9i7R#6tS0@eusǁM׉#6K5BE !UAI HX.b004ڐb;G.NGIF1T*N_;QwWm3oA8TX$GaJtކ`g N }[aYIB5R[#%l8q+Cȡ#.F+2fis l3YHu#.Iawmw#6naqʌW;eU 88JD61C#%CGf1g$NɆ#.f i q0#. ( hLJII4dpjri3f̺Eِ9Fm0jJG8gt^Rg~V@.uXil4I%lK900[Xn&uA#%0IF %#6qC$R5@w;iSh#┈1A6*?g|z(W*I"H#$d#%|j?3$䈌@q::7HhuF@&4TC8 #%-=k<~X$*vʪL>Jhi/'س7RM߬YEr,;B_}P4&A\s'M.iRk5Bh7n20+a7u% exnX!Ս#%c=^4pu\0R pBev#6h"Jk[m&Rj*ᚈPRqX1H_D#.o=#6JABp5㊡\C$;8SoW4U*#%nnR 6._SWwUٍn.nn-sd5<Fȵ*}A<`PLv*r8#6#%|(u/qz0g-Z" ({'@7>GԻo?62.0V<?|~ r=FwEM 44,;Si#67Pon Fbh1D) S@n0.֔.eA2Aczf$YЂQ#.cG3!:?+x˖%A.9FKP?V?_Ui&RkFQ[FIDL`3-lX|Z0POҴTR᳧zK:k5vz4D+Z"kDFI0#%D #EV-ރ xfPD`@[΋I3_k̹< Պ~Qb*Hj}Ja_x;w$l[^A4Ldpb7N(cKi"F'f5j]*MKoB5L1fR@l@E`+KD#0QR&lc8B+ Hy˱JEHq @QUo#.;eK'(z#.sdd%$Ϗ&ٻ#.PԅHT]c(iEj#.^&АZHp08W.cc#%m LNLuY^sŲa#%=wKbmlc+A)aLDH -5Iq0]i> k@ /_m!*  I, ݏ;dD_&,ŠúMʊ6 {8Q`oUNJ## qe]gRrE0;>/#:>mcz"4U#. 2]l0jiiZ}kw 3FCzi!Kbg:'5bCVmc{^ZH8 ˿+#%ӾZ AI4z^ym'Gi9E$P%* UeYNB1B)#669sypY.ݘUfylMxkDilʋ]ѠC|F1#6h7nbC#%ktE.J ԘDbTJ Q$أJ#.L@c#%-P$PR[ t`luqlgL #6?@ B<5 ;+>ER+ɟzM8Qb{(=G+t ~L>WT/k <1GA|A]fs10#!G"dFH"bƨɶZM "BC5 ?=xT\KJ [ikE/k|tu+W n.rr$E#.{:iY%leʵS0:2KYk8ijm&&dv5-zbScA##6pH.KӃƶ>-IS1d<SKۦ3ے$iȉ3̸jMsy@#Dmã(ьjuN&Hmr=fWN5凍˭i>S)1jnI$C#:y>*iyXzMp\J}e)3&=&B"75+cdrpQ M=W0 arr1@tXEDLX%ѵeB̴˕kU\^y^ƺi ,sKB4!_hRM:#RD͔Dkm^| U"hI1<jY*dK 퍃0]#6j&nu&\ɖ*lLi[c]hTe?HÅ(ѳRme-U$f5S5ԵҴ5SUF6^xME *fVЕl҄T7ytccQթ`IPQ6@bQQPX!+D='qz.kWaK9S@6yO;ngq Oz,EqhKS-#%DFOXť*jXɪm'O#;LMl1JS "[KRU(J01!UЊa#%j#%)"n͸esf:fsp#%H5.Gf` #6 2މeRDT5yk#.#.=A4\Twm?2sO^nU \#%ruJo%ŀ AS(L!$yB`w?6lx3ݟbL8o\rEDS,`&Q7ht{+on/1~bx#%.jޡ ~0Tƹlod8YG|fQ0*mʂ< h%j6E(eQMmr,kMc)m&6-")Y,JU,g]aM)l1&FڦMӊ+"+ }^ݻ*b(2#6em)IkjhƥWƯ~l-cdHen]iheRMKmMl3#6ilMKlcn͖;R2hkRW5xؔĪFk[L7LUqTǎR,^?/>w>Bms'`Oe3$SF<XqIFzTX@񘅪m_NW{<{1MEC=#%.b3#E $T B 6أ^S "-J٦m鶱oK5soה#6QdFAR*fSJl)iJ),E֖FR3E E&KmiiY5Қ)R|V (-EeQ-%(JHQ)M"jLѰ1ba+e2IdhXԚj#6RT Y#jQ#.M !BJLX)2 IJjƨ$mE,Lړ$6֥ɓFқlZkVҲMԆM6)Vf6{*zV-RmFդJ*mmZc^6g]! n5x{5`fpbz7WkN-WmE<!#%yFoDz$>Xq⢵E^\$1ga>kg]7b٠3mez4:.L@ݤ"+Ļ/7_K3=n9=C#6?qő@P \cha! )]28Ƙ (W_@p$ -΄>lsu)P;+:lP .3уILFz%;8mfЫԬov.yg PM"~}b\2e؁WC|sл\Lu#68TT>KtJZ~VPv Hdְ# = s~OlqGF#6ƺj76Wi0#._}/[onӀ @#%#%ljʲN"Q@R+r'8#6o("2! Dxbu^U=_v҅Xd[&ya&\TCA!$*c#. SL`OI.!&*xVz@]7=v#.W]{|)LcܚP fsD\Ox#6((S)b#RdA] %@gaZeF6HF&cD2L#l#6h{C5U.m H)fu'6ơXVFA4^Zj vg6& b SRF[K#6@QI&#6L*cR⑿JT#.t#%6:^tqs(Q jQhXzA;Pl|߶4" JLhJX`8ŤM5k"k;CXxj/z/#P|4L`Zh-AXAKۭ[E9b$>#%0 =V.nkm>s gźSi{HԷt0Jc*$L¼Z$!$wI;tnl -i!I#.njRYVTʁ[lc0XlA4(F/nOdʃte&YA16a2e8Qv!h+br@Å/))iQO9&B>|ɭp*YKmji!:I$mDVCCpPc&P1ssgٙ .!H #6PR3xQȵxǙcQ:Ts hi5JKF'3GÜysKM:]` 2UKՕJ̫ZN7~R(: @"ȡQffđiDThA~~N>TPQpGǚp󠥀CaFE<Q#%,2䰮G&9Ť;1AuȌh)`kND =#.Áq [tcv#.iLjD.1V&q"J5`̆/:lF#6ROQz#%=;No+fy*wЙF|"TXBSM4DB *rJZeE@rfP\/Q#%(tWx.ǨP QՂBUN#("X2#%LFM`'fT(#.2p.=Zpv3{raY &&4G] :\cl,u/ xanG$46PDkIt2v+,u<xϹem "#%$l`1R{nz3D2ә#.Şd߆nh/!ݭ]n-:+~A8uN35P pCm$/R4,í1=;Pp'#.> UGLc0u{2=A+('_HCX=DmqzEn$ON-ˡe#6SY}Y#6I %ݣH?]$E/]j˼nnșHNZj%mjRC@mQc^:wn) 1K)y#6QҐ4l߷w7D9;nբꙏtN(5IrB̩[q_SRgs%_ƗM~%Db+)'hwJM05YFqt "懲:ʭ4ѴLZQM4jW]6rbK$ج[FߝVbVkJ*K-k嬳oki[V*rtt7ĸe h 'kг)K ]e"JJPB7&r2(-o)<( 7 uh%,3al! hv>3]l4}X(qOtT5(i(#8+!iH@aS*VzPT*dWfTp9P5tQꪅE#5v"IUl#%&+t00pF`&,܏}Cpt,VpCA2 o|'Pg=b,q B=cik=A~x#6gLqˀ.H\h]I1#%%扨&?dI~;#.a aQhFpA24 Mj-F?=+Rl~C~$4K^n&߃By(`=4x(C~A@TTвa81#6{B|<Lu߻/6;pt6STSbGzCw.;=#%d#%RI #6J*#6DYX@PPflY!xEdBD b[3\0_aˏq+bIbNQ~0Gq9eSQ~j DP$SҞԣCN/PlV_UAd: C[!ak%E IH^EWc|s RgńX!`≫r'P;OybPWG>'`y~6ߝ=a,rtԟHT̉I9VTSRT IPцj,TU#63SE,#6@I#6e,%ZeC@Q>9y_-6#Dca]<>Zcm\g:F0 0#B &DeıGf4'F4҄qIlb-LQX(J*Ef-H#.Ԓa4J@f@ I2Pْbb$]LR`MS$ ZLibm-B1+=B`HVZMF`L%mV>L䱼xaEXVÆު AF4gJZ,jj0'6Ѵ\6rܾ#.c^.4<-14\3lApjӴK&D`:3k0,$iKbc,PyA5&ΨhQ.H5@k[R5O<#.C:Q6%e{]N! uP2h|imO:4Mix,#>c!Imwe7Y q1 +p8#.ߣ.H( MT?9 0`1 8A%Q#FljTj,TIn0Qr]JƬQmDbd@b -eAn*Z$A$TɸC )ȰD@QHXHEǼ?X#.Gꂤ@Yg¬{S(kf!W]n[erZO¡.TPI&#uV1 Ng["ALlBPoTBDWUZP4mdJT淍V̈+XlTzŊ껪-zjב/]n\|ךZƯ7uY$թ3JA@G2<㥩X|!kkE{\5pʑE.I#%}3@ ,e rIcQ4״0NK2#.#% 0#q MnXq[l'}@a%ǿ3š74&iG<!DWbUTU˛[H=TvU hZ7aqP׈0a̰oˎu> ECObȀ(|Md=` T6qr.;Q$!a^ ҥ#.-TiKJTcj׼Uk)WQE%D/9@5];c{nJ*dBda6'.vFqpFCN3BKQ4]#6Ӌ#.g8bCְ :P&&42iPZ0#%G{" M18=ɉG`‡+h}*M b]OCXdI"BMQQ<nF^Ϻ!}z`ûdW#" %54I7L03'|@YA#6?KmɃ:Mlp(̓V "ԧVl@,jnF@$+RM6SHDB0 D#PN5X4O4D1͗='155{:eQi`K;41$еcF^#=ΤA>iTSMZʸk2P|T5PajJLi֌e5tA&2jܶe-,`LKll6Lm$J"3f4M5MUQli$1H$#.GiGL"8lƉ)D MVVŮkvo^aQoѴצA'"vC#%#%FȍD*TK[R]KUm~B #K=Q_fHEQ GZ#>adzApQٸhB9!OGk!' %J؁!NHl`G>B zAm#%܂%KtDByxq;8<I#% CPŌГyzԴ!7$=6*a x2^30xp#6XXPȅp>WU8;g&Lx;Q7VZ֐K6x#6ءp[1v*@7̨ݣB8#. \;:hzp!a#.DV#mbL(4t!#6IO;NϔRb.-+?VF0V~}мmFiAQ:ĕ#%>#&!-ctL!/ٯ9vWԐr[ 9+S:~rt&-twL ,S4]P云C#6(,HQXP9x諒#%2O&ѬH8ՖY!D14P)#.h4L*v#6HLMIJB#.X11"lN9anTGd#6숒6%  `6Ĵwc&i︷lC#.@6dZHfhU)%E`҉c JO'T*cmhF&(}q#%DB;sR; STyzg?~<s/T #.\/l:o\qF(P8' J*ƛ%S -l;wDÑ@P@4B PEBȔPe'#6zxvi=>~ Qu}&`]|N^@UBfUЪ`EZ}MI#GG$=;}%&Zi]4ff٫ECnBvP`Cqh\5&+&WsLjF|fw{rTP#6`R#.!%r)(#6a<,<[މK:Md.!Y;0UQTY"$D0BR0!D.bo#]̚T`ѱ".)drf5"^ ih%|#6eOuBȩPW.T[#.i?L#%Ez#636~*(4ͻCVn=辻VHfmڨaYe֔ je 5VWE[$FBNy3, /Xp 8gdp:>j"O=HEeh ˋgؤHޒ%?^uFVmTSĺ+njB#69Br,81rsU:bkg:܋|Ʋ:#6K%$,`#%*#%A DPȊҥh#I n1l+p<;T) ٿ:Uyne#6eǷVQ12Q#%dd!CBbuE!Gv#6($U`ѽsqKѣv\5 H%A#%Ν|:LOBŜN#64/ LdMr5wmJIǒBHT*F2D#6 _J(-!n֐wrPl W1`Gnb bFϸ:64kR&'&{_)wV7t\-25bB<* \KlH2Ym@&"-Pf{n{G.z&#.zP1`WP2O!-0ȔfFD$ #fK0#.`mZhmmؕX"bͲ9Pí( XmG0.M쀿˜iLI!1b;EP DSqScvw'祥U<zNu 'IfbWGq'NP!#%ĐIm^efʍdj)}ʾLU?OǗkە氵0}ސ^xa6<XJ.mڦv"OmՐ}Pr)N=\wˈ(ײ|J+T-`U vI{!WoUx$UYYya<n6I6= q.`vǁ]to +M`sZQ4$n@tp&/mHF1מw߸,bB&3k&72^A#%4߹LLK3Jj >E/A!~,v7ۯ#sz%2tƶBÊ -*F,"s^C^GUG݂!p6m=m#6N0^b!d%]-.JZʝ0O ^!0ןb-sy䃉eSZXKfsM%8Tb\?}{>%a8qgd;1C ) T'd%Bnp>f>yKp#GsmGycQۚ%\U#.$HH0QKACoUУR*s$.?K b\ňaY2J2TX (Vy۹uުZۦ#.1AA( ^3@dntyr=EI&01$ե["'4|lMGQOgC_4wA0CfeEpIGG$끪UtZ0hk>1"՗,W%F;q56z{@@q(yCScp_{J!0:t4QZ$\v庺ͩNݵu&4\^.^]Vl@ #%C@US_Bڔ5)ALjrS9 5TXlzMu>#.3k# j G#. 0nJYUiK:ap<h6`g5!50zp՝H!upS-VMdmL܆6u|#. @{&}vuE'sj>Z2nǁ׬-Wß #%`+ގɬ'm'#.|_uTF#65*oX~\gi[4Gk]9*itQ0#6'wgo`!(RY} _b.5(,DPRSX%DA&?3Y{Z$*mk_z.#mlAA[v,a$v+'C-H4(an~#.šX>7PţFsEӣp&K~iKn<{6gTSE_ϞRI'E,#.mQC^[#6<PiqL =Lo#%{BhҬ܁ls-jg0xAӥgLeXdb`Y)0(ϐQ3I!aÌ:ТIe)Ղ<=c),[I _3r^B8i5V-WBCGcHgMard7O1U;f,1!k q.aP0OvѤg NJO5Ĩwy rm|#%#%p+UC'G j&˖ ![#%BOE+QZ5mdƩ5Y,mb @*TEM25Wn\4"Ԗ* ARUz,.`!i p:6vg7nY;d(])5=:C7-\n}cuWC2mFm CEli 2dS ~(Oy!wF?"5j<CiRPl.@#6%5U=Ѯ%O?#6\p`v㋜NJɆ@;$s]_2BPV⢁0`Cm79FD7|Y&V,[}]t(L:"W.{@,ϬGo (TJs I/#6yfx&qK*.q<L%#6eq}Kw4(#H *EZ#66-5W`[@DFq#. ̪MZPwg{7>Gcc05⻝vo1kp&|OYQ>#.zޓŇ'kjaАTʥl$ Bm=eYb]ع`#%.L?ɧtnu#%N؀L81zp#.nʪKqt/B+5֍kYf2UBCYI MIǵ܄$:¤*uηLw]#jffPh]u<QfnBhTci0x`hpTZJ0`28hJ!ѭS LkLDa6(߃pHW֙%Do!HHIjUM̋6J\23Y2 rdŠ#6&ۤ 3PE ZH Z@ 0V52lfS$e6,3$e-/]˵ri˛rAf[sNݽy"hR3wٛ f輸"FJ2وA\h@4Ecf16#.j*cq0UkH[yIQMҁp&b\3 Pֆ4ս1ΙEnՊzEFI[#6<qTUu5!(db吴 #k7 +6\]\Y"i1 y ic$Ii%ehCVc"sz(T&0D1~/aen)ᙐi3aCwc<j[J\Mb0s.#.֤2E5`il(AGMٷ2ATnܞ]DO3JiAӫ_ݻ8+skh gjF V4.#%cKlTMX,A(SXzG 2#F4Äf#.nd|Pk#zL1fݗd6ro?v:WGƎ.5FcmtWǰ1qƌM!jMVoz5 dvUjhٵ#.\"9lT=C#-q}&0#.7BB]&$,eeUc GO8a) P4/t֛{5E]5fT@& 6:  `"0lU,P]DQ %Ħ tt=An n,8Eiqu#%T#.Pqbe4~(Hg޷f='Oq6y&V?狪+lMHCޱ\#.h#3mK#{{Nˏyj=y\M\lّK*̵Jpuj-kٻqFb!$-ۓټ讹ۄF6q8hPh,C7ふCdqm#MBy|3y!7稺(4QX0w,h#6?!\o/gg^ij#.M,=6ͪ뚷곐xFpS4쨉F,~Jֱr+id_>M#6kfl#6놡){={v R $T@# *%l#%H*GBXtL,:jU(lF `][Iwbםr髵6r%HTwUڱZ+U[[I@%rء#6BKI`W(06nnaI}JX9*ae(Q#P1EZ.pRշӛ͗Zr%&X %Os}2PBTBeER_QMZf&iE5E6+5IYLɉTDmLɲlklj79? >ߺDEOjM+ RR9⟋^lxe˟EӸz8ӝaT9h !M6Ѿ7ѽ]BfV#6O F6ӻݤKnk5]+ZVvj(JEHf;YȌIb2_l($D@ :o',PBM4p#6a#.gWPm+4 Nκkfֶ!4>>k6wĐ 0Ϻ訅]hʈ,`O]3]7Lf7?,lc]̓MBbA1H4id8w&N)Ĕv#.u?! 51<t2btV8U;"ǑC}tȎ|w^]3a8f=6̈́%~ѶzW\=XZha(ƙm<HUvЬbEȝ#6N۱=L~+"e)~V<0T&*ž2uJکXBxQG`h -TѪKo/ZB2$"c\I\rh{:UlmڹU#U#%iy?3vLJ$e#%b ZTy/o\.@ں[%]-lfmBl@E1#DBR8$j\0zF [oZ[[fIlm\X>*˼UDF>wWْKh*7j*A#6V(1*AXU!R#.aS#6$DQ#6#6,ʎXIvUDdА%0zh RLcAAT#.9?ϝ*(hy]asa`sp%Xgՙ3n#.#.H9F1KQn mkU~[` #.;@@HnݡQ!*'[Cm/C ;&ƽqj#%AD^G9z*J#.,Xc#6+fz#%fθC8>yvo֢JF鍔8ꦈbCD#%E7G*% t4n#61*M63qM!!ȵW|TaX## hLCZxbi(6t\YlEX#%$p4b9&K`Ȱy~5D_ #%^3;HנXYU["zGEMpCݑц|JNqI.k6#.?XC߶&UMWJ(]eB%\;(BLgޭWNWu\vegd!Q Bt?g> YA h7vgy\2嫆uf"i$l&ʖ۴&S60xMܮU͍w^vo.ul ۤW/9y<urk2Ƈnjh宛VKhԥη),'5wft$RќʻeγQhX"Dhm #6XTbzvA؉CH#%mo;T<8#%G; #P A;XalEx*oNx{SpAJA!E #%w'+}uh%z3#6ܟm:r#%r"1 VkZ+kg #6#%Ξ6S!5jjХ5{|1PA!%AbBVjuԲc#.ݵZc^li#. 5w!,8*N߉D l&PtIi&I#.֭|\{U۽(DH"^b#6#.)k`ETb"M(0'n\)OVu#6'=ؠ1HAcQ Aj=a3D#%;" T͖iJfM!/Y@sV2#ͩZdQڅD&1#.@ ˆHH"Zf0mFkBkmˆ7Lvvk_T,T $6#%DOPp%*CrW6}w~|d`B2gC#%aaHX4)RkUMfOv`iΌlDHI'2'sђy?b@coS5cX^-pqZ#%#.IY%iQ8$M$4B£Q!.pK#.dmsWI*^&ŭ*6VjBTBW@#6#.c0LK1B-$ok#% "JM!hKE̩#~N͊/8o:f$^쪰pք 331(`mx]O,).d0L\Ҳ u"b0h BXT+"7* H4u͒it52LURnݸ6 ht#w2H`*{Ntp~mfEDAى`=aPP&iPE*#6E#KT-ѺZƞ^v0lađ$b St\vݪʭ[.꾻mR׺؍+fMĔf#.-RHE9xOd#.yþg2>?D[a3FAR1m6q#.rPqP4`!FDm6ɒc$cvHEJG+2ĹR&.-S9;73P-2}ӒxPצ!DO[m4;CAA?{@~Fʈ;g#. ##6&dT#%O]#.0UX%H+Q#6'dg";#%h#%6ӆ#.!]zLɿ$уHX*fO'"[>PA#%GQMA={}ᧃvY)uA_dFϲc[%oE,y J2&ot;6^ouA(77) YLxl+ۚS54w$;8SVT:<t(HȪ',RڀRgpoɍ2"ZB0/x~TCIh&"#6bC;3ْ>Wc~ q'lc0U>L#8US3Hw B?kz&)Em[p!oqJЗY{L7LAӎ V0>BG.#DDJ )M+Ik=i4bs '4>}2ksH|DaXwk}e&fRyiΔK=iٰ y/u+`x$0}]J3d-^϶9@Ѽ#.3]7@XT՝F{벛Q2fqLd!o!t8<6c8CgB~QMKCm"9n H<J#ر#%׋1Mn xaOڟNuQbz` 74*9Nt Szօ8 zJ;ExW3Cd6JH a`&C! <,]IZ:}P#6f27T6EnmFؙᝎOumoٜSV#6ԒI&#}3lCY]6dj s݅M)gV\,'r8PC5U4v!yc:pkd^קD~ ??[TEw;kW]!Aprmd+ #L` | P Hƹ_gXo,i\ Xy)4`8kgBCHNw3Uv0/n83'=1Mwə1}2xERPL@עLQRI5=x_ZC#.eGke(҄ F@ٹuWn q/(o`ĂHI/#% $;H| !8$DYDvtFX*G^ҙ @E8*m81P}f>ޝs#.An"3-GCt.9#:>pwP +m&#%fFִd92hş$Ey#6,:@(:xwu#66쯟Tή>H{:rzY5z[(g 8uOڨ 0V#% FB$<jOfD+8Ə#%A)hB+ qk_g??̬OS/P@JFQ?2:߷gdH5R#;M羄#%x N_n1lX+ g_4k3>Vgl6өCqEW9v@яaN7 vV ?T'e;}`9=A+/nwBv>U;QUΆRt:`#A'B1a+1MpAH!OmmCP6*#r=??KTH?U7IȗۗXrxi7ƚ:t^ͣ!K,8Azw(I hQw,.1Zwb!&t4!.2E)OkR4?eBF1e$8xnnx-PɌ^û,XFKP<+wOjѬEfMRbZRMR(gfsʵ$u.kOU;HEUԫ@ȥ~ש?~dmeI0hmLBLhb#M*FJ&!&͓a#6$gxɳ':7ܛo'6yRMv̠0h4M4aK7tzh%*M^&y}G1Ԧ׃M6`m8@{*E#R5sQ틓ގ&D +fCx&cz\/ӆ[]4>nܟh#.L 9obkMNu.j!2̺ju^83f[G _3O8 m𦶰9T/ 3#6Rim#%F"[miG#%0NPȩ-D#61u2HQU;JnVݮsuukYۉ׷7 +l4h@1A*%*@’&M˘#%_2HAB,$֙Wrrsk$3*Fד2LE:UA-HK29XarReF?6tees4~/c hX!#6Tr!S/:kz)\Q!T`U7*͈ll/c\(!6B5A;ͷr.xǮuQQiIħ%4Ъ!h&M9Q%SML<D$AVeCbz*:R\+(mUš:‰WT7"B\+-u66Rʖ Uj<«5C`5&nnU.*.TZ%&#.*AѩŶeތ1'39Da[#.iHH7SlYeUƫA@; )ucN }f=,$|WoAR<&QAg#‹\\Cz!GK1dR0qy8B ټ1EMjJ#.BmKH'pJ*8p#0oŁ-ZN5FA4B戠2+j!B SR1F/40#.bR sȱTnͯ7nnXʾNL!ILsC#&DVE Fi(a S#6H-2@aZM'"1Aƍ<j˧lUM8KAء)"Z9fJ6ޘќ d'̓4ǸjN0fii(0bQHD5qen#.nO\m*%oC#.ʌMu…wjT&Cr@ޱp6z$&p@V[iYD(F)96pҡ& 4J(0\0 (!BQ!bJ"4 ՃGMI)Ǹ9pd!U![;+QbQHCTEI;ڋmͦl"jC`.3#64J("#6H==Rc.e}>RDFY>REB#%!TAܑB{T}*=U&00ҠW!P':Dg<F rO#.4P쬐p2Fn[{k;[U0Ir Yvk^(RLdRuB滳qP(f3Т#omB7\㌊L6-"-۹Vt$YIkX$Yjt/vA>#.H1G۫DPV( NLar($ВwRb4w^׻C/X,^bK\ٽ-!lMv|!Ȧ7@_]̏u(7#%˷I#;}+?}n*>[{oѤvB ~?c(G U#6+杂d礇콦zR'j[br#.v#%ηUT71wVfmEßX J^v# ECyƱ, A2AETNRQeTމr\95j#.&t0cZb8z0mEfHMq4 9 #.UHr\vV^QkrKhKrK\6UƎnV鵷MY#.cnm&k-͝r,mj Q+;-#.w˻$v07<}Zkzo=H^"΁!7@A"#6ޔ=D v_oQy>)G#6"4=$S2w@d#% Gq1(IBH%J*N^T#6Z\#x b*""([u#.mt~{oFRd%T] *Q@QHѶ%[~k!D,#6B?@#%$Cϥ'HjMFF{ZPuPP#%;f_@OA$;&Ԭu`٨3`:H#"&Q)T(uqe-L͒IJ1idi66hmIEbRRJd4)E*kjZVjZ52b֋ci5[Ϻϝn$cu#.Ǎ"6W"Dne6aPA$rtӣҮ=t<iqDO c!&$*+hlc H-a"#6@`i#.H 4ECJP*(B*Z0$(B #6Y2?5#% D% %ݘ:WV(@d$f2@M%^B3`N"vmwcٔQRN {ABH,OVȌ5Oȡf2vpW_[,1Z%/-l@ҨܨMxFߗj1pV!TH!PJu0UR8GaܜNFnɼ˩R/]sQhZp9v?HEozp2#6e_j`8C+dX{Ap!>(YD˳eJ[LTzF"``΃ՁRՠiJ%MYS||#6uֽusFp"}rC"lʪ<oבMr# X}vt;?;#'Ri@ܡM|]?ί^B}#.#%`A/ў|hl\1**;3*T4~3a)GZ*zUƶ` p]ڣ]u\ʭJQU3&eI#J{i'[lǿo5٥'Q#%3!O"eBGVH#'53Z#.PRn tCxmUy/VA<(}Z81K$=E4Bl:ESlW_׹/#.ۇQu:vG\{KX{V#%L#6Bk7(/%ÞԦb-ĢFix$ZF;vq!g7tVBZS/ yo#߫%&sX*77/cMrW^3$"Q?:y6"ƺ{#.V~ggsmm9)jWMmf 虘8k[]6rXNVRTYZ;grsByo,pѾa& q<jf)nۛEŗf5w V\)2l`qDxf^aTkĆWnh6m<py_Z>ؗ[hNI/$f7)֟Gb$CÌCC:s/}wʀDJnTQjPxbZ:>춖9W_Jװyixuw}:甼'u{a*^WW0o򁹘8Vx˜|ʼntCVo#6Uvga*F򪋭lg/=5ln.b</Deó՝<s>NΠlrA׬<Ec!"D^y8ܣB6Kۭ8,g :dDLoY0Pڌ/iys d)7T&hdr#6%#.-/C&d.]'O8m7UI,`W28N$`.X@=ZmRmf3g#RcQb3x00_$gBț0ں9#6) 'ϕǖ ke yc0)Հt( dJݧ-AǓ0<^ G& @1[cUNcAn>Рo#.(#LC#%@: #6`FaPa:L6 x`v`qG=269kSAC#%6s^zv Z3K3.&˘sx\#6?#6_Dd\PG`{uDO.O'6 [k,* Gg ¾T"Vs ##lNƹkFaCghz:癎>cETQSSNFb((SۜM e{A]k]zV4o{#.U*#%9G֓6(#.o϶;3\4G>FD(ѽp9f7Z0C㒚֋N21D!.e-lZ'UbQء~%@viVkQ-29ys83D^[)Xɚ" Dv:e;F#6~ٸwÌWoq̋';Cs:#.a :YT [4v4nsx; 2~sw~/j<\WtmG^5&ow7 A%#0M8pq<~7Sۻ߃LΥmZ\dQ{x09hRĥ#%}D4bͅ3璚E=;H>.x[CUZ=7,htz=<7~OfBEo79qLB6Y܉Y;W \q.6 )sf\=#ՃV4(v<@ǔ,,g (y zw-H]BEq3:f,ҭ'! @1HF vM\; 3@.g$$VtIm-SL0w0'#%Y"aU]C#.`f+um?NCw"0)qOn"xXN&y~(p`\r P <߂(;VL{U#.>xKf w'y;`kphSTCç2gϥ_VGG8v'$ODMN};O[Tb,ҡE~ eDamlsԸ#I#6K0m&@<ƅ(`YF91!"DȢEXB!$Jս+F5nZZ"(ciFPTҊaQFR0!\g&x؅ @L##vЅ$c9bmnEɱ*mVUQUUUײ܌P:~C5]c-AKx*1K+ud/io #.hHicf-+W SkN4bJ:`@,זcɠ(2tHqbePDiOR;]xa%dH!V7V (&icRE,%Ecbi4CZ*=[gAuwLtd>lL!P9l#6@Y0 fL7zU3RXQ6֥4ێvFjhMUzmkZALk.<c9eoL$DMqP\`SZXL*ccix12BC$Υվ59;haY SJ!#6A,TP2cLhǻ*sPAnU"^Ck*V%+XIw&]J&0ghnothsiHN'ai]P \R8F0Tj?޺֓cS򣜵.CZFLdq"7*#6* Ѹ0m0m15+Fn-,za[ tC 2CdT]&09EZ*#.hFemF?S5@LRg%|4k h~Ɗ$*7Į@zi<D]8& .r#.VFq#BZYmrm2L䭄lbWfJڢ&n\cFV=^1QimX9Vޟފ=Gye}#611("фe0Qs zXr8cMA@=6alS!vH#.0Ռ 0\d#3$d9:WbTFMP1#BCV#6e-ͳZ"ۭhDF b0f8҆±Fx7ԃ{ A6&'"ː9#.+UN!U*2s1M`C!0rc$n瀿+iL5M6H$䔴$zO;bCHwڪwU֭4*Sm U+ {}E*XS^#.qeӦ3~s+?2C9Ag**JAF#.oBxzQ#ț<*vA K?~Cy\Ube>\f={p1$a0{w@y9;bƪ['0ot<(k qΑH!Q:ǧ @H !5i WjKV\Ջm]7hlPPHІi#D~9ߥ}~v"R(H#%V Z#61&1 D(ZUil[%RQ$4(ʍ͡M5#JMQJHIQhDKFHS4TȥSFal%(I1X!#%:xg9S[4Uy~Ws5W$$g1$8)ڵW#o$VElO">#.4~Hgahf6ؽLKnR& ~zkpC%߁(2H\9q]=haѹS(BY$ߴsCJFqyڵNj{!:o?Wjߍ&XKX֭siBQݫC!bRbXK20'uE$fP#6>"ٛ).#.>+Qb0rcD>n.5(XRb"D.f+@6lxRXV,I.1{.Гь6&sDܷ/PQ. a#%!`СGLĈĽGI{5OEɟ'& u1p6eJ'LHQb`VAdb(#.LG`6 kgμ6r1JN2,=4,p| d<۫6jHElm#6#%("3@pBh_*UYU#6!պL@ @+-= #%]~7c#`>Ͼ3CftB$#!?"dꅄ|پ~h%{#. ,(`塤|ؔn2L3@>7Vƒiy°=ߵR|2Zf5Uo[52UQJmZJK*G;&u qm g*4-o^n2nF-tiD7C56&TS` &!*QA r]u+NAB/{,tOئӒrFAJ+)( ;Ns>]M˰#.L|R!)B9^jf<;#6A0xw;HA)4gfzqFcEk|!uMG&~#%Q;t@d"Ȉ*#.>/}Rb_cT#D:Ζʱr'5>|}._Tl=Բ]4X`C$0V9'Hv'FIj &g@>3#%pJr`{7s@q{A/U\zge)^=sDX1ƣԌ吰nd[wofXg)P6B\+E,3̈́JSuUtD19W53pTl#.ch` 0pP#.@n`%V TU*2/&Yr-ި5!P0E#QHa6P";^-@Y1n5͗#%1 ,!sYڌflzٹciD&Ho?/3нX2(ṵdݭzYg$&5D EPڥHA~3#% LC}Yf-"}qIQ%^FZTzHK"@Spu^<o.B3@DmjtalrX@zQfkQ A(n7۞W ":7`R7{BdXg&Sf% 0,DuS2cre38t#%a%筻#6LpOpߒX9 WDvkb+L#.ˆ#.BC7<D#6H2#% !!P X#6ɊH y-_F<an۵So1[VWe 2ptc,2i혎s4F!RAV,ᇉ$Eæm;cHo!@3k9Ç_'LYBi1@'*kM.ƣ66 AmL\RE΢Fe%oݓ*,u`ё91260o)i#.&Í@V$ltvckrF)KE M,vL.\{՗[>!mic#4!k}avߤljW'7q$[vLUD<jlH=fU̜L3a萒'>4OЌ!ZS5{XC&;8k\@r2n&='IZBŠF՗,\*V׳p`\HfJY#%㚨"ߑkk%O+vv&?gr{޺(UI XE#656ɛ!dhm^]04qs:0@E}hnLe}8܃^-1|2õ@pVכ;"'塄X%􌁖#%5`+IB27t#.NZũBAb+m:+BN\mgY[6LVlaDg@iDбP<B㌱4 l?nMM$Y'Btoi\+~8]-Y[tͤp?UQm2LqE#.q<<4x1 (3Hr<9kt Ơrz]Y1,̊sզ)F.z—y| +d׳F~Baud֠lŇ-Ņ3XSC=qth:j&#61iu]p3Bt_ m}i2H۠bvvx$3@̷gnՠ PJP Hq!Y%j`"#I#P/=fgMC;,LMM4Yp'*SxEYvMY@S̎#6ݹpf#%;tyA&wbOLO#.&?8L89ݓM7%89-u#. /L]\տ%.cxb4&!`,flLM#6p:5HdAH0̑ 4EqTk&x3)e(1Y`ǎ6! ].$#.  T0%DH!Z9ǴQPP4 wi8,H%Fl!6ae0\*hiD58h陖*P K#6@ALCD6DS*wB]4,6"wA3b")٘iU HMlE 1 #66v4#60gSP#.FHWU%0ME& 83Ą3mtHrĢBBꙕ 3[^6_#.c%%yyCkU@(צȚnf C5#6,"B@MbP l:uy\eXky+'J#.?>M܇)xm&-BkM7If sQfE뻺f$$#6DXfY#%W%UEMѐ^hI#%JSx|k0Jh(Gfuazf?n'ҫ*3#R'W((q#Hei3%< 쪌Jrz#67oSkJF9T@Z0TV(NAqKV+ma4}o{;٬66[[:>DH{hˇ}*s9eDʳ:d{T3D#.CvN]GL@Sޡ#%4C^WfDΗZR7p#6VX"k˻[$2%5;o7vȮ^vҹR#6A0@45*F 8iQ3 iGZK(4]-UD7v)<MFgV#34u*bQ GF읯!<Z H|pDA#%ϡ3jd.1<#P$xamVĮL+ؚ$p((7'&9w#6ncl!#.:&LYU !G(|#%mr,A-aNKMN8R0;n[5')L^b>%C38]6uLD`X B!:Oi悘n%ڒ"a\qK董LQ#.i1ǿ0 0~tm6V,V"$skҌʸ 0')HH2DO"ZͭiHJXDVBEGEE0cUƄe5@mn\0P`M`8#%#%Iѕ*Bah<2ƦZ\Ȥ H:bX1 8 #6 #%H*ߖ[|4O4o}#9thg#.`޻Ÿ3*\Y9)h#65P4aePM2Xl+%ʫEEDLNAGUym^VvV`%QV@INWP3:$#.EG^2S4#%ܥM2,s4PN0:=G,0!&*Ƿ=aօз;sHcif t1aS& 7!,H#%5@% EsRr_kW:*'õnH[h4׷`wAuu#.,q89 J;3O8~k1 $9%y6ZhIEG!+]fvo՘8`n^tY[Ba֛iA~_X>H&삉Xb#.45%!rKl}9='p>(^7/$jYiVIJ#6ba&Ċ*kLm&ߺZkYks<X员aޢs: #.#.+;ÀMI@\z/aٰn#%N=ǾꉑKFzFC?6(ukàXE{J9&rP|i4-J[hyôme;;#.MIqAq)\eqACD 8: 7$l}ufێ6c6ZAI%!pj[-[ +cTdLͶf܀#%PYl!VYCrhM #.ѭN0LoţJ1ȲnFJֆ bk#.?{5b]%OZZl}0 E!΀,"g4 )@w}* uAԠ$ALpIFwIϞ/tAO!## j U6t<4O+ta L;X#6ɨF#%oٔ1d@!T {WnWm'y͌6@g8AZ0?y<vulfPx"%70 'ws7u"𒎚ݗf500.=Ԗl:;X澐QP@?LSa#%G1#6 AffXC&3#lDhK #.Wa]\]ųɔSd5Qy7s]&Ѷs\+yv[yo[s!S$S`6m#6*%|}<L=AIYa<>#%<>,U!f,ʹ5j"-62)E4jtj7~#. D1SQ*)Jzh'̋x"#%Z"gBR1ȸ0ć0HrHNT$>#.D1)-fQlc+E*(@'](]4{qM_Osfaב$W #%'}eL|*j #6{.Ƀxv,!3Iy8Uo{,EE'Ft &nyBQbpt"@T֡dHE9Z B!JSjh("jKNkTRiI[RT l@njma0 NR(+\/*SRe/n.gUP dt=v9.k Ni`Mljh([;R*5XVO8"Ņk4#.y@^K?W-%lc#nzK!XЇovM)%I51R`F&#6Z7m+WZ(F AUdQ$9,i!`P*6+0rBF"EH(Ʌ)hlQ,#%h}qz/uJQ!"kWmF-ڔ5F6XXQZ[3:zYݽ%$kg#6с5#1KL5VFLкƗI$ȫmiGl/$3" *hc<jֆӃ90(8A K"2ڷnI-6N^56&\wY2nڊڂKdYywcMkRTȔ󺷚kΨ$ISa!H6bcMi^חjMIUZuujlm(eRםܷktYc)&A aNΓ#%#6qVm 3{䚂5Hd>QEF*d#%@ S㒡[Fhȓ(1P{5\&4)Dڛ7եG46Y3O `>p?~fE#6ݧ#RffKXZ#.Y9~07"^GM#.UJmG/lr$aB1 SJ@#o($QG)ļ.#.Lo.E`%1GP5/mlpB#%|$xk(#6(i 8>Jiߎ>9t(a-Qޖ3oUbٛ`|B h)Iiף|#.AqK5"gtdBKVw|jPOt~J'`O8)A.F'RK`,n#f4lQ!n`C#wƽ!ȱ|* DaMF՚WuzC<kGG Ղ]j?70EyZ$YϨЁǽuMm/ЅZ8@?j8睯fYF4d-DEC=vN|m:Pi\X"#.%t#̍ѨU׎1Qߪ@[8A ʍ;çKB4Db:[nX#.dRS(@r:|Xrc'pdlA47m~f1ޓBG_hGC"Ws;/~D#.fY{ao}ik*>D-(?Ld* zQf@ݨQD߳2Tm`=W#%w>uL䑙 l#%#%b#%?s/_H]>_|O?~#/?|/=GG__PSLB?i,ϳDy;"e\?E7;sL@Zm4 #%)40)*ǹI IFVh #%QߐjB MρESY8,g +,C҂x#.Ǒ^A:0f@#̏%*Ul·i\şyvǧ‡eԫj݆떅 &P"ƾrUr#.7_ua:Xs+u#6ٙ.7*jHxiiQ]d*iea˜T0eTh޶եpvmTYn44Bl`sO6e75m8q훓Mmƶ9TFdMmTIqx,Y.5myx*ŧ7FskCF #y{4h2faqS1a HF#ydH?F:o4)Ц^EKN\-NS4 '>:`wJ4#.p@ĞGF;tk5Sޒ 넞g w#6Hp,#6^>`L5RE "IEqOv].fB< 3PEATV*AB9wtc߷Y2 k\69U>!<N(K61$@ 0P2Hqw;\xH1M}lı38bKuɀ\ ,F#6"`T70]#. wbs $LjJlkY3[FmDŅS_EWZ|:־:?E3t#%]Ti#I(co۪zSl#%M}OFA~o;|mq>^F4?cS9ɬצU1"ǻ'_6+W] 扄8uKxte}C3zg,r.;}0זfT9P^]*zg_Ƴ8ΎQHԼ=#6@C&9Ғ#.QO#.D'G\U򻵊PCGݼម-v챯[kش|nmcm(oSEMAjZU݁y]#6.Q#. |P?!xndN2Ap'PQsp]f!OVݤ7Ub*J9H{cԼ(5@$fR'w=<܎_.EqPX#"(];#D)Qb(mLtjfh ! H 3ئ"~({fYr0mCN.!xN ,A<?  &^|YY?B-6t&E hN'c`[l@%AOeC.G ʽ1'waB+!,y>^#y;9ѵgs(?l-A!ԥG#%#%(#6Cۢ OI[BY=+4ks]B_F*N#.6pJqTE3oYwfӧm3,M/-80aw<}8fJ$ o]ax`H$Y@zn= i~1^#.Jz(LPc薪"|xg?I~"]B@&
#<==
-#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAlwFQwwACgkQSbTGfAUn\neqqiUA/8DR2Vn15HoJreMJ5EyOk51K2In8HWeHS15CZqUnBWBnD1QKIS5k0SS4su\nFJK8qOvSK4WSvKxTbIVduZwSBmfSK85j7MK1SScDbqo85EVHwZZVhRAd6zPooNsV\nGKtWuE1pf38wI6QpLivn7qRpfCdCIluyJgMFGLb5Fcv8Fv+NdhUPB4LTqknw/oOd\nQliG6ASjmuoI/5FgHdm1OoPrbZldj25+EZRR16HXJ9Hy3uV7LKjSb792LpPedIs2\ncdAlix+e6PPDT/08jzcKlyWlQIqaGDe4+GIf7KClf0hc0MSm3Ri0Y/olB3fBE+V1\nxiGyqXPJXtECl4GMWAkz8i9Bxo5rx778GgEeAdTrayjhfDU56giwwmu1UM9c0gwF\nTQ4cvnr0p09fGv+DUqOYEvywP4et+Qcv8AgeTnwrICQyawnOS2i4xS/Uonk/R/r8\ng+ZyrNE0HtFbkG14RJWIkgbPrq4VaYsVesXO3aPYb6cb164jrtnNdTQxfpn+o9t3\nq0IKH8MSLGgebWACY88YtTQB+Se8oxvRU1aZGUhPUrX3WBZZu9UISHOVHbA0NbU3\n7to3lMp4M3bQC/PRZKlsqRtIdDGXxAXhuOgFvHNJI+GotSFbu+B7BXSyfPRsDk9p\nMq7S1Q0Lr6WD4GOqZbuinCe2+g9+yAYsHppDdCtFKi7ul93Wufo=\n=34KS\n-----END PGP SIGNATURE-----\n
+#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAl3aW3cACgkQSbTGfAUn\neqruqA//Y9oJ46ZR8W7YB/e45bfrYxGbN7NnkvkwSPNziObYur+n1QpQEOaPTn/U\n5kFtPWHXRJzaG/A9poKn7pl1Xd7Edcu1aalfoEazZbuD37VOxIp9lnrefCAeICqj\nGv0SD96Zac91CbA+b20Q4xnqxKMi3LSI4NPjfFGy62FkSk3MS4p6Rdp0/WAKwwNj\nw7WEjQCNmLb37z+FGSzXg28aljYeteBZEthsVmGJ5QqVwMBwgj2+y5FOTzFfxmqB\nrWgjFYS0l85kgYRZv9yzdNmFs5SScwafwpT8Xmdr49tFn/+0LxXyRxX+rdODgrpV\nY4EOiQz0fd6mMMnaTDXlLSXls3JyVYmbTjeNL/9gcHmnStzJ851CJQfyQg7A+JoC\nc7nz0HbiFyTgB+PUZr1OhGj3A7287o8XQ0tqR3oa7jXIOX0OynrGplMQKr++0jE1\nBgKzjLoE9CTbjkQfICLG+aUy3S1ZyDk/BcO+5+Ytbru+qXuDsIgAdVosMfNSv9jJ\nXvOINsbRMekdejYMZv8fIkn5OEjCFHVhNpobEsCb768bjB3p7alQGECBvjHCm6dy\nXZPzl9cBMWIXcBjPTS+GZj+PIXGcu76pbsx6HBHWf+uJ+4xgOsUCVu//0AV09jvA\n0MjtLWwQ8mdRH6Wt4hsp4HKtSvQrhmljf2OnuYBgaFmcdJkN1zI=\n=C0oT\n-----END PGP SIGNATURE-----\n
diff --git a/wscript b/wscript
index f491d70..293409a 100644
--- a/wscript
+++ b/wscript
@@ -37,7 +37,8 @@ subdirs = ['rtemstoolkit',
'linkers',
'misc',
'tester',
- 'tools/gdb/python']
+ 'tools/gdb/python',
+ 'trace']
def get_version(ctx):
#
@@ -128,13 +129,13 @@ def rebuild(ctx):
waflib.Options.commands.extend(['clean', 'build'])
def check_options(ctx, host):
- if host in ['mingw32', 'x86_64-w64-mingw32']:
+ if host in ['mingw32', 'x86_64-w64-mingw32', 'i686-w64-mingw32']:
ctx.env.HOST = host
ctx.env.CC = '%s-gcc' % (host)
ctx.env.CXX = '%s-g++' % (host)
ctx.env.AR = '%s-ar' % (host)
ctx.env.PYTHON = 'python'
- elif host is not 'native':
+ elif host != 'native':
ctx.fatal('unknown host: %s' % (host));
#