From d94cf5497644810d8edf8b24efa4a174a8038f1e Mon Sep 17 00:00:00 2001 From: Gabriele Tornetta Date: Mon, 17 Oct 2022 21:28:09 +0100 Subject: [PATCH 1/9] feat: add Python support (wip) --- CMakeLists.txt | 21 +++++ include/austin_symbol_lookup.hpp | 25 ++++++ include/libaustin/libaustin.h | 70 ++++++++++++++++ include/symbol_hdr.hpp | 2 + include/unwind_state.hpp | 7 +- lib/libaustin.so | Bin 0 -> 99656 bytes src/austin_symbol_lookup.cc | 35 ++++++++ src/unwind.cc | 4 + src/unwind_dwfl.cc | 139 ++++++++++++++++++++++++++++++- test/CMakeLists.txt | 4 +- 10 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 include/austin_symbol_lookup.hpp create mode 100755 include/libaustin/libaustin.h create mode 100755 lib/libaustin.so create mode 100644 src/austin_symbol_lookup.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 3df5c1179..38e9c6044 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,6 +264,17 @@ target_link_libraries( ) target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt) + +# add libaustin +add_library(austin SHARED IMPORTED) +set_target_properties(austin PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libaustin.so" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libaustin" +) + + + + set(LIBDD_PROFILING_EMBEDDED_OBJECT "${CMAKE_BINARY_DIR}/libdd_profiling-embedded.o") add_custom_command( OUTPUT ${LIBDD_PROFILING_EMBEDDED_OBJECT} @@ -283,6 +294,16 @@ add_exe( LIBRARIES ${DDPROF_LIBRARY_LIST} DEFINITIONS ${DDPROF_DEFINITION_LIST}) target_link_libraries(ddprof PRIVATE libddprofiling_embedded_object) + + + +# link libaustin to ddprof +target_link_libraries(ddprof PRIVATE austin) + + + + + if(USE_LOADER) target_compile_definitions(ddprof PRIVATE "DDPROF_USE_LOADER") target_link_libraries(ddprof PRIVATE libdd_loader_object) diff --git a/include/austin_symbol_lookup.hpp b/include/austin_symbol_lookup.hpp new file mode 100644 index 000000000..7b5751e98 --- /dev/null +++ b/include/austin_symbol_lookup.hpp @@ -0,0 +1,25 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. This product includes software +// developed at Datadog (https://www.datadoghq.com/). Copyright 2021-Present +// Datadog, Inc. + +#pragma once + +#include "symbol_table.hpp" + +#include "libaustin.h" + +#include + +namespace ddprof { + +class AustinSymbolLookup { +public: + SymbolIdx_t get_or_insert(austin_frame_t *frame, SymbolTable &symbol_table); + +private: + using FrameKeyMap = std::unordered_map; + FrameKeyMap _frame_key_map; +}; + +} // namespace ddprof diff --git a/include/libaustin/libaustin.h b/include/libaustin/libaustin.h new file mode 100755 index 000000000..1c5aaef8d --- /dev/null +++ b/include/libaustin/libaustin.h @@ -0,0 +1,70 @@ +// This file is part of "austin" which is released under GPL. +// +// See file LICENCE or go to http://www.gnu.org/licenses/ for full license +// details. +// +// Austin is a Python frame stack sampler for CPython. +// +// Copyright (c) 2018 Gabriele N. Tornetta . +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef LIBAUSTIN_H +#define LIBAUSTIN_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + +typedef void (*austin_callback_t)(pid_t, pid_t); +typedef void * austin_handle_t; +typedef struct { + uintptr_t key; // private + char * filename; + char * scope; + unsigned int line; +} austin_frame_t; + + +extern int +austin_up(); + +extern void +austin_down(); + +extern austin_handle_t +austin_attach(pid_t); + +extern void +austin_detach(austin_handle_t); + +extern int +austin_sample(austin_handle_t, austin_callback_t); + +extern int +austin_sample_thread(austin_handle_t, pid_t); + +extern austin_frame_t * +austin_pop_frame(); + +austin_frame_t * +austin_read_frame(austin_handle_t, void *); + +#ifdef __cplusplus +} +#endif +#endif // LIBAUSTIN_H diff --git a/include/symbol_hdr.hpp b/include/symbol_hdr.hpp index 40456fa46..a83bed58a 100644 --- a/include/symbol_hdr.hpp +++ b/include/symbol_hdr.hpp @@ -14,6 +14,7 @@ #include "logger.hpp" #include "mapinfo_lookup.hpp" #include "runtime_symbol_lookup.hpp" +#include "austin_symbol_lookup.hpp" #include @@ -42,6 +43,7 @@ struct SymbolHdr { ddprof::DsoSymbolLookup _dso_symbol_lookup; ddprof::DwflSymbolLookup _dwfl_symbol_lookup; ddprof::RuntimeSymbolLookup _runtime_symbol_lookup; + ddprof::AustinSymbolLookup _austin_symbol_lookup; // Symbol table (contains the references to strings) ddprof::SymbolTable _symbol_table; diff --git a/include/unwind_state.hpp b/include/unwind_state.hpp index 6e56aab9a..6c530b8e6 100644 --- a/include/unwind_state.hpp +++ b/include/unwind_state.hpp @@ -16,6 +16,8 @@ #include "symbol_hdr.hpp" #include "unwind_output.hpp" +#include "libaustin.h" + #include typedef struct Dwfl Dwfl; @@ -39,9 +41,10 @@ struct UnwindRegisters { struct UnwindState { explicit UnwindState(int dd_profiling_fd = -1) : _dwfl_wrapper(nullptr), dso_hdr("", dd_profiling_fd), pid(-1), - stack(nullptr), stack_sz(0), current_ip(0) { + stack(nullptr), stack_sz(0), current_ip(0), austin_handle(nullptr) { output.clear(); output.locs.reserve(DD_MAX_STACK_DEPTH); + } ddprof::DwflHdr dwfl_hdr; @@ -59,6 +62,8 @@ struct UnwindState { ProcessAddress_t current_ip; UnwindOutput output; + + austin_handle_t austin_handle; }; static inline bool unwind_registers_equal(const UnwindRegisters *lhs, diff --git a/lib/libaustin.so b/lib/libaustin.so new file mode 100755 index 0000000000000000000000000000000000000000..68a6babe01080c8688868ce4b6f646ec0a3a4711 GIT binary patch literal 99656 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 +#include +#include +#include +#include + +#include "austin_symbol_lookup.hpp" + + +namespace ddprof { + + +SymbolIdx_t AustinSymbolLookup::get_or_insert(austin_frame_t * frame, SymbolTable &symbol_table) { + auto iter = _frame_key_map.find(frame->key); + + if (iter != _frame_key_map.end()) { + return iter->second; + } + + SymbolIdx_t index = symbol_table.size(); + std::string symname = std::string(frame->scope); + + symbol_table.push_back(Symbol(symname, symname, frame->line, std::string(frame->filename))); + + _frame_key_map.emplace(frame->key, index); + + return index; +} + +} // namespace ddprof diff --git a/src/unwind.cc b/src/unwind.cc index 299cd9208..a36554df4 100644 --- a/src/unwind.cc +++ b/src/unwind.cc @@ -17,6 +17,8 @@ #include "unwind_metrics.hpp" #include "unwind_state.hpp" +#include "libaustin.h" + #include #include #include @@ -31,6 +33,7 @@ static void find_dso_add_error_frame(UnwindState *us) { us->dso_hdr.dso_find_closest(us->pid, us->current_ip); add_error_frame(find_res.second ? &(find_res.first->second) : nullptr, us, us->current_ip); + austin_up(); // Idempotent } static void add_container_id(UnwindState *us) { @@ -50,6 +53,7 @@ void unwind_init_sample(UnwindState *us, uint64_t *sample_regs, us->pid = sample_pid; us->stack_sz = sample_size_stack; us->stack = sample_data_stack; + us->austin_handle = austin_attach(sample_pid); } static bool is_ld(const std::string &path) { diff --git a/src/unwind_dwfl.cc b/src/unwind_dwfl.cc index 31acc5274..0ea5e61f6 100644 --- a/src/unwind_dwfl.cc +++ b/src/unwind_dwfl.cc @@ -5,16 +5,99 @@ #include "unwind_dwfl.hpp" +#include "austin_symbol_lookup.hpp" #include "ddprof_stats.hpp" #include "ddres.hpp" #include "dwfl_internals.hpp" #include "dwfl_thread_callbacks.hpp" + #include "logger.hpp" #include "runtime_symbol_lookup.hpp" #include "symbol_hdr.hpp" #include "unwind_helpers.hpp" #include "unwind_state.hpp" +#include "libaustin.h" +extern "C" { +#include "libebl.h" +} + +// clang-format off + +/// Hacky way of accessing registers +// --> imported definition from the libdwflP.h + +struct Dwfl_Process +{ + struct Dwfl *dwfl; + pid_t pid; + const Dwfl_Thread_Callbacks *callbacks; + void *callbacks_arg; + struct ebl *ebl; + bool ebl_close:1; +}; + +/* See its typedef in libdwfl.h. */ + +struct Dwfl_Thread +{ + Dwfl_Process *process; + pid_t tid; + /* Bottom (innermost) frame while we're initializing, NULL afterwards. */ + Dwfl_Frame *unwound; + void *callbacks_arg; +}; + + +struct Dwfl_Frame +{ + Dwfl_Thread *thread; + /* Previous (outer) frame. */ + Dwfl_Frame *unwound; + bool signal_frame : 1; + bool initial_frame : 1; + enum + { + /* This structure is still being initialized or there was an error + initializing it. */ + DWFL_FRAME_STATE_ERROR, + /* PC field is valid. */ + DWFL_FRAME_STATE_PC_SET, + /* PC field is undefined, this means the next (inner) frame was the + outermost frame. */ + DWFL_FRAME_STATE_PC_UNDEFINED + } pc_state; + /* Either initialized from appropriate REGS element or on some archs + initialized separately as the return address has no DWARF register. */ + Dwarf_Addr pc; + /* (1 << X) bitmask where 0 <= X < ebl_frame_nregs. */ + uint64_t regs_set[3]; + /* REGS array size is ebl_frame_nregs. + REGS_SET tells which of the REGS are valid. */ + Dwarf_Addr regs[]; +}; + +/* Fetch value from Dwfl_Frame->regs indexed by DWARF REGNO. + No error code is set if the function returns FALSE. */ +static bool +__libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val) +{ + Ebl *ebl = state->thread->process->ebl; + if (! ebl_dwarf_to_regno (ebl, ®no)) + return false; + if (regno >= ebl_frame_nregs (ebl)) + return false; + if ((state->regs_set[regno / sizeof (*state->regs_set) / 8] + & ((uint64_t) 1U << (regno % (sizeof (*state->regs_set) * 8)))) == 0) + return false; + if (val) + *val = state->regs[regno]; + return true; +} + +// clang-format on + + int frame_cb(Dwfl_Frame *, void *); namespace ddprof { @@ -91,15 +174,28 @@ static void trace_unwinding_end(UnwindState *us) { } } +void print_all_registers(Dwfl_Frame *dwfl_frame) { + for (int i = 0; i != PERF_REGS_COUNT; ++i) { + uint64_t val; + if (__libdwfl_frame_reg_get(dwfl_frame, i, &val)) { + LG_DBG("Register %i = %lx", i, val); + } + } +} + static DDRes add_dwfl_frame(UnwindState *us, const Dso &dso, ElfAddress_t pc, const DDProfMod &ddprof_mod, - FileInfoId_t file_info_id); + FileInfoId_t file_info_id, Dwfl_Frame *dwfl_frame); + // check for runtime symbols provided in /tmp files static DDRes add_runtime_symbol_frame(UnwindState *us, const Dso &dso, ElfAddress_t pc, std::string_view jitdump_path); +static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, + ElfAddress_t pc, Dwfl_Frame *dwfl_frame); + // returns an OK status if we should continue unwinding static DDRes add_symbol(Dwfl_Frame *dwfl_frame, UnwindState *us) { if (is_max_stack_depth_reached(*us)) { @@ -184,7 +280,7 @@ static DDRes add_symbol(Dwfl_Frame *dwfl_frame, UnwindState *us) { us->current_ip = pc; // Now we register - if (IsDDResNotOK(add_dwfl_frame(us, dso, pc, *ddprof_mod, file_info_id))) { + if (IsDDResNotOK(add_dwfl_frame(us, dso, pc, *ddprof_mod, file_info_id, dwfl_frame))) { return ddres_warn(DD_WHAT_UW_ERROR); } return ddres_init(); @@ -258,7 +354,7 @@ DDRes unwind_dwfl(UnwindState *us) { static DDRes add_dwfl_frame(UnwindState *us, const Dso &dso, ElfAddress_t pc, const DDProfMod &ddprof_mod, - FileInfoId_t file_info_id) { + FileInfoId_t file_info_id, Dwfl_Frame *dwfl_frame) { SymbolHdr &unwind_symbol_hdr = us->symbol_hdr; @@ -266,6 +362,11 @@ static DDRes add_dwfl_frame(UnwindState *us, const Dso &dso, ElfAddress_t pc, SymbolIdx_t symbol_idx = unwind_symbol_hdr._dwfl_symbol_lookup.get_or_insert( ddprof_mod, unwind_symbol_hdr._symbol_table, unwind_symbol_hdr._dso_symbol_lookup, file_info_id, pc, dso); + + if (IsDDResOK(add_python_frame(us, symbol_idx, pc, dwfl_frame))) { + return ddres_init(); + } + MapInfoIdx_t map_idx = us->symbol_hdr._mapinfo_lookup.get_or_insert( us->pid, us->symbol_hdr._mapinfo_table, dso, ddprof_mod._build_id); return add_frame(symbol_idx, map_idx, pc, us); @@ -298,4 +399,36 @@ static DDRes add_runtime_symbol_frame(UnwindState *us, const Dso &dso, return add_frame(symbol_idx, map_idx, pc, us); } +// check for Python frame evaluation symbols +static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, + ElfAddress_t pc, Dwfl_Frame *dwfl_frame) { + SymbolHdr &unwind_symbol_hdr = us->symbol_hdr; + SymbolTable &symbol_table = unwind_symbol_hdr._symbol_table; + + std::string symname = symbol_table.at(symbol_idx)._symname; + if (us->austin_handle && + (symname.find("PyEval_EvalFrameDefault") != std::string::npos || + symname.find("PyEval_EvalFrameEx") != std::string::npos)) { + AustinSymbolLookup &austin_symbol_lookup = + unwind_symbol_hdr._austin_symbol_lookup; + + // The register we are interested in is RSI, but it doesn't seem to be + // available. So we loop over the first 64 registers and stop if we find + // a register value that resolves correctly to a Python frame. + uint64_t val; + for (int i = 0; i < (int)sizeof(*dwfl_frame->regs_set) * 8; i++) { + if (__libdwfl_frame_reg_get(dwfl_frame, i, &val)) { + austin_frame_t *frame = + austin_read_frame(us->austin_handle, (void *)val); + if (frame) { + symbol_idx = austin_symbol_lookup.get_or_insert(frame, symbol_table); + return add_frame(symbol_idx, -1, pc, us); + } + } + } + } + + return ddres_error(1); +} + } // namespace ddprof diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fa0445b57..5f1fd35e2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,7 +45,7 @@ function(add_unit_test name) # # Create exe with sources. Always add logger and error management in the unit tests add_exe(${name} ../src/ddres_list.cc ../src/logger.cc ${MY_UNPARSED_ARGUMENTS}) - target_link_libraries(${name} PRIVATE gtest Threads::Threads gmock_main gmock) + target_link_libraries(${name} PRIVATE gtest Threads::Threads gmock_main gmock austin) target_include_directories(${name} PRIVATE ${DDPROF_INCLUDE_LIST} ${GTEST_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/include/lib) @@ -240,6 +240,7 @@ add_unit_test( ../src/mapinfo_lookup.cc ../src/procutils.cc ../src/runtime_symbol_lookup.cc + ../src/austin_symbol_lookup.cc ../src/symbol_map.cc ../src/signal_helper.cc ../src/statsd.cc @@ -285,6 +286,7 @@ add_unit_test( ../src/mapinfo_lookup.cc ../src/procutils.cc ../src/runtime_symbol_lookup.cc + ../src/austin_symbol_lookup.cc ../src/symbol_map.cc ../src/signal_helper.cc ../src/statsd.cc From f6369b72ec7b7933452e66d784d6c9efb5ea1e6d Mon Sep 17 00:00:00 2001 From: r1viollet Date: Mon, 22 May 2023 10:38:27 +0200 Subject: [PATCH 2/9] python prototyping Fix the build time dependency to read internal python frames from register state --- app/base-env/Dockerfile | 11 +++++++++++ cmake/Findelfutils.cmake | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/base-env/Dockerfile b/app/base-env/Dockerfile index bd7ea1979..50197b253 100644 --- a/app/base-env/Dockerfile +++ b/app/base-env/Dockerfile @@ -177,6 +177,17 @@ RUN VERSION="3.21.0" \ # Install cmake_format RUN pip3 install cmake_format +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libtool + +RUN git clone --branch r1viollet/libaustin https://github.com/r1viollet/austin.git && \ + cd austin && \ + autoreconf --install && \ + ./configure && \ + make && \ + make install + # A specific user is required to get access to perf event ressources. # This enables unit testing using perf-event ressources RUN useradd -ms /bin/bash ddbuild diff --git a/cmake/Findelfutils.cmake b/cmake/Findelfutils.cmake index e9d4ac48f..d99a91261 100644 --- a/cmake/Findelfutils.cmake +++ b/cmake/Findelfutils.cmake @@ -28,6 +28,9 @@ set(LIBELF_PATH ${ELFUTILS_PATH}/lib/libelf.a) set(ELFUTILS_INCLUDE_DIRS ${ELFUTILS_PATH}/include) +set(PRIVATE_EBL_DIRS ${ELFUTILS_PATH}/src/libebl) +set(LIBDW_INCLUDE_DIRS ${ELFUTILS_PATH}/include/elfutils) + if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") # Variable can contain several args as it is quoted @@ -59,5 +62,8 @@ set_target_properties( INTERFACE_INCLUDE_DIRECTORIES ${ELFUTILS_INCLUDE_DIRS} INTERFACE_LINK_LIBRARIES "${LIBLZMA_LIBRARIES};${ZLIB_LIBRARIES}") +add_library(private_ebl INTERFACE) +target_include_directories(private_ebl INTERFACE ${PRIVATE_EBL_DIRS} ${LIBDW_INCLUDE_DIRS}) + # Elf libraries -set(ELFUTILS_LIBRARIES dw elf) +set(ELFUTILS_LIBRARIES dw elf private_ebl) From 33951482176651570b242ab39160e426cc1a66de Mon Sep 17 00:00:00 2001 From: r1viollet Date: Fri, 9 Jun 2023 11:57:06 +0200 Subject: [PATCH 3/9] python prototyping Adjust the logics to cache python frames --- app/python-tests/Dockerfile | 2 ++ include/austin_symbol_lookup.hpp | 11 ++++++++--- include/unwind_state.hpp | 1 - src/austin_symbol_lookup.cc | 25 +++++++++++++------------ src/unwind_dwfl.cc | 12 +++++++----- 5 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 app/python-tests/Dockerfile diff --git a/app/python-tests/Dockerfile b/app/python-tests/Dockerfile new file mode 100644 index 000000000..01b555885 --- /dev/null +++ b/app/python-tests/Dockerfile @@ -0,0 +1,2 @@ +FROM python:slim + diff --git a/include/austin_symbol_lookup.hpp b/include/austin_symbol_lookup.hpp index 7b5751e98..20c15d421 100644 --- a/include/austin_symbol_lookup.hpp +++ b/include/austin_symbol_lookup.hpp @@ -16,10 +16,15 @@ namespace ddprof { class AustinSymbolLookup { public: SymbolIdx_t get_or_insert(austin_frame_t *frame, SymbolTable &symbol_table); - + void clear() { + for (auto el : _austin_symbols) { + _free_list.push_back(el); + } + _austin_symbols.clear(); + } private: - using FrameKeyMap = std::unordered_map; - FrameKeyMap _frame_key_map; + std::vector _austin_symbols; + std::vector _free_list; }; } // namespace ddprof diff --git a/include/unwind_state.hpp b/include/unwind_state.hpp index 6c530b8e6..e8b933aaf 100644 --- a/include/unwind_state.hpp +++ b/include/unwind_state.hpp @@ -44,7 +44,6 @@ struct UnwindState { stack(nullptr), stack_sz(0), current_ip(0), austin_handle(nullptr) { output.clear(); output.locs.reserve(DD_MAX_STACK_DEPTH); - } ddprof::DwflHdr dwfl_hdr; diff --git a/src/austin_symbol_lookup.cc b/src/austin_symbol_lookup.cc index 091725bed..94e958e83 100644 --- a/src/austin_symbol_lookup.cc +++ b/src/austin_symbol_lookup.cc @@ -16,20 +16,21 @@ namespace ddprof { SymbolIdx_t AustinSymbolLookup::get_or_insert(austin_frame_t * frame, SymbolTable &symbol_table) { - auto iter = _frame_key_map.find(frame->key); - - if (iter != _frame_key_map.end()) { - return iter->second; + SymbolIdx_t idx = -1; + // reuse elements to avoid growing the table + if (!_free_list.empty()) { + auto el = _free_list.back(); + _free_list.pop_back(); + _austin_symbols.push_back(el); + idx = el; + } + if (idx == -1) { + idx = symbol_table.size(); + symbol_table.push_back(Symbol()); } - - SymbolIdx_t index = symbol_table.size(); std::string symname = std::string(frame->scope); - - symbol_table.push_back(Symbol(symname, symname, frame->line, std::string(frame->filename))); - - _frame_key_map.emplace(frame->key, index); - - return index; + symbol_table[idx] = Symbol(symname, symname, frame->line, std::string(frame->filename)); + return idx; } } // namespace ddprof diff --git a/src/unwind_dwfl.cc b/src/unwind_dwfl.cc index 0ea5e61f6..958c04958 100644 --- a/src/unwind_dwfl.cc +++ b/src/unwind_dwfl.cc @@ -105,6 +105,9 @@ namespace ddprof { DDRes unwind_init_dwfl(UnwindState *us) { // Create or get the dwfl object associated to cache us->_dwfl_wrapper = &(us->dwfl_hdr.get_or_insert(us->pid)); + // clear at every iteration + us->symbol_hdr._austin_symbol_lookup.clear(); + if (!us->_dwfl_wrapper->_attached) { // we need to add at least one module to figure out the architecture (to // create the unwinding backend) @@ -404,7 +407,6 @@ static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, ElfAddress_t pc, Dwfl_Frame *dwfl_frame) { SymbolHdr &unwind_symbol_hdr = us->symbol_hdr; SymbolTable &symbol_table = unwind_symbol_hdr._symbol_table; - std::string symname = symbol_table.at(symbol_idx)._symname; if (us->austin_handle && (symname.find("PyEval_EvalFrameDefault") != std::string::npos || @@ -413,11 +415,11 @@ static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, unwind_symbol_hdr._austin_symbol_lookup; // The register we are interested in is RSI, but it doesn't seem to be - // available. So we loop over the first 64 registers and stop if we find + // available. So we loop over the available registers and stop if we find // a register value that resolves correctly to a Python frame. - uint64_t val; - for (int i = 0; i < (int)sizeof(*dwfl_frame->regs_set) * 8; i++) { - if (__libdwfl_frame_reg_get(dwfl_frame, i, &val)) { + uint64_t val = 0; + for (int i = 0; i < PERF_REGS_COUNT; i++) { + if (__libdwfl_frame_reg_get(dwfl_frame, i, &val) && val) { austin_frame_t *frame = austin_read_frame(us->austin_handle, (void *)val); if (frame) { From c6b3581229671468e484d68e3d360321ea2ef95c Mon Sep 17 00:00:00 2001 From: r1viollet Date: Fri, 9 Jun 2023 14:25:00 +0200 Subject: [PATCH 4/9] Ensure we attach only once per process --- include/ddprof_process.hpp | 9 ++++++++- include/symbol.hpp | 2 ++ include/unwind_state.hpp | 4 +--- src/ddprof_process.cc | 31 +++++++++++++++++++++++++++++++ src/dwfl_symbol_lookup.cc | 6 ++++++ src/unwind.cc | 1 - src/unwind_dwfl.cc | 12 ++++-------- test/CMakeLists.txt | 2 +- 8 files changed, 53 insertions(+), 14 deletions(-) diff --git a/include/ddprof_process.hpp b/include/ddprof_process.hpp index 191124bd9..987e1aa03 100644 --- a/include/ddprof_process.hpp +++ b/include/ddprof_process.hpp @@ -14,12 +14,14 @@ #include #include +typedef void * austin_handle_t; + namespace ddprof { class Process { public: explicit Process(pid_t pid) : _pid(pid), _cgroup_ns(kCGroupNsNull) {} - + ~Process(); using CGroupId_t = uint64_t; static constexpr CGroupId_t kCGroupNsNull = std::numeric_limits::max(); @@ -33,6 +35,8 @@ class Process { // lazy read of container id const ContainerId &get_container_id(std::string_view path_to_proc = ""); + austin_handle_t get_austin_handle(); + uint64_t _sample_counter = {}; private: @@ -44,6 +48,7 @@ class Process { pid_t _pid; CGroupId_t _cgroup_ns; ContainerId _container_id; + austin_handle_t _austin_handle = nullptr; }; class ProcessHdr { @@ -53,6 +58,8 @@ class ProcessHdr { const ContainerId &get_container_id(pid_t pid, bool force = false); void clear(pid_t pid) { _process_map.erase(pid); } + Process& get_process(pid_t pid); + private: constexpr static auto k_nb_samples_container_id_lookup = 100; using ProcessMap = std::unordered_map; diff --git a/include/symbol.hpp b/include/symbol.hpp index 0e54e7a3c..7a0fc80eb 100644 --- a/include/symbol.hpp +++ b/include/symbol.hpp @@ -32,5 +32,7 @@ class Symbol { // OUTPUT OF LINE INFO uint32_t _lineno; std::string _srcpath; + + bool _is_python_frame; }; } // namespace ddprof diff --git a/include/unwind_state.hpp b/include/unwind_state.hpp index e8b933aaf..820977884 100644 --- a/include/unwind_state.hpp +++ b/include/unwind_state.hpp @@ -41,7 +41,7 @@ struct UnwindRegisters { struct UnwindState { explicit UnwindState(int dd_profiling_fd = -1) : _dwfl_wrapper(nullptr), dso_hdr("", dd_profiling_fd), pid(-1), - stack(nullptr), stack_sz(0), current_ip(0), austin_handle(nullptr) { + stack(nullptr), stack_sz(0), current_ip(0) { output.clear(); output.locs.reserve(DD_MAX_STACK_DEPTH); } @@ -61,8 +61,6 @@ struct UnwindState { ProcessAddress_t current_ip; UnwindOutput output; - - austin_handle_t austin_handle; }; static inline bool unwind_registers_equal(const UnwindRegisters *lhs, diff --git a/src/ddprof_process.cc b/src/ddprof_process.cc index 0a9d01c39..1744369e1 100644 --- a/src/ddprof_process.cc +++ b/src/ddprof_process.cc @@ -11,6 +11,7 @@ #include // for std::from_chars #include #include +#include "libaustin.h" namespace ddprof { constexpr auto k_max_buf_cgroup_link = 1024; @@ -20,6 +21,19 @@ std::string Process::format_cgroup_file(pid_t pid, return string_format("%s/proc/%d/cgroup", path_to_proc.data(), pid); } +Process::~Process() { + if (_austin_handle) { + austin_detach(_austin_handle); + } +} + +austin_handle_t Process::get_austin_handle() { + if (!_austin_handle) { + _austin_handle = austin_attach(_pid); + } + return _austin_handle; +} + const ContainerId &Process::get_container_id(std::string_view path_to_proc) { if (!_container_id) { extract_container_id(format_cgroup_file(_pid, path_to_proc), _container_id); @@ -74,6 +88,23 @@ DDRes Process::read_cgroup_ns(pid_t pid, std::string_view path_to_proc, return {}; } +Process& ProcessHdr::get_process(pid_t pid){ + auto it = _process_map.find(pid); + if (it == _process_map.end()) { + // new process, parse cgroup + auto pair = _process_map.try_emplace(pid, pid); + if (pair.second) { + it = pair.first; + } + else { + // todo: probably not an exception + LG_WRN("[ProcessHdr] Unable to insert process element"); + throw std::runtime_error("Unable to insert process element"); + } + } + return it->second; +} + const ContainerId &ProcessHdr::get_container_id(pid_t pid, bool force) { // lookup cgroup static const ContainerId unknown_container_id = diff --git a/src/dwfl_symbol_lookup.cc b/src/dwfl_symbol_lookup.cc index 11c39a1c6..271dcf6ef 100644 --- a/src/dwfl_symbol_lookup.cc +++ b/src/dwfl_symbol_lookup.cc @@ -134,6 +134,12 @@ SymbolIdx_t DwflSymbolLookup::insert(const DDProfMod &ddprof_mod, table.push_back(std::move(symbol)); Symbol &sym_ref = table.back(); + // slightly hacky way to detect python frame we should replace + if (sym_ref._symname.find("PyEval_EvalFrameDefault") != std::string::npos || + sym_ref._symname.find("PyEval_EvalFrameEx") != std::string::npos) { + // flag this as something we can replace + sym_ref._is_python_frame = true; + } if (sym_ref._srcpath.empty()) { // override with info from dso (this slightly mixes mappings and sources) // But it helps a lot at Datadog (as mappings are ignored for now in UI) diff --git a/src/unwind.cc b/src/unwind.cc index a36554df4..5adcd2fae 100644 --- a/src/unwind.cc +++ b/src/unwind.cc @@ -53,7 +53,6 @@ void unwind_init_sample(UnwindState *us, uint64_t *sample_regs, us->pid = sample_pid; us->stack_sz = sample_size_stack; us->stack = sample_data_stack; - us->austin_handle = austin_attach(sample_pid); } static bool is_ld(const std::string &path) { diff --git a/src/unwind_dwfl.cc b/src/unwind_dwfl.cc index 958c04958..1e06dc968 100644 --- a/src/unwind_dwfl.cc +++ b/src/unwind_dwfl.cc @@ -407,13 +407,9 @@ static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, ElfAddress_t pc, Dwfl_Frame *dwfl_frame) { SymbolHdr &unwind_symbol_hdr = us->symbol_hdr; SymbolTable &symbol_table = unwind_symbol_hdr._symbol_table; - std::string symname = symbol_table.at(symbol_idx)._symname; - if (us->austin_handle && - (symname.find("PyEval_EvalFrameDefault") != std::string::npos || - symname.find("PyEval_EvalFrameEx") != std::string::npos)) { - AustinSymbolLookup &austin_symbol_lookup = - unwind_symbol_hdr._austin_symbol_lookup; - + austin_handle_t handle = us->process_hdr.get_process(us->pid).get_austin_handle(); + if (handle && (symbol_table.at(symbol_idx)._is_python_frame)) { + AustinSymbolLookup &austin_symbol_lookup = unwind_symbol_hdr._austin_symbol_lookup; // The register we are interested in is RSI, but it doesn't seem to be // available. So we loop over the available registers and stop if we find // a register value that resolves correctly to a Python frame. @@ -421,7 +417,7 @@ static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, for (int i = 0; i < PERF_REGS_COUNT; i++) { if (__libdwfl_frame_reg_get(dwfl_frame, i, &val) && val) { austin_frame_t *frame = - austin_read_frame(us->austin_handle, (void *)val); + austin_read_frame(handle, (void *)val); if (frame) { symbol_idx = austin_symbol_lookup.get_or_insert(frame, symbol_table); return add_frame(symbol_idx, -1, pc, us); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5f1fd35e2..43fc9d057 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -249,7 +249,7 @@ add_unit_test( ../src/unwind_helpers.cc ../src/unwind_metrics.cc ../src/user_override.cc - LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle + LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle austin DEFINITIONS MYNAME="savecontext-ut") add_unit_test( From ec8535dabd6ef6bd89c0cde4ba5858cac71e64f4 Mon Sep 17 00:00:00 2001 From: r1viollet Date: Fri, 9 Jun 2023 14:52:55 +0200 Subject: [PATCH 5/9] Reduce calls to libaustin I added a minor cache on the registers that yield valid results. --- CMakeLists.txt | 17 ++--------- include/austin_symbol_lookup.hpp | 1 + include/ddprof_process.hpp | 8 +++-- include/libaustin/libaustin.h | 38 +++++++++--------------- include/symbol_hdr.hpp | 2 +- src/austin_symbol_lookup.cc | 8 ++--- src/ddprof_process.cc | 7 ++--- src/unwind_dwfl.cc | 50 ++++++++++++++++++++++++-------- 8 files changed, 70 insertions(+), 61 deletions(-) mode change 100755 => 100644 include/libaustin/libaustin.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 38e9c6044..dd14bf8ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,16 +264,11 @@ target_link_libraries( ) target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt) - # add libaustin add_library(austin SHARED IMPORTED) -set_target_properties(austin PROPERTIES - IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libaustin.so" - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libaustin" -) - - - +set_target_properties( + austin PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libaustin.so" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libaustin") set(LIBDD_PROFILING_EMBEDDED_OBJECT "${CMAKE_BINARY_DIR}/libdd_profiling-embedded.o") add_custom_command( @@ -295,15 +290,9 @@ add_exe( DEFINITIONS ${DDPROF_DEFINITION_LIST}) target_link_libraries(ddprof PRIVATE libddprofiling_embedded_object) - - # link libaustin to ddprof target_link_libraries(ddprof PRIVATE austin) - - - - if(USE_LOADER) target_compile_definitions(ddprof PRIVATE "DDPROF_USE_LOADER") target_link_libraries(ddprof PRIVATE libdd_loader_object) diff --git a/include/austin_symbol_lookup.hpp b/include/austin_symbol_lookup.hpp index 20c15d421..a9b39f184 100644 --- a/include/austin_symbol_lookup.hpp +++ b/include/austin_symbol_lookup.hpp @@ -22,6 +22,7 @@ class AustinSymbolLookup { } _austin_symbols.clear(); } + private: std::vector _austin_symbols; std::vector _free_list; diff --git a/include/ddprof_process.hpp b/include/ddprof_process.hpp index 987e1aa03..db4a6a396 100644 --- a/include/ddprof_process.hpp +++ b/include/ddprof_process.hpp @@ -13,8 +13,9 @@ #include #include #include +#include -typedef void * austin_handle_t; +typedef void *austin_handle_t; namespace ddprof { @@ -39,6 +40,8 @@ class Process { uint64_t _sample_counter = {}; + std::vector _python_register_indices; + private: std::string format_cgroup_file(pid_t pid, std::string_view path_to_proc); @@ -48,6 +51,7 @@ class Process { pid_t _pid; CGroupId_t _cgroup_ns; ContainerId _container_id; + austin_handle_t _austin_handle = nullptr; }; @@ -58,7 +62,7 @@ class ProcessHdr { const ContainerId &get_container_id(pid_t pid, bool force = false); void clear(pid_t pid) { _process_map.erase(pid); } - Process& get_process(pid_t pid); + Process &get_process(pid_t pid); private: constexpr static auto k_nb_samples_container_id_lookup = 100; diff --git a/include/libaustin/libaustin.h b/include/libaustin/libaustin.h old mode 100755 new mode 100644 index 1c5aaef8d..4a1ba1cb9 --- a/include/libaustin/libaustin.h +++ b/include/libaustin/libaustin.h @@ -26,43 +26,33 @@ extern "C" { #endif -#include #include - +#include typedef void (*austin_callback_t)(pid_t, pid_t); -typedef void * austin_handle_t; +typedef void *austin_handle_t; typedef struct { - uintptr_t key; // private - char * filename; - char * scope; - unsigned int line; + uintptr_t key; // private + char *filename; + char *scope; + unsigned int line; } austin_frame_t; +extern int austin_up(); -extern int -austin_up(); - -extern void -austin_down(); +extern void austin_down(); -extern austin_handle_t -austin_attach(pid_t); +extern austin_handle_t austin_attach(pid_t); -extern void -austin_detach(austin_handle_t); +extern void austin_detach(austin_handle_t); -extern int -austin_sample(austin_handle_t, austin_callback_t); +extern int austin_sample(austin_handle_t, austin_callback_t); -extern int -austin_sample_thread(austin_handle_t, pid_t); +extern int austin_sample_thread(austin_handle_t, pid_t); -extern austin_frame_t * -austin_pop_frame(); +extern austin_frame_t *austin_pop_frame(); -austin_frame_t * -austin_read_frame(austin_handle_t, void *); +austin_frame_t *austin_read_frame(austin_handle_t, void *); #ifdef __cplusplus } diff --git a/include/symbol_hdr.hpp b/include/symbol_hdr.hpp index a83bed58a..0f20b00f4 100644 --- a/include/symbol_hdr.hpp +++ b/include/symbol_hdr.hpp @@ -5,6 +5,7 @@ #pragma once +#include "austin_symbol_lookup.hpp" #include "base_frame_symbol_lookup.hpp" #include "common_mapinfo_lookup.hpp" #include "common_symbol_lookup.hpp" @@ -14,7 +15,6 @@ #include "logger.hpp" #include "mapinfo_lookup.hpp" #include "runtime_symbol_lookup.hpp" -#include "austin_symbol_lookup.hpp" #include diff --git a/src/austin_symbol_lookup.cc b/src/austin_symbol_lookup.cc index 94e958e83..9f7df3f04 100644 --- a/src/austin_symbol_lookup.cc +++ b/src/austin_symbol_lookup.cc @@ -11,11 +11,10 @@ #include "austin_symbol_lookup.hpp" - namespace ddprof { - -SymbolIdx_t AustinSymbolLookup::get_or_insert(austin_frame_t * frame, SymbolTable &symbol_table) { +SymbolIdx_t AustinSymbolLookup::get_or_insert(austin_frame_t *frame, + SymbolTable &symbol_table) { SymbolIdx_t idx = -1; // reuse elements to avoid growing the table if (!_free_list.empty()) { @@ -29,7 +28,8 @@ SymbolIdx_t AustinSymbolLookup::get_or_insert(austin_frame_t * frame, SymbolTabl symbol_table.push_back(Symbol()); } std::string symname = std::string(frame->scope); - symbol_table[idx] = Symbol(symname, symname, frame->line, std::string(frame->filename)); + symbol_table[idx] = + Symbol(symname, symname, frame->line, std::string(frame->filename)); return idx; } diff --git a/src/ddprof_process.cc b/src/ddprof_process.cc index 1744369e1..dad321947 100644 --- a/src/ddprof_process.cc +++ b/src/ddprof_process.cc @@ -8,10 +8,10 @@ #include "ddres.hpp" #include "string_format.hpp" +#include "libaustin.h" #include // for std::from_chars #include #include -#include "libaustin.h" namespace ddprof { constexpr auto k_max_buf_cgroup_link = 1024; @@ -88,15 +88,14 @@ DDRes Process::read_cgroup_ns(pid_t pid, std::string_view path_to_proc, return {}; } -Process& ProcessHdr::get_process(pid_t pid){ +Process &ProcessHdr::get_process(pid_t pid) { auto it = _process_map.find(pid); if (it == _process_map.end()) { // new process, parse cgroup auto pair = _process_map.try_emplace(pid, pid); if (pair.second) { it = pair.first; - } - else { + } else { // todo: probably not an exception LG_WRN("[ProcessHdr] Unable to insert process element"); throw std::runtime_error("Unable to insert process element"); diff --git a/src/unwind_dwfl.cc b/src/unwind_dwfl.cc index 1e06dc968..bb5f28182 100644 --- a/src/unwind_dwfl.cc +++ b/src/unwind_dwfl.cc @@ -97,7 +97,6 @@ __libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val) // clang-format on - int frame_cb(Dwfl_Frame *, void *); namespace ddprof { @@ -190,7 +189,6 @@ static DDRes add_dwfl_frame(UnwindState *us, const Dso &dso, ElfAddress_t pc, const DDProfMod &ddprof_mod, FileInfoId_t file_info_id, Dwfl_Frame *dwfl_frame); - // check for runtime symbols provided in /tmp files static DDRes add_runtime_symbol_frame(UnwindState *us, const Dso &dso, ElfAddress_t pc, @@ -283,7 +281,8 @@ static DDRes add_symbol(Dwfl_Frame *dwfl_frame, UnwindState *us) { us->current_ip = pc; // Now we register - if (IsDDResNotOK(add_dwfl_frame(us, dso, pc, *ddprof_mod, file_info_id, dwfl_frame))) { + if (IsDDResNotOK( + add_dwfl_frame(us, dso, pc, *ddprof_mod, file_info_id, dwfl_frame))) { return ddres_warn(DD_WHAT_UW_ERROR); } return ddres_init(); @@ -407,22 +406,49 @@ static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, ElfAddress_t pc, Dwfl_Frame *dwfl_frame) { SymbolHdr &unwind_symbol_hdr = us->symbol_hdr; SymbolTable &symbol_table = unwind_symbol_hdr._symbol_table; - austin_handle_t handle = us->process_hdr.get_process(us->pid).get_austin_handle(); + Process &p = us->process_hdr.get_process(us->pid); + austin_handle_t handle = p.get_austin_handle(); + if (handle && (symbol_table.at(symbol_idx)._is_python_frame)) { - AustinSymbolLookup &austin_symbol_lookup = unwind_symbol_hdr._austin_symbol_lookup; + AustinSymbolLookup &austin_symbol_lookup = + unwind_symbol_hdr._austin_symbol_lookup; // The register we are interested in is RSI, but it doesn't seem to be // available. So we loop over the available registers and stop if we find // a register value that resolves correctly to a Python frame. + std::vector ®_indices = p._python_register_indices; + SymbolIdx_t python_sym_idx = -1; + static unsigned python_lookup_cpt = 0; uint64_t val = 0; - for (int i = 0; i < PERF_REGS_COUNT; i++) { - if (__libdwfl_frame_reg_get(dwfl_frame, i, &val) && val) { - austin_frame_t *frame = - austin_read_frame(handle, (void *)val); - if (frame) { - symbol_idx = austin_symbol_lookup.get_or_insert(frame, symbol_table); - return add_frame(symbol_idx, -1, pc, us); + + // Hacky way of caching the regs that are actually useful + // They are stable + if (reg_indices.empty() || !(++python_lookup_cpt < 10) || + !(python_lookup_cpt % 100)) { + for (int i = 0; i < PERF_REGS_COUNT; i++) { + if (__libdwfl_frame_reg_get(dwfl_frame, i, &val) && val) { + austin_frame_t *frame = austin_read_frame(handle, (void *)val); + if (frame) { + reg_indices.push_back(i); + python_sym_idx = + austin_symbol_lookup.get_or_insert(frame, symbol_table); + break; + } } } + } else { + for (int i : reg_indices) { + if (__libdwfl_frame_reg_get(dwfl_frame, i, &val) && val) { + austin_frame_t *frame = austin_read_frame(handle, (void *)val); + if (frame) { + python_sym_idx = + austin_symbol_lookup.get_or_insert(frame, symbol_table); + break; + } + } + } + } + if (python_sym_idx != -1) { + return add_frame(python_sym_idx, -1, pc, us); } } From c6818c42ab1475af22fb9dbe6d5fd84391d2c87d Mon Sep 17 00:00:00 2001 From: r1viollet Date: Fri, 9 Jun 2023 16:56:51 +0200 Subject: [PATCH 6/9] Link statically to libaustin --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd14bf8ec..57af123b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,7 +267,7 @@ target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt) # add libaustin add_library(austin SHARED IMPORTED) set_target_properties( - austin PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libaustin.so" + austin PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libaustin.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libaustin") set(LIBDD_PROFILING_EMBEDDED_OBJECT "${CMAKE_BINARY_DIR}/libdd_profiling-embedded.o") From 383e0768b017ca35d21a77145f6cac103d48211c Mon Sep 17 00:00:00 2001 From: r1viollet Date: Wed, 14 Jun 2023 11:00:37 +0200 Subject: [PATCH 7/9] Python frames - Avoid ending profiling when non utf8 strings are found - Other fixups to investigate libaustin issue --- CMakeLists.txt | 2 +- app/base-env/Dockerfile | 11 ++++++++--- include/ddprof_process.hpp | 2 +- include/symbol.hpp | 2 +- include/unwind.hpp | 2 +- include/unwind_state.hpp | 1 + lib/libaustin.so | Bin 99656 -> 0 bytes lib_debug/libaustin.a | Bin 0 -> 106454 bytes src/ddprof_process.cc | 4 +++- src/ddprof_worker.cc | 2 +- src/pprof/ddprof_pprof.cc | 5 +++-- src/unwind.cc | 3 ++- src/unwind_dwfl.cc | 10 +++++++--- test/allocation_tracker-ut.cc | 2 +- test/savecontext-ut.cc | 4 ++-- 15 files changed, 32 insertions(+), 18 deletions(-) delete mode 100755 lib/libaustin.so create mode 100644 lib_debug/libaustin.a diff --git a/CMakeLists.txt b/CMakeLists.txt index 57af123b1..81643969b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,7 +267,7 @@ target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt) # add libaustin add_library(austin SHARED IMPORTED) set_target_properties( - austin PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libaustin.a" + austin PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib_debug/libaustin.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libaustin") set(LIBDD_PROFILING_EMBEDDED_OBJECT "${CMAKE_BINARY_DIR}/libdd_profiling-embedded.o") diff --git a/app/base-env/Dockerfile b/app/base-env/Dockerfile index 50197b253..9715735b4 100644 --- a/app/base-env/Dockerfile +++ b/app/base-env/Dockerfile @@ -179,12 +179,17 @@ RUN pip3 install cmake_format RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y \ - libtool + libtool \ + libiberty-dev -RUN git clone --branch r1viollet/libaustin https://github.com/r1viollet/austin.git && \ + +# possibly useful command +# CFLAGS="-g -O0 -DTRACE" ./configure +# Valid commit : git checkout a7a292b3f5a1058051b017c0d339a678efdec704 +RUN git clone --branch r1viollet/libaustin_v2 https://github.com/r1viollet/austin.git && \ cd austin && \ autoreconf --install && \ - ./configure && \ + CFLAGS="-g -O0 -DTRACE" ./configure && \ make && \ make install diff --git a/include/ddprof_process.hpp b/include/ddprof_process.hpp index db4a6a396..455b88eea 100644 --- a/include/ddprof_process.hpp +++ b/include/ddprof_process.hpp @@ -36,7 +36,7 @@ class Process { // lazy read of container id const ContainerId &get_container_id(std::string_view path_to_proc = ""); - austin_handle_t get_austin_handle(); + austin_handle_t get_austin_handle(pid_t tid); uint64_t _sample_counter = {}; diff --git a/include/symbol.hpp b/include/symbol.hpp index 7a0fc80eb..113a2df95 100644 --- a/include/symbol.hpp +++ b/include/symbol.hpp @@ -21,7 +21,7 @@ class Symbol { Symbol(std::string symname, std::string demangle_name, uint32_t lineno, std::string srcpath) : _symname(std::move(symname)), _demangle_name(std::move(demangle_name)), - _lineno(lineno), _srcpath(std::move(srcpath)) {} + _lineno(lineno), _srcpath(std::move(srcpath)), _is_python_frame(false) {} // OUTPUT OF ADDRINFO std::string _symname; diff --git a/include/unwind.hpp b/include/unwind.hpp index 552aaf709..a88281ce2 100644 --- a/include/unwind.hpp +++ b/include/unwind.hpp @@ -16,7 +16,7 @@ void unwind_init(void); // Fill sample info to prepare for unwinding void unwind_init_sample(UnwindState *us, uint64_t *sample_regs, - pid_t sample_pid, uint64_t sample_size_stack, + pid_t sample_pid, pid_t sample_tid, uint64_t sample_size_stack, char *sample_data_stack); // Main unwind API diff --git a/include/unwind_state.hpp b/include/unwind_state.hpp index 820977884..219d56cba 100644 --- a/include/unwind_state.hpp +++ b/include/unwind_state.hpp @@ -54,6 +54,7 @@ struct UnwindState { ddprof::ProcessHdr process_hdr; pid_t pid; + pid_t tid; char *stack; size_t stack_sz; diff --git a/lib/libaustin.so b/lib/libaustin.so deleted file mode 100755 index 68a6babe01080c8688868ce4b6f646ec0a3a4711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99656 zcmeFadwdi{`Y+r=5(xr!5YVU~Q3nODiNcZykr^_P4o)y~QRHHXAt8}lOlCBSU~nct z+m1vIS$y6+$FIp^~^zu67l z&r?-TJ@>kFb$e&F|H=+7mty_mlq-~0NKrhZK%3Kw^(g5|7nGzZeOTRitMPHJHA6Vk zx++N=CTa!E+}^RR#I>x-{)*t*K?cz+f@ZLgCD@OIW!Zg&ETnuhTDP5!pmO{em9 zm3{fuYS#fEwM)t5^;2@&Uh6riSl2|+oOqpFUOU<%#;f8{ezw0j|iE5Lc}CHS_kR)5!^9qIGG{RwwbMv4B~ zlP^j1XI+I^n7l-Td~Nv1u?ox)C0;DG(tQsBQ~4wNr=?%HefyuE-Jh@~<+hD$!YleR zb_-A^2Y*ECMEp@Zj}o2|!R54J9ZyiMTW`(RE_Lh+aaaH$=%9QCZ{I#Rl2+SP3}!@lA?_25Fbf;Zt23Yic4A8 zAwE7jOgRg;+ZLX!%}1 z_`eZt8sk`e{T!;9bDJq zZ=HbYvR>RL&xoYo{rc%E2h5r4*|c_1++PY1D+umo)yA$KqR4Sdyf6N#d%a}T6PHC6)9*0hA@(yl(<%$ol z_+ZDh^Pju>rK^i3o$-6`t6#x&s4>3##J?HQzN?n@FF1MEuJY*S^Fo^g6Azw$eZRoY z;M}OW`Kn0=R-H7b?_JunGals$9p%_dp^#Uj4>)=rpxczo8v` z61q=04nGuU-HwC5p&dHwFd!X|&mY>sQ|unE{>*mh``e*Y*G~Np+M)k?JN0{GKs+8l zQ`+hGiFWABYp37ZcIafcgYVl8KPR=*@BQug!?bqjA8x1qDHvFerw6m0e(&w1V8}W4 zx4RuZ{G%N@E{vMT>-UFt`1zzAI!W!&d8?iJ2)pC@|I6*vr@VeVJ{Lg`$BUQQ?eH_E z9lN~Nj$Q6+hYn3tj>nJQ4*sQf`i-_z|D<;O@R@e%_iLwqsGa&v?dWr4JM|~F!~fcL z^fs&=I+1qlMRSGY>GPs?=nQBFe_=a*HNG7?zSIt#D?#UM{5gM}+R^iu?a--er{C|P zx8wP3upK(vF_S!5IZ265;2hm77}FS?)0LEQ6j7Mu@{95Wu2jDY6JPpGX8$V6L0r2j zrz@$~@rp1j``yIkI!Vc+1dd<5U~a#_FMWdJRen*1Fu6`rQbhf81^!yb|4B+JONgy}@kD-|F7)8Jp2J!EqP#5dlgD$! zWdi>%=AY05O|J3Vz%R;tQ9ml^TrBXNEjrh61dY@5+bHO)q=`Cy7M-P{ev0UKC@)r4 zL9yMGGnDl5wtB7;_{k2uU+A-D9w#tZ(Ag&XO)20At6wr7**E2Dj;DDz{W=Q$H{8hK z-u$AB7y4{i#1S2Y;ja_)nX$vu;dA&!sTO)j68@7d@E<~6;`dlgE z`e%+-9v1r9P?pknFDP;fN=oFZzuU_-G@C z&*T@Sz>$#C5(gpq(%Z?LyePK@J4D?I(S}E+_ zSx%{gw&(Ny7IdQdZRykqJwzRP*eU8) z-O#qa8z9+T>%{S57*ReK^*xi?*8fo8m5FWfw*VwPM8D>87V(R6ozPoU_>VP?kR6zw z1)tV9s0ck&2|ZZjsY}pF6?UX~4E=5r{VI;QyBQ91iqf4|!oQaPe1&`Bvz|7X@H>-> zp?o6rrigh0%*TEUEWHT_vHam8EANRovgT&Jh257fe)FN&kw_ z{~&?iEBs0k@}4E|w+lVgh&*P&SG2!qU&(LJv)U=k>Ou9{t`G{WdxJec7Tf#*2Bpx^ljdt4hc_Q{dkd z_@(1H;vWL9f)4plx`X~?7@Fm=lTiV`rR-nuBkUL*&+(QWU8qm;E){WID(bJa>@M<; zRlk?8V~x<8<)2RpdFvhgEU^6HX-;^Vpg$ROC?9$p`S5fhZ<)}8MSrf~Gf9jWeFdFU zEW8*uZxi_530y9vkRvV;2KxehGXEFxV#VXPC?|gUIrzCy)K`AwbS@C|-xTti4tbUQ z{HZf$&CIU|6qE<@^OgLZF}e9uipqpEDbnFdi`UvM~(E3$+|lnI9}u%5KXKl#~}0Ovx{pK7CdpWU&gT%(``^R9swMWET|# zAVCQgf`h{RnMJqS_xX-8m=-2%7V%TvtX!Wz zXJoc=bFe5_lwTSsn!(gq$gCh^Z3Xh0q-%bcUs{o09GpH~0*hvp1#Yw8DMhTF1s2V0 z16pP&EGd8^5LJFh4rbk!7tNS;OOXX3XzY@bf(i?55dx|tKS1Hfs7YY{)S`eQ!xavA zO40N-fUsm2QA#DkVbFT!EG2*Xtf~38mcvfv3T~&C&IG0WTPkj=paO(4!xok2m(B!N z=&NYvEehmB>5PJkX$l21?x|LOG5Z&+5=r)NDf@Q@Cgp13{OJ@d(8Rg=#V4zSb zE}kB&D4~i|(M{p>SrxDh{cDk(fyhE0DgX(kn6U~%G97SLRxq`wf)XDXFPt^ASSc%= z0&P~JW@*K&!i`5k@nDGX*rA5kV{1y_AG5$N%Xvv#{(ocAbL6luxFZISYzMQljLRpC6cB!OTcG zP@&9&M3{fejC=~;TPQh*phte6J{_rG7K(V?)}on3m4VhgLhLGKqS;NeN?F2VIgO&L zJUDYE8G0JXDZz^AMMab^10@)RZn;#M!9&$TT$X>CU3gjk0O>w0O$JgWAVa!Om+l8i z_kY7ZPqTdJ!@y8J&E~lfvLj^|RTdTAQdXoONi!SSO&|qlu$2l>Qw#G8XHB18RER-f zYHLYrHntR5G@~@Ysuq-06iwj+S8IK8U~A~HdqjlH!0-yDB!dKJ-iquY+832fDbHv2 zVw#?g@jIW=4wtg9q;xulxK>N>a&XPFo7E9Vx3VnJVjkYMUa)Kmu{5J-1|=WT(Uj71 zg&c>j@V&sS;B>kcmX!0bB79pK+*yP%c=uL{X;YDsDJau$YGs?knE{2uxI(eU7s9}| zXCisf9V|j44t0&G0b+we6hv^tBI23fqlDs>Q-Se#E2#|0oay8^Wd*lPJ#HCVmxBd% zk?Bndmh;Htw~PKZ zfRL9e!~HqKvhpvz__Ef!0j;;K*tCH*7U5#GYOR_!$VRr_U3PJ6g-b6UNE65o?0<3W zUz`NM>Z}7E7;tO{oKxw5b08MPuY{BTm&l+__}7JV3~~vl)9_cUGRD92D@WtUO+EVIzWg3Y=Dok~pQafP<$K+(9{2 zY*bk1pRDz0{nf;!qBM(hQ5L>N;1zN1$-@793SSQri&_lIWbm*l`-D)1=|d~*k0Kh=RBB&^MEwQ_UWw=YG&%6Ebrbp-%deKbrV2VK4*VQ}_c-vWf_|O@ zf0w8~*@6F5^jqe@mk2sl4*YyUr^bP|=qz>Mmk2s59r$|$ou~uC;J_~t z^_6Se>g`E^PjcYj5%?4beuKcLI`An8oUX@#|47uI?7(|O{W1r>UevE~;LAk)r4Ia7 zQGcZazfIty4*U{9r{018R@85D;G0B!=X1ZZ6S%z2eWvd%ejNKB>PHz|ZevbXrkie%p@Jj_g-GTRX=lyyd_{jpVIq+))oyiXTLQ%iOfnOo; zWe)sSL8sDzuNU>J9QbB|U+BQ6^x*v0IPiG_zr=yB68NPKe3Rg3g#*7r)L-eqpCtA_ z8yxt0QQvgnn*_eef$t^gq={c#Tb5>dZtB<}~47j|}(IXU^cz|&kqJnG{`SM&Pxm|w;x3Ov!V_V%ekl7ufN zg!m;(cuRLwlp^8jOq=!VC*j|=khrEwclqW_Jre#!LWrLx;UBS(xaLXt zHzfQx3IB|QpDf`=Nca*7FSgWKX_bOL&ij-zMQ}C47T~ z&yw(_gnv%LH%a(;624i&50~(hMgFHKcu2yRNcd|cd~zO-7m5jf#|u*={M}Ohei9y! z66{~9gvX;0`=XqgfEitc@jQP!jF^icywa_CQEpVdFxjq;jtjG ze`OM0Jo02^l@eY&%4PT}32)d`6lI}=7fT&hS|j1tN%faV__Y##se~7g+*s)f3Ex?& zzf!_?k?^Y}{9O`0D&cb^{3Z#H1*QF~m+&XqSVh?;;jL$0RNNrpDMwpBQ^Jd*Sgfc? z!duT|si0ZHw>}j{!8NUZ`zWKp|4Eeaw@COT3ExA)CrkLA5zOqbmq_?tQvEUs|B8gKl<>VJ ze3gW^&a_bRLJ2P({jydy5GPnGZ`5hJ;U% z@Rv*YeiHsp37;zAgAzVn!Vi}49toc*;WY_=g@n(O@Ixj1I0+w>@RKFHSHhP_cyYvp zm6l2P>!kXX68>EYUnSvlCHz7OKTN{cNO+%wUn1eNCHztezevKbknmSZ_>~g=RSCaZ z!q1oRQ3R*6f!HfHObgKiu_tYq7LEsE z_t`Kl7!Sne*)T1R55#V^;Y0=(*f1?555%sqVOsbdhe z--c5Xf)CwD5rZHr$)R-E5c^6p-JB z`!IO_QA_?52Jf)pvlzVDhG_u+`E8gc{*d2>X<`rgZI~wXkl%)BA`kg(_&f&RY{UH+ zTwudAk%#;?OdDdbzYWtw9P-;RO|&7u4buc0_P1f05JP?&rin1@|A!?%O>|*@8>WdV z{NILY;tBa}m?n^r--c;|2>ET8CVG(HhG~Ka`E8gcaIn7()5Hz-w_%!4L4F&i2@~YE zVVd|rej6Ub;InL)CP0wihH2sh`E8gcJdpp0CBKKkJ8YOHIFR3lY2pF-Z8(d;uh=k6 zKp?*j)5HVv+b~TmAioU{XYkE7OcMskZ^N{K0{Lxt1cSXcObax~Z^M2DpJl@|fr9)t zJd(jK8>WdnJx!?d7={5Cw6!S~tlwG5tT!!#j; z{cU(0g9~inK~{F52H!-fkO zyxE3tWN@tw7c%%28=k`8CvCWh!S~s4F@xvX@KgrhY{N7F!TvT}%HV5k_$CH>ZFm}k z(`1R612Rh)39B^L;+}i>7biiF5 zaDoFq+Oe&?zd7K&4tTc%{?-A1=72wTz#AO!yAJpb2mGP~UhaS&bHEQe;D`gRcEEES zaL@tIaKJ?lc#;Di>wt3|aJB=^bie~0@I?-|uLJJwfO|UNt`0cC0UyP20omVwbHIBY z@NNhEtpond0e|d(H#p#T9q=0t_(cc2+yOu4fFE?g5eHoDfaf^ipaY)afQuaPBnLd! z0p~j4YzLg_fCoC@iyUxY2i)5M_jJHr9dLpJK8j-`ZS3!W_d4L+4)|LK{Fwv(*a2^F z!0$TXHyrSb4tTi(e#`+s=zt>*xY_~FalkvUIMV?Sbifxm z;JyyHw*&6!fV(>21P6Q+$Fkbk-vRG+z`Gsrw+{F-2mG-E-eAFX@qPQz@)bX|a~q(T z@uNjD@550DEs}6IT{4;jJ+V~&qy;HnvuH)*v$ z57la$<22VsZS$Xj9w0y;_#y&{Ee*UMt38ePRhfO!Rta7(RtsfbPLG8(eRrUXW@KIr zw#_dOwX~R1usGeAa1^IcT$9k&?w{JnZbhNshhXoRv6}wbXe{$njNHUu)VT>afd46) z(W5V|q%?huJ2`EOdP>4}phLgA)bJ-j8)K98W=;R4dQ0$f%@~`c8RdyLbFS80v22IcQfYvFX zr5UN_MyNEc#oUe+53s4g)_K^2&$MVPAIb#s42GOc3IK8hN1nji_k4mbdR__&Xp;f8 z_Z}q@>7sToj@-tO$pYDtBiA!zsz4sVnj6huV#p~1`7MxZXir0n1l&oQzTKRUsj1)S zy8zWlcS$+A&t0NrYz=g(x?EAi3$OtLjV$E?LnEsoUry~5cS#ShcrW!qlsHY08^p<_ zGje_+7w^_GJ__WjtGZPku294Dmbkj83H*e&1TL&PJR^8%)ored;DuEv37)H!MosMT zP&0<^$?jy{37cFg1>u^k-7n-eEQ@hkgDOt{jA4c zqQ}|C(07AAG~AbXFqlew?nKhbX-p?G0$ec{F_oy{!MHN{zTsV)K6IP|<`Qk);2z!6 zCPN!Z)c)~qW}jkpR=oRIkse`oA2{I!ArhZ1Gj;QjA3g_H zZtF9npmELUIU3b9eb_kWH{>!2aG5|q#{6)>`~r{%jUv#fIviBP=fO&`O#D+^gQQit z6=gXnBm3r7f3GgO1+E;s4*wL_F!*k&AF%4H`aB37n}&aiYv3P>TD=)%vSPF~heq#O z{>b|&3fEaiy*htA5m|wMc$2lHxYDR+n9BSt_B3E~=C7bxe6bbmVt;aUvAPew);0Zk zMe*u~HT^(#+6UQbQJ?-fav zJ*e%Fa<5(h7^F-V~3=2`aO~vZ;TB4FWmL_*&@BBn6oD%pTq`VDt#%aW7YI4(yAxHajtJiDq6sZQ6*E=>x|I9lEMG*Qo(RZ@x z1wokh3drx*e>OW%>eRQx{N&XLkE}#^c9e#iM3#iSoY&q|;@4CBdSPNHj`&H2W6;p; zowUA&Whu2=R$}8W+g_yap`>q`lHkchexg?Wb>MV9-_o~hMle}3CM23EFfvMljU)lM znlLHWb)T@i6%WiFZR|~(<4l(>co6K3mJWL}iSx zxuu0sAYq8OH9wI(jN7NNduvJ~I)Rfp)!>1&s3o^%3{S#6^T+?DKI{5ee;;ao#G(Qo z|LR`K4>C8F|9Z!)C8dtmk1)4q%BA?w3XiNMXVdg_c!_x*P~;s}ZXlOr{!I!wZ}ls; z4SGCni>AK3Q9Cq<*w?pc#*lg{JTdl$70-}Ria0wy)v$q%7!B{WM1%Qo+7>pog$o-h zMbM%BMAxhTdt_ul1G#06aRt%8T+67vrMF-INrPWsPF}S{b7dh!$w$36kRHt}=mCz< zh-OJ6j3Et;_1quP@{6>CW*FMZ(NAb}&i7fqLKjoP3EcV4L}=qk;L_tr`-jZMk;rSR^5260LS$O8@PlPhI8#Xn4XT z=0M0F*0@6YW$|jMnlT!AV+1B-wSfyXBV9Af5azccj~oodY5Emf#;~M7 zFHP5wGD(9liVKq-wDBa

OgpQZ>CCD!!7Z%dOJ*A@ZY?921dok>h?edqWS%k^PaM z(cw>&CvW-*Oi{XKN$Tebn!b+tRqSSHx`yU6SUREDC`&Tu!s*9EOUYJbt;;_2`cun8w#{`m`_B||z zz$hk)1SZwRyUB?#U5uysqM1ms4ZG8lmXiu5YV+PFp4XdiLqU>EOn?8!<-y|ZKgg3# zVb`8KjpqM_@?ihqKgd%8hig}!GyZRshYm!v?{7;eo3v-2tA0GjKBFme=IyB`qW?Pr z$mo6VUg~OxA49%~CYDS@;AqBR7FJ9`D~ecTnVGvuw9DS3-4C+{qM)v+QI2UVZzseT z$unWSIsOL-Y>$^BpDo|Rb55|MnAgR&eosl1&7Zv2ddITi+vq(I^?b%v7*MYAWLJNn zhW)gaqJP3BKBmeTxXQDJ?7<{Jwp#NjSbKvF0u>%?BK?H^skv2N{g|SxNnrf9R@#6{ z#%$J2b&Fbk9eP6@>B=4PgLCgTYX z87~7@c3s~<-4@$38Bi0`HT z3K%Jewp(ajJKQRfSW= zOk;K)j1bNPwN^cx!#oeYcaik)E(Qwo14fzY;ZHb`Z0oDR0~5qtW>>OinX%2NT+>F( z@kH)+R?{J7(ZeX2Y?a7j_NO*iS|#LHmXUg4tH`_%B|g|Vn~Y2!ueBwD)BLy^HAV=Y z+j!NP)L#>GZzfn@R>|o<+gmHdqXPT~J(2w6MrUJsfKIDl!NyD*DFfJsG#6W;BfHgP zD!JV%k)_O{lF3$y+-B21P;wt>;F~sppD6zFqo2HiWF@*|)A^6(p0Z1ocu=;++ zZAGtaQeF}2lJfch*@nqM)Zukjhq5Bhp^{9iL}u~?Dmlk0p|Ra^kzcXFXLe#tPJ@de z`f8ySJ|9K`U|IJ3o!EEtvF$yd}@pB)_ufKyWn+73Md4cVMS!Az&7S`UdkG z_(|>26GbFyg&%WP)R#9Q5*M{Ld>svALm4wwOix!0AkT$`t6zamjM2Tws8`^Yr>QXN zGj3#+B$0UMw-U{_D#F)mI>3Vs{*4ho>5V-iDod#up1?W}mwWKd+5_7`~kFM~}hB z?to~m;~}MmC=|@Kcm>)Vqs0%gZEAkMi%OVHPdi?vm#ETvt(AV=j^0ngcBoj#aQfc? zdkp=XsA;&h>04GMSxKipi%R*el@=5ImzXw~e`t;oLnWe@u=;RO*+fVFiISdHiEN_p zsN{&{)^eMAD%pV&3|AB-k3fg9FQHqpUxz0H5B#nuV@K0^zKhuILX3MbOB=WxjWI8H zl4yKP9;k1i715Wp;EVK}fOe?V8~oDqiEpTUpT6F#_!irFM_hi@*EbziCQRasU^aij zdhil!JxGhg1T%r!(IYhm9r~J4nH2jOH+=mLW|*G5*X8KV-fMI8Pp~0=9#~?F+cor1 z5ua_;e`Vo_or`&U#sMX*eZp=H2wMJ3cYDC1xsDWt-R9-sAK+H<3i75q;Y}o>zRMrU ztVcd*JQMS(v}ms0ue*HQgf^Iiog#|;+ae-W{o7WCyjkcS;zhhK__1<^T8zX#} ziN?EcT|l`p!6OcR*4Co;25&z0v#^QT_yKR+_<_9)JIM5alS$xXh#TwlHtJ{wf3Vm`ObkpHw!oF>NzP=w4tZ_>3>|6) z`fEZPVYbA?V+XHprnS69UMHSAZe&wk^L*@5FztO|9-hkh*G&JHF~L9@b`~QPtrNYBhbs*sH!d#(g>d4Y<>< ziVPW|zPyevkGE<|{4PAt#d;N`KKB(U0m_(|o=gMK3m}q%2M;OP>YIM|-_oLf*UrYI z;+jy@<#&CUu~7}vm#{;ZyMlW)V;CMnZpL1o&V=!3hi=CDyGiR`PfKx)%QOMi{e$mt zC#fMCmo>d!yMV3E?{H(c@TflBotLYB=hu(=jUjG&)RGL*QO)P>*XV1_SMa!(DHQ}@ zS?ooeM)UrDIdR!ZIdQ{5XLxdsk)48Tist$@$Mvn2F&#b5zZVJLYs^vnu0wv;kKWLn z_|CyMKx!gLY5E=R6w}ST40j;}t^wOLA*OT(#4|uMPodthn;7iYdTWBVUG~>%K)w^nO!{ZwX<}C=5 z7Ue!;hM7D8H`}Fp763vt=_>>mT#-E2jwH-6km(Rj#>w0qgm=eoZ$Z99$*;&52%MdSyB;_wyPr?ro2?)9>UBQ7 zIVaL9J~SH^>VtJ)+#PN_J~#-gne)JYbyQQcCt?Rp$3NMweZGvNxBiPgx->vVqd5UR zGWQwfNo*dq8-Ot)nG|S@?*Yqw>~sBswt22iI$XNJoAI3*ic%#{5;U6v%|PTSw26d= zeVP&P26q#&O_)xIbkJd5OGXlUHzpGIP3YdExwaYUad_kdrc<@J>25Td?$-Pf_h2GCBiZ4-YWQDhhS=Uf zI?#9Ed28thS$O2Bg*Ljhv|4r53E?Q7hv=JlHc2zJjF_hS)?r|I6{E(8jQGO=e@>{* zl@n@-5B}3)Nn^E z7rl_plKlpPAKm0J!)1WJ-?%9`7b9%=LIB_-RegC_Kg}4Qf?VZyK~R_uX3NR=Rt@qJI#E)kZ|(&&Q5&93BiEW4FuI@V#Ifb{k;X?Z{`eWs9tm ztMB&fzZ}Hfjc zpnBK&(5jd*V;qB>(!L~h?_+k#hPNTJz2Pt1+SX2tBJ5Pp?6eHe8+OEIfRf+x0QTC4 z*%MN;d8hX}HbYpG0{Jk5xcn)IX>FI_iOak_iJfSj)v@hRi46;yIlSPmr)HMH=%ybK zNWuHe3QA6qfk&}(K;SYs7Mo5yh}4aTh6(3T^207W21z_VETM@`SM#K;s3o3ovQ2e{ zZQjDLEKR;O7xd}*zaK+0JJs-xnjTj+t0XTQ)O2LDYV!LZkyqnla;8_$^Cm|uL3!QYIlT&IQ~rD~CZDNo`8 zu`<6DVugW+Q+9z-@bm&?Q0dR57}(C>H3H|7hav+Z>kzNB<--Ql(MU~Msj(@r5#{Hq zcNn`9)bJ>>T0}@ek8I(4h;DNPSf(((6y?-Y(i(gw63qN;dPq-Di-$P7j3h&EWoZWi zQkdX*rnwiAc=afom6U?zG-QGg-og!^zO41&4po!Qcv!-R=6KfKOph|qA8KL7lEmux zP^nijt9d#|f{kA0JTxhWGbdxJLSNm&syz{BLFllEHV=G4Kt;WGHi6(TM9dOn8_i8* zb*3FW9M-cia&BaL()7{kOh;KBa{!)Jlhi|~IX#?V6XWmd0SSNM?>kY$R4pT`ngc^@NY4;nw3(aoUFga~hdUhr7u4j7J(G#HEB z&9EQ_UF%5OTc|*TE}}&maf@qwI8#wVqH<--p&S>Qjl%)e<#gMsmqG%emub`~*UL(3 z*Qyr`lE><$*Kb@eZk9ap7>em-xhr#qBm9n9FQ31OuN^`Af{zf zVesS}V|b-D_=R}tKn<_Kzh1tpg1H_EDb4k6BH8A>PfEh1Me;mrd^XScq^0HUzMw&- zbp?{JL}UN$nbl-0)whkT`02m7m7hQaaMAVT#`~Ck%&$NG>-LL8`+T(5lVc;HJF9;j zzd%NU>wZNUNNBNDL6l%32sv6iqe7y&2ae*^HzL%lJA#Zi^j<&cBu-uQE>i}5l}tkz z_Jd-6gb!(v-k1Lb=3B>O1U!prZYRmJXy&UwYFnPX!E*wr30qj+KsfVriXWP)Pu4OC zp;j+I_gW#=1{fan&5g;Hd>T#sGdD5jXlT*~_gIf}A&6R!X562GPV`~@V#B~{PTb4b zo<)DplOx40VG*F1v;Wo7@@`xE&~i}`_A$vmmu%(sX@Md`rDo?%-fTKVW>?&CIbO=(Q=oK2gZUWqyXb(3T8UbK?(gFbZ!i7 zn*=D#tAM94ClSatmAF=sw?|%gmr)t*JuG9lWJ0P=)fS3sW_Rd{kL+G5%A~ex^>n&7 zs>xoO@p?QFH>%0QG<|>@sZYK4Bh*8TsNq+sGwf@OKf*F>bsB5h!>#ELkR2O`@%VWA z!@rJ?S(~~2h9Z`9xQ7j+D%`E<;8Ld@pGHhlrqcwie^(6Wvw=O83q^;i`O!x0~T zByVQS{Jlp%-WAEkLpO|S+Z8EFJoP0SHe}Gv3T|s*&I)mZIR^k+X58*Pg zF2GsqY$H^I{+T_S5MNM*Ll46DE%sUse5~zayFi%7edt|^=0tUo7ev{*s2Ubgv^Z`D zJ;ag^%@)jVuqfZ}yml>x0Dnj-4uP3Z!>?%ri!4WPE8tayOBz8*9~X~fiY07(tg9kw z^i!)1{OgZgbPpD|t53oG+xZMJ@Ced#PGm+3Mt~e6SJ7&BcAU497RBMO1Ey zMKOdA^jn*XJHFk$2pXfZ6w(LEk|$sj*E(bd=fCpTwjI}e&qP)zF4)g-+a z!&UGk^FnkPN{_=N6{k`8K4|OU2!F8lL$MW#-a~szq>Sxzk4uYE5Wfc_(yVV0)ND+P zuLF6?_m~e0M=LDn}UDLO9_p9 zO8OzEV)<3f=^eFDT|7A3P7!Q`nC>G3zr7!%B9-xGA~k25_-eSzi9{b8`F?CCMEH0g z8F&@wnfr+ueIJby;m@esrHtn&3k5w?Pn_}9!g(~y49!ka)bJk4=b_ojxZOdwMphZE zPSgeacmX{xAs)$ladf-QE`F#Q+K!2Al7U;#sQnW zocCktjs)@W6I7UorAlK$>uMcj(h(kz8xsUVYXq5_;Yxf2&5lPQORO{L4wD-!S~p|M z7e_JGRX=OBW|CGLOU5oricei-dTaM2ciQB`wp+3fxL)C?8vdpux#%BQ8iv51HVv(5o}|g%#8ul9V$>-q=#i;5mccCGBj=ljH7+s^ z_Lbn~e%ffkwmdB|lFfbJbKTIl!-?7q?NY4~RI(E%lUvRU>Yu0aI#eWfk)pH;XGKD{aZ4n&H=(z~$c%YWHbHF7A$*Y0T zTqaxbG^Vk<$yO)(%)Mk3ILJ9#q~rv@{yVK_4`7l4YUVZ239Zhty)oULc$I zK$*dz3VOav(~w~(gZuZw+&|eseCnHvao8V!A9kuOR&T6T!^0pr$Ob*3pW;G?5tDRe zhMBPnX{cEJoR(!l^o_{87z)Mvg~0HLL}?X;Df?Cl&6=x!A!>)A&&QZPyDF4q3*+z8 zT$}K=pc#4ZuU9+XjofP85Cd54L^3x)U2vLt6q$9Q)i5Ldpc;B~X@1E3tmg!@5`$97 zPWZ5W{@#3!Dhy5-1?;U}9*)lt3gI{sVo|C%ml(VIH3XlZky}pQzy(Z&OCV%V z!-Y(hhe`;TO21KvIRUt0InNlKjEN!TZfW+5Ee_h|!UH9bn8r?_)3HRy{)kE|Z?Acgots61r%GkqS&%&D;7*h@g@Sel$ zeoA1MmQhi1E1tnsl-=?Lsc$IDMZu4tuBHZbg`Z|*Q=TSe#HxrmP2q@M1>?Y?XJp~w zPBgGX%h*`{jTV;$pWaxpm1s8ZWhSU={3l;uO?VedVp|vaaIoM|QedC=xdQjWn|K0_ zoq|!A=1p|^x*0<cyftmn;FvgWXt68i$Ri1G$e^dKvu)lTdk z#<&=SpMi~H)%eHqFSNNn!Yn?D6mKLv4+^m>&>st-Oq7Wbx|Ek2DCZ#*TTNbPBc|4y2-*t~#Qw?e(u-w12y0C1=QpOM=D7CyGxm}@ zAn$EAziecW-UIKzPrG3Ig!$G#h->CsdvE=s5m`CmeqwF|-a5nme7e7$f({JrujS^h zg(uZ1=a5ZzqZ+f%V_3Eb``izcqp5+W!am7@z)C@&iV-*)1hhIf>x(5|SK9pGZTQ4u zSU2`4{*h+;TT_0r5!&`X}p(~q}TmC}~<_Y>KpdU&?5Y0dNoi9$C@gW+a)>HJA=z<)@g}lCC2I%Wp>ANai04fVvD?q)ha|dl)zr z(~T}L4~GZcjl0IzEPau@U?lV&Pqz=Yd7TJ)qH;RzQUARIQMXe`B!WxU%lTTb*4QJQ?yjrj_3Oq+z4VUrMuARgg`8i;}~ zK83b+s4sik861vn9pZ|=pJIkq|MkAnp!c{TI-(~%84aL`XUyy0VnG*1Pvokwzc}N< z*L3stBx4QJM(9K!Xt-SFrvH4exdhgi_CmA^*f51H5!%(xiz6As?rk7W+uu3#(1GQ1 z+FGgy*51xu4PsP9GoB~K(WVK}r#*M7$BvK0yGpaORlq&TK(rmq zx}`x6hg=n-J58PPXw>u2NT>Pr78>iYhhTmLr=+}o4%`GDHU&c*9%dVP1F)%7Jrdl| zFqo9Kw-K+W`&bn(s<3jrwI{ZtFcTU_%vgOz^QYkW*61Cw5D1EY5me%||}{aXoR{9SJEQ(e%y{a#Dm&3Pm?cOLUFpt#lVekBq zPs6zw>*#@);l*Nq2cF6t5Izn!OEJHD)9U#g@+9N=C1}C)>8GO|2>mWVpdf&C2U_&= z(_y1reP52gp|L;ZPnw&jtKsF;jyoHk_9toywEvWoQKyFJySS1Bts9YWw--1!qzkl1 z3pg`>X^A3|xF?*MF1QTdZ)UzOW=Cq6{)_Ky3QqgoG_3rNCr8Ct{d@q6@>vpRuDTF; z#IiR%(!?1v4Q)s|T$EUrRBlBKg(extFeowP)M0vyfWZ0e2o1#Wv~I zf;N(+xhvmzwUdP~JQFORe2-0B!_3%S=R*YJngPIJQ}-O|$w(*%&W`|2M@+|5;Q0E9 zPQuXnlN7-l4!F=EXXxP{-1&z=YowN)cvMSjdG!r8`+fu$2*>uzV&|bn>pXF_sJReT z>J)IK#CpT=S${-ni6}2&6aMrMGCGQKMNw~7 z#9V1Eip&{co60W|L=cuKIm*8JIGui+oK5sz>ylHTyywc{~h1qkEj3|EC z5pZO0`#pqn=AvYtRYH-&A7Do^M^KxotYo$)Ti=nbn`~;DfflR|hN*EV7Oye=i9Tt* z%*Rul2PVEq1f#-!F@?^t*spN;p|E4vSxlWCAZO+$-p^x|W_wtNdJH^Za=aXs(D~PP zwbp)P2jKgQ(2?-g?*U zX^=dI(Z)MKILGF|oXaY;DoSXvXJ^jqC88$l3NKgFY&6cJJ|2cLgi!~4Myxn5V*Bdvs(re=Yx6cN<55r}T5#($Vq{=Fh9UzC=6tj}q z9!vNAQQflsc$D#Xf2#S{gT#v~vFV6M_Poj%iYF!~U{&NpD;1iLJ!IR5=EJl-bE|xw zzN`7h3t+M?n{pwdfuF1IYCgfSNdhbOb##ucBWUuC z88j`3FvFqq#yUOp{>r=tQRCG&(BykQD)V@t{T36>Z4Aa(m)`Oj!6mQ;D18sIZjuF4 zZn(Ja0B~Rnt3iP!}(?FCD|T1`X5*~6@Dk|q4CoR|G(Me4sgcp!PXgKF;q*+=ZhgO zooo@m*MI@A*J;}X-UgfC)eG}on3vfkpvH6%sWBU23@oF|QAf*|=uQvrH8u2U&G(MA z$?eD3gyym4uIK*RF0{NZ#(+5$>b33im~EFzQLLeu+vQ^13AH2>1=a^tm<)kNtREkgQaT?veY%*BZ=n1RmsGODJnWBfM`;&eJ2~@-`7Y&Fd`~ItrgA&! zsMR-M{DG4b8Ov4pBR?PTg`?fr(ocpFBbm!@gO(!$-wyy^yR(D2Xa#0lSt(2lk<580 zHQ#}BSnNT1HBA5MDC};T>7-af&@lvcmDW9S&8Jm|6K~yx<#~7Ukv2Sf!2|n0NzJ6D z-`5b;96icLJ)EXTxHEf_{=ve+ ziC}@93SQfv@RPtUS?bcN$_^=DCk|d5sq6sz;@lnWs6+<7hksxjI+ztIY_Qv4YWYUm`C`}D$;SORWZ z%eht_x|L}qAAH5=l@xOWl)&Vqc*gk-{>iKEINyTr87zbw(Fd>S(^LEKfvHw%-2Z9o z_78jul;-O!-UE{{ScS>3l+WO!I5S8+^M{&x6#siZsQgP9kH$ z5UZbt&tk=#=nY-yc9E8!%MmujtA-D=QXJc+X;Xq}0Hj#OK#uuj><)ku!~DF^Sav3c zeNsXGW>gvavyKeW1O$E)L38#EDKGeSdkNc34U_){bT7xgEbxiEKF%%IS*6FHj@K7E0Gq>(Z8n<#ph^ngaWT0fH;n! zM1u&yo8Tk>(+)x%Ga-vtf1fsuvAc<$8-&#W}lA=-x;Otf1Oi z8oI?BfDh7APzzO-VAZ+-x1qy0DNMV${9Gxv2dhx;7j;%H1$qi{OMH^rtuviN)3eg? zYGD~eX&KpxYPcJE(62%)W#Og6Y7&_jA{W5`cy9{jImTjk`pX#Aj}9>s&-8TmqTu_q z4ULoG`{{gY70~jt18nROicQRMwT>N=VhBgtV8@R0$<|Erqf{i09@7^PXtJI{xI#}q z@N(}4rVSr_dvL;A>wyQ86`hF9YWhkjtsXM3uYmXYNttz7{UD7vN^ul-tr^J64-B5D z-g`b1uX+iJ+5Y75WuXm8lErU~iAO@>2X0f)#u9|?{kGK+v8GPJMzPC9yPd=cHKzpO zFx={XeY6Gv@`OEGt(J`^SCZZ0j_oDd>cjqRRx+$VcRX6x5Ig+UO<}qpfT_#! z;9pV=Y`6(bNIUoR_6~xDyj_|hP<#;ix~ybsNzPTh6Prnv&hC65wiO`D`{rwp|Mh%% zDz=4-F%ui~B+ZwZkrtQ3NY6q;tI@X-n~Xfb=@o+BUu{^OD(L;Vlxe4WR3bXc zCfb=1<>QIj8Fanx(GC-Ts?N^wvJYOd6X!U`+f0+jcb0Kh?*VCH7oVOUn~cGZ@z-jk zv2tj2KgL8Xv{s>+2(8#!+_(DQp>6m|UZJlV&!K6XZVtGUhMVFVJ{QCb#cZ^hw`VmN z*Nls1fISWCgf>YDsezd{yM)E-OzcQ$#>36jVzbExCVUWv{9C)=;U-*iupY?RR&k;~ zva$*w-v0Av416F;tun*7sCP9ZJ8Q;W4Jgev#!`ff%~AvbaI_9*&PFj0*g9MMI$MAH z^e^b}F6$6^jw+gSo}eLeBaeiOK|KdAtX5}$(;Tb}z-=;lB7L~#x_I1N@G$MAg3p|c zZz?+Hif%ZgU*X{(82tzr^{!JnqY7#ZMl}jlba{LpnHa*^drWJhVp@*D|f8h&%^J+mE zXH5R3&M&138LyJXEAUlScHh9jdH4F8fUQe-1BLX%?W^Ke?RFR07#GSX z6vVO1*^`)K!bKa*QllEW3e3TL6g%uKWQ+;v(Jeuh>GW11{mfe&qO-UHHZeAs~_j`^?`i+K@piX+#f8k@vIIVn|D28Jp`|TKT)$} z(aijZ{XB(E+_LeB%9|hKWH+($z9h-VRyj5_{r%TzF8mhf%M2vq$OpY9IQ@kWKzjM&5LemUN4UyHGFF)9ZHH!w%R*6f87o3YP-4xZl_rXkTU&jLBC`ZS zTlwCxUSks@TC&mFG?}IA^Knw42R%ISKoVMyk$kisBM$jGtj(;6UV()#8pH7o9eQR; z)x-e!g4+Z@a|5O${RKB=cXKry)E@YzQJ~}V4b@NKhJD>X8`*%?WnNnNjw-mrG6)9?X)k#EoeQM6U!%7lJFF5yP2phTG<;UZgL~{XH;42(#xoSqdeB9x8 zG1WRq+fS%WYa^0}t|Wskr;yDY4{J-yCA_YUvAJHYo<%%>_OOldNnmCO{<9n5xscOq zJj&D^I)b%t1Qbc{YW35YBzQybxasu?yu;}h)=7f4xuV%Nq`WA%wCpz)Gn-lVAq!aA zwzwtknO`yapIzLBWqO%}+o1q=k{z%N=4!|00B+zMmL0Hb00h|q-@agWz=xOA{&xsF z{Lq!zq54l;sntqU2Ak}dj5hBGx5;v7N{h@EHu=To2|G{t2FaEHw&ifwR({Jk6)a{ip-V&ODcwL9$H)+u($FdDn^&Q=$bbqCBT)tncf|UEa!W_dyJ>y- z#r@U@L6I3QWr5pN^RLT37wy1k?#N_rrl~+>z~4nDgU+3bc0dbfg#%vVnw^aKZX5FuBo;a|{gPR3Vin4P+gWHuHuvt5Yp zVrO@ToA#+fLzcl>PujN|BUWDfPccHwQH+=i@3Va8Os)lFsNuM4KQc%^HQ)xh>n|eX zUpzxq_kal0Y9(4xgkYFb>}+KqY+5z^C9zZewMbY92{n8@TK|XW`0oYw@das`5{db;C6T@l-bf_G3v~27TZn;IPS@el zI7G7?k8Dnk*~%c#D`t2Rc(7i}IFHnw(WqAY5y4C~8{?C)O*xky*r_v3JapW^{yQ~n zALA`dwn$AWUgHIp>G8Cl##Hp17&?lipNR0+*He{bTKr>MjpuJvNL8y3!_(}6;JCVS zjIY~9IFd#Jc2KaCxwgV};nCO%da55Apbj=m_(zjv=goZp^zk6syg6X$sB6;RJ zOuxC2d{-{!nfG>q21Z1N98Cf_BBBvFj~}0dN;fm0Dtx!joh~wfh-qjK>D9wJC!M8I zZ<@uNrFN0VL(vo>mTKj5Gaft=(NxY}3XGnUv0kknftz6)eZ9d3J!d^c$~su*QmbDA zhom-|$w0?$=ok<9@UOoR^DBJAZ*HEtDyVof4&E_5C$tYsgKSr}-joyi@lPzp^VRV;LCj;QHK~(R2s?<5EeHw-uz^@D^3u?%JwlYzltE_8BL_=dj)11G0y|@%oP+cg`9x5-pHLbJy|bHR=a75u{SWgRIT$63SP3KVW9pVdm=pk zpeXn0J=|tTbmCpp7d`NmSd!1^fv+FLSv!FBREq|bRY*caX+9`X9iNU>a^}JtSnAv# zLF)ViM2LU$HT|#Gu~P-PK_G{9Y$Wp`PU%^qghLz(oqzl<==2eE_8EfCNKPl8=-iFD zdux6%&j3A^mBgU=I^2;BnkSPj;dOYV6v_M;ub7dbkAWhqEf!cG%6eTPDpjIVor0+s z_+{h!dFbBU3865aUV!egK6Qi+&BN=_zz!W`tv}6KAS8{Et7$^PVrB>K{Khg7GZWoB zXud#9A!v3dfe;Hna`8m76FMf>PPF2M;w0S)41AkY9LJ}aVg36>l7e3%O4?3*%&*}a zj2aIV`i?jYl+SU4KX zx&Yf35G?AVeV~kk>H?M4%7EE})(*5JUo|EHd0=xCuORN|7}}}gq4!Cys>3G*?yNej z20pAh+~t-xst$JzzQxE;Z7<$F4mAu7?eqlBP5Z#!kD&5U?Fs6tI}#NAaA@bytvkJ) z>O((zg1deCy3L36&4<23JC~Zh*|jw`6Om}e=@vBY5SiHhU~C+l5{1Du*pB46)K$I& z7hbBR{#JMb=b=0Me4Q3rC;IED=*_6GwZYpdD*B6VZq{q9{%T$8wa75nL0?aoC(zwm z&k?@|eF=&u_#1VFTa@-}OCL>mxoC~`2rbQ7yi4t4OM%@OPhpLi?99tUM>_-?+dAX# zT7=K_Y-i+ooHjbiN83*C#M)1+0cdP6S1&v^-}kV6cM#p-$xaihTH{W zzrVp_Skm_}C&tb^AD%tTe}A|A6{Dr?va(0N&ZmB%C{94V&SvM-gw3?h1ZSGQj=S#f z7{PM&UveXJTXHf!$5$+{fq+mya6QXjmr<|mPz$o|Lt$3*<9Jq-%oxH<{Z2xo>tr*C zI|TR0UoE{`l66w=ZKRdGWlz-Z;cD?09-^A=|N~B z?f&2()#MP;?GcEGxHew{@K@iNntC^9bPyP2`F|v*{w#WDU&t=5A$dHaS`w3o@@(vl z1416mxKiyE-)pqtVXydHpu*>$G$V_hgeEwDA+Or$(*vzCZw7g({vTvp)_+vzY}QnZ z3Su{j{^NJ%M|b_ToOcQWSA&3f&iC)@5IFClNBVjdeGOt4>6iJ|X`mSUH$XOyP6H%{ zD*C{@H7biQb@dgLe}g|V4gPU0bJ-$Lj@5vMdBK0d-}Z%Ez?T^nHiY#W2mbmHfAn&_ zi8*+z5&zVfU(fq%c^3(K`HWs{17&pUeWFLuUdZzrrc$l)m}Kg)qGexdN$+}J2zg>L zMxY9YSIh~m?U(TOYIOiN)cRD>`bYSlu+ef@k@QHj@>KIoG^VdVqzVomvM7`Y3ZE_D znph4BtfCvsQPQs7YhPYK!6!!D>ul$AyDj53Y19ZnPZrZY6)z%`lG7+n(uhROM|8K3m_RN_>#(H}n{r=y_`7&AOtiATyd#}Cr+H3E#&)z~B z2B|nnv(PT6IyOnrY7XK|eHuZuWVw{W+a!D*!bRB7aAV|RGR&H&LWn+Y)5)*9qi`Su~b;P3o{4-SHFGi5eld90K?3bIo22`W)1aK zlp>wNz9n<h-a#@Gxzfnz^N!I z@=7Q1U@@Ve=JxP)1j=Q^He8ST#P`GNsr&}s31qLhixQz_ZgoiL2ScDD32tep#PPvy zJ?+hGVkc()?6kJ1^iUDofxfdp!!Z|OP28%&rM*RmY5U#U`F|_eGI|`bTs3J)f7bO_ zpX=z)dI)a{vBIznE7N{KCyrr=^>EFeKNi*;hYg$Gh&{#TgwPwX;%jg84jO`*WW_NU zU4)wfDu3TGv<$emXf;eweQZe#E`aQ#RfEAMZ|3n3Ee9TVExA$ZoR0Mk==1pu2o%SN zZG=wO^kLOEzE|lT#%Z3UQaSK0Yxex<7(KmxIOYUdVjeMEMvMA#V)uiseCKi@!s(V* z`^CPM3^)S;w4FKLC5FqekIxY)9LRwS`J=6zH&>OMufggPphiVMN{Tr?AeHW#2pj43yd-iox zk~e__ALJmsXaDszd)_!6G?CBi{zQm8Rw6_{Hu@^XT?#a~Psdk68-4}TNnF&aCvy?v zfYwWif8qGc?;Hh=kzRP8e?ks=AL@C$jFv7q9^%ZLE9rcAO-g3|bbRD<;#aH8{tn@^ zU5w5xGV({t$iu)xXQ0bsq4gC@Q}L75rMy%$S-)q4}mI_NxVVfX+8Of3`3syuj) z4YlrDVX~RP_CX}8I{4p(vBy+feELqrrUpoM>)j72H;$ajzRTu0dhn*37og%aroN(I z;@qXkE~;^~b|dzaFX9bg-*i^x%HB8YdSCB32sM8Fm`G92Q{80+KZ?h;fdmT~S%?Qw z<$pRpTCD4v2|7RkS=CDpM!LvBR`a!*M+5VCGs)xF=qrcNoTJ6G=JXp=&gp0eM0Yxw3KR*tY{X*gC_A52mkQR? zc0;01-$PWZx*T7yo?b~O`y-*-06M!~@sZf5BCKvg&MrmHU<)0*-lmQps_JPy5MCl; z|2#**qKB|H@L@b*y-~rJ#&dJUUEac@bE^y1wtk zI0#Cocsm|lNYfg~A6~@KYshXqNRD>MS`fWNX@^=1GK*7xRHfd*see$g;WFIR^lhZk zXDxRX?yUOS-p6>Z;y$Xy{>q|YUEd9yHF6S_{$q!mHUnp>_GRCM4|8DOBRKbXrK5KK zfesw(U008l0<^^F#tN{fexvN~9OI#3In) z#6qx>VDxz*%S)1LejzTItm*5{K}uL-kwq1PM}7s`_W(a4!}ABp83`fr!nBW}2wE9O_ z=R#uMunSG7F409_f8mIO=ft{^HpGAa%S+g5{SXp>ZjhP`Q9xgI5c5R%w1Hj>fo&(e zo-Hr3yUbhI`Dh2ESNI&zLsJvuZ7M&a!7$}4?VWyr3{*|eUi9w=3bq_%mnn$;eS%x{ z@6RUt_otHm`$6>YkJ5bHo17)xzdx+{cXaKTP9tiEt&>N^mc`bP1LL29jotj@BZd71?vsiO_+i5o`cs@SF#S&SlX0M_!}}Vcnn55fLU1vl%)2uWXdd zd;Z8$o?y)xNjW-^=hq;=4`Y9cAYVUxm&Om}J-C)XVqK}s*$g~ol-|Z;K(5)l1P!V+f*MU!>ZnQWAzB98UW3)L=!#0 z(wz_Vp?=brgB?m=ehiF^T{rv6G6)vKds!4@3?VNvm&Pz|7akApY@Z+2pcF?$3X(0#Y7{BKR-xI{0HU9rPOt zdXj?HQ_OzV{qwHFTpn1(k+WG_sM@iY6zIiQjNL`ijz>0>SJXf8ZcE@z6gc`01)g7m z-4)eBwW0tJk7j=c2)c0lX}KL4g3TvIx#4O=ta@KPMChRaH9{5pAo?AgA4;f`!8mpf zn8^$eEk~u-I~#7t4OQ&hQB~(Z1GU(QRn)2A+=(*OHM>x~*g>^x7f^?~3rcih z?-gwAgtZu{`!neK&%bpc_g`8!Quk8n1-m%{o-yegaad>DxtpJNN5zXFK{Y zLWe%SrgrMH)Z3R&3l>wKP78>!hK`ocolbR!{FHO%zfGCH4ygOl*(B8C00Lu=Ln5%(;K)t{ z#0pVwH2+iH0LNkle}C6={%c=>KqB~tcyA7UH>J<1|EUSI`l7g(RJS+#ArR6kIJk<7 z=@feCB!B6~TTRE@Ao}SfibDbTIwdD|5yz{*cOS8}KCr->AFz2JFBL6`$x(ORy1%0Q zt;7cp4U`&QSDXxMofCU?4b{c9lVZQ48_cNuni1zeNz8b!g1v-zH2Xdv=tA4OBN4V! zh4o9=71l2jSsuy##MH^a`7Gx%jFO2;viC+yowh84ZV{ref=3WZA5!w%vU56XXkgpkN0ubqYu)VdtL7WFj z(*{(-mT80Uk^-ya?Jd&=iFv0PF9)eUR;pp?W+X;)32Iz=9Y0|j4Ei^_A7qJv>2y5t z!1QML6;(9VMdLE6_C^d5=?uR{H8A9Xl!Lz9uPq9y(J1Z3)kgn#Y?Tu$M!BeNc^Eno z{Oaa^6gd+~KDZf4@-q2*+fdF&v%d=jUAVnVZn@TFs7m+vN&)8;l3s8=Uc%7zd2|kS zeg7Uq#3Ki*rBs!TGTieOwaIYLSJW9BKouqObr%e6-TV*n@&Y$9K&?YEQ2lGNO8HF4 zS;#tx0pnlHyNDjp_Z>BTH*Z4Xk+V=$hw!qGY|dv{%C+6`5V6p z_B1}D@hBCAOYs8Q7Y$cwfLh3Tm4YB4jE=GI9c^URNOx_q7p|gg%Qs52zk1#LcMCSq zcORTz>W&_*>AR0|gl$P^Y+8$t?Si;h1JelIXWx0V>bdsO4hq!HYv+Gduzmwax}AEi zCH>>q)?n7PuVF$EOYx^&T#U`|C)M>Y!UE1Rco^fK>b;11uG(|4ru<7JH2A02eF1~H zvYLJeMx+NaQJj;obx*@d^zp@duttu~on5g0$6${!DaNI*VO;uG9+%=2b{>~vGKAL2 z)b;)A5Xjl!MT01Am4``r)CdfXkH!X{Nu&o4Gah>m1iZg0 zjSsBjvY%RGri^#6rw-@Cn$Ccgn$tO9fcbY2{|tCScK%mdV~ok4T2zp%C|0zgZH;^u zWu^HGHUSr(2NQ4>svGt7HhKfm$6(j9X{JP$!YGFGHYxW#01iKs=>t~|a zbif(dkwZ&;z(dimCi(#ET#xhC=nw!ksZSjNVwBXj(_#It=`wcGO|S zUP1!&gwCKjNrBM5b%n7#$-qTA5C>;su_VS|$+W1Qq@?v3O+0ctY8?X-3e~d@AAqSE zb5SbDL-bVE3(`xKrx!U{gpAaqxGDNyg|{3(K`=2>gEGzJ`9nXoC3Nc%3Gw@zWICZl zI&@x&Ztq8=w^s4cT^mR5>OygEMka$h6oyTxW9pNR zaoAh+Mm4*4-Z8i(1ByWaZnK*apJw%*BhR0*R)<$s>uETIZ~Mr8*7ZJzqacqVgXx$G zf2DI$@1LhFE+*sI`#2fbqzN6_jU2{K(4~s3Ly01nQfYqOrb_c|cfxspkOm4E9ep0< zP(68X_FC9&8Xm1c7?*U$*(hnFD(QXbk&>3Q=3a=($=XxfJEZm#v-!WgPZvqMw(qV@tSa9pjY)Bcss3=7ZgQ(KSm9+@y~^bH1`QMU_d>4*E(ex;87dx{2u6{lxCKhSjhP9O=A*BP4! z3DA@y`#ZFsXx;ob3N|#s7?D;x&`h&q1uR(&yk%IEA29{XS3EbrDe!iQ7f8nIg6X7q zIDQ7DKv#re1UBfZ?0pYCLt(7yaU~_{HC_N0^dLM@{ATQZq%(3RQdRx{u`d($h0LD& z$Ge!l^q;1Lk2X@ar-0qam#pE2gb#B#$A3}xuf%`9TNQg(GXDKK9Kq#tNuRM{SF8Ya zJJJW%$@YZs`LlKSo5Dj)JqYiry-?B>Q358+w9U{#L=2$N!Wb3s&!WtDx}s`*Gh=Fmq412n5s7 z#Ch?B9LyO#4P)zK%X*Ku3NGKH=nC#XeBq&As`QfOQ}!Zz21G=IdaA0v>_E!XEcJvc zBjeN8Y^7Y8q)gfqP^}KHG18sPF_0tc zH0K{?!4mXOIS3GYA4<+yrgosSyqBWB%pvs7x(ap!ua2^R@6sfFByeg!SfN6TQ?6w} zVdfE;Fk%N$H0Fu-JqeySP-^|mlV`9WVj?U1;wDYa?5|GHqAn+?q4mgS#3DPAQqmJC zyR(_xCi_=P5i&`KSc~}bnF#p^l1D*WA$Ay0++BgzrK;jrm)NQTbGqBXP0#863Q@7X zV;-&VSpOAp=80D-J|x>u$-c~$X%l@Lg?tHdMHwED8czy&U1N$A)+A{MK#NxwBw2b# zPe=Bph#`-PG>z$zs#W&clM*(B{T8O)J(hz0=)B}*Bw7Y5tH@t zGsx_|OA#9%@ga33x}B?u-HhYFLt4#u5Q+Vwf0>EoHX)o|gl;+wXtHH*)fAkycQ?rn zd~N{=Y*_YNl;d z6x#Fe1~+(XK%dCD%09&j1ixG?#nCMVo9>i6ybbG#T@bOaBJGz;)|D`izg=+U9-Xs6 z@Or!85^MwhcEP1Xu{{@4F|$K9h8agN+=qgtFhnVz059gN7$zl3tVdeFTJ?9>mkVVf zAz_b}o&)0?#|@L6AA)rs8hH#FkeFxED|xPhNJf60=&$H?0I$kePWZ>)rx=wa0>O`L z{~)FRLQ_Nbelvr_D{b!iRmuJdi=prx0+ojH^D^?YkL0xP?Sj>hjhOLdetJh8y?@sE zPS6d3ZfN9HOTIsj-}P3ykKtEkq3g>Yhp$bji`X#K>C$4~$NIKaRm9NSq;IHM zY3DoY&X9wn*HH-^mB3L69F@RP2^^KcQ3)KCz)=YtmB3L69F@R7CIMG89P#=MuYaw} z=XIO)HI+t((a~fy2in_Rez$p9`SR+Ll9DSx5%GjTWcZ!Yuo3aLdjio29@@i(%NLo^ z5(u@sB8I24*%x(tW_a3zkuD?b^LTEU zdfi4xndmnF>Lw+C{8#M;w+;`$P4jkbA^#Ma^P(SyDla}k&z?L ztjDD6d7w|YR{C8e2vV1j#}&aHq|u6s^izJJUMA_GVXQc!?!A7q-P;@rK$QZ1ci1d; zdsWQ>L4k!+* z1_W%We9m_?7!093nO--lZ+nwa0LTbQE2P+w)is@kER+_*C^PHlmd^3d>YR7ZOl47Lc6ir#XEwBXW)PDZBv9!GSYEL79@FiKxV%2nGIr<|RT>THoV&gv$$1d-$gU3AhX|f9fOAN{q6b8L-b0F$-lQZIudUDbD$z(`7C8kXtZu96Nn1rZ}fO6>y!mWaxqEfOi(G`fA9q{nbQFl?UQ5y6BWG|-Yaz?pR>Ij6^D5qmO z3a{KxcpLDeBF!dzP_DSq?O%JqtcjSuKmf!XnYg>gVoq+H=!|x0NOCTbRjve1XD5Rx z$kQN_aM0D^M`z&jcZu7X>f6BkH0xZDFu9*x#@s|M;BtB3Qlpd*MabLQh7^+m(b_k< z{pO$k50}H9CG!(zsl==yH$khS77P1|tXpuoTp|WhS~xo>q(!>u_24Zsml+k>JB02&V;&~!y30eBS3iA&01+r8$}00apqUn-=;ToHgj(MCe-@OVRR^D6R-+aN{{ zYDMykCZaHKX||htc9`XECU~K^95SNW-H1bLgx>g&?UYFvN*c^>o;pV<4O#bC=1G^ zN{BR7T~&S8RI-k=x!`1iaIM$val<&6YAi^Z;8sE0gbyhN97A|w;`Hl!Jn+rUIvYM$ z)K4R}L>_cwhRnz}W?RSwi39>BMgq-ieE42aU9g+Cm^J=zw57${?8V5%3=(~qOePwg z_*97z10_O=2fYkO-2oa9)O!8VPI7?)A-5+)mNO}rbYm4TPWszEcu>@I{2s^}(OW|< zWCJPXO6p>TDea2#dgqEuRygY$mX}vlna!@Cs|iD-NEdY2hV^ z2DVu{mD+mM(3oF!O3F6{V^@gkR*izEL&g}mViet#OVPOaMROf-FP zqnhe)QE%8vqa~7IGsgFxuyoX;+hGqD!DUpkPkOu{l*t1UjvK=RVv#bMQT-+~l(HP@ zGMilPTo`nzH7y&_9gMY65Mc>XL7&m_q7b-m3BdtI?!qubLXyc#lii}@lnJh2OwKvd6~G0?sMN?<7-<=<-bwSdQfv@nJ)J5<>J-T##4$Ni3Q@uanh zm!~Ti-2^5)Fk`Wzp>o;E2GWd~ZqM48ewqQJD_Jkl-H_Ja7aXJW*w*xi(mWI#5u81crj5syxJPCE=qXop%Q^(8rymEH(9m;>s!?Rr~wNdD4|D8fun0mo9VGE~~DtSz2wJZW;?r zgXSiT=3vz5fGsuJF`?zvZ8p?o%%uC7;jZw^HJEPl_-0}@*4ggrG|-vMSY^(>;EY*@ zWMhiEg#WZkv%S(22gtQKC2W+qyZqsVRWLlY zz=-)Vl85^aE7##;AdeE9QB_VaN#%00tmtnDFGO@I<{ber8@~p)fm#t^M3QV(W$OMX z(e5k`?E=^gO#6`8*4(7GM`zs{X@h;TbnG-TBl>U~90OF_UAN%hEj#eF`Lu-Ir*gAc*CXXE ze$BEKT8X`Wbh|Vq5b*2zOT(8lY4r!v&{;et#W4-t3U-?;49BE{PaWm4Wqp@D!kF~+ zT`8W+9AsfkItyQWgakdiTVxTEu2zaeC<05N$6O2EMtp391bu3~Sro}8H_8XQahA$b zzFbOn#^h@$r#*o&>Fc{JZkN(aYm;G-@Vm<+d`bNh1ey-5RX-!_ir|F^YZtXk{0ts( zh0!Rs-D%PQgx&(8CrZ5jDKW@ql6T<6G)FT}|FS?1Z2r2*?}EtNb_-jROeyAtOJ-yG zfy^k`%iC|J2_v)mh_^d-ViWVs5_!rLicR+Rz}6pdbS{1J-A*wE%rjkUTj4o%PSwf! zq9_yp%qJg6q(5+nSsOqmwMa~wG}2KZ1vP1YMA`{iz=*7Yd*H`QZ?seC`4Y^y-$K2T zKO<6O`M}n6n^in7_6gJT%2-qCK&c)7?_OV0&NpRS%M^Nlg<c z)8g^DxPRr0c>Hp}cK{=R4JGk-(Rjm{J2M{NjOU`c@%Rv6<=OH0D-;fR7|;zkKFcuf z0h|Z87qAxa6+ky&))(UOYXBzz4geMb?f`56d;)MiU<~jcz`|_A2P^`725=r=_zK`VfJNuW_oXFNKI z2EZb~2=+ON0KS7SI`#tIgH`=>7E#2BdINj~-zI$nun6C2JpkwidE6CEz`T$7VUt({l&Pbv(k?#pAmGD*^WcJ_EQP(0xrjJ~^}= z#JjK)5DqvQa0g&1J>L)g0i5uYczhq=dcaoz8y-RZorq^Z2jDA@K@Rl%IQRiR1Na2t z0rvyG1NaW$gr6gwPoeyPCSU{LJirO6p%~-VEjPw>7LGe*{INL$n7AkcelmXZCZnFA z4@Q3BqWnpV3-UU0x{V7@UU1f&qR&%QieHIe_vz4QLdZq<%kk?3zLDa~RSHN$;K6C} z_yM#7V^MzL=8W?ENgFdO^Ua>D%KWm7f6vV~fho@~T$rCzooARJ+lk-hrSbS@X;P&s zzpy9c%KZ9_dvfy&%ky&_c?PBZ1b(Y$#p6&Xz6iG;zXl7g7|-wER}Wkjpe6l9`KB{B ze^MnUy9l~O@lB)`vBalx%>^z9+{qN*kzcqpKZoT|3w);yPdwbfcLNWWd=ah_xN9tY zN#2_!ehE>bj2km*@+b9V*5;cRu{2OpgSLLUNBDh?=oS-Q-6II%IvYmVZQwhd!kC9c zc+@9&Q2K{K_YKg|JzWIH<5Qh&z(HO4D#d>jIO69gjIOJm%<6ozBO$A*yoPdr(7+_|J!7xDjU5*^pyc-Z$g&{Ys! zQ+{CsLP#z}zz+gHgYdWrHxIZ6blP~PdZ`8OdEk=uvMf4@}Q;qf$OjV@l5oemr+_;XEtWmP+He>Qj1yUWM8^L+W=ZG(Q@5nEJd|c zQClHh8btUGg!79Y@Dg1@z7x7s+Nx9uilS7B+Ai__E#k%ycNXrIE?uIO2ahD{4-lS% ze)}{E$3-~mlP3W88OA6a_0J}7Qv}C$5xDb!a}f?U0C~tM&+~D)NEcQkd^5ryrtl^C z`^S;30tJQ9PEBR#@5c%oK-fEJ!tO@cPWT1(cqVx6L)hi;30}p$lH+1ZbXXb4@;oZr z>!2xyKX5A1;Hsy8@D1hyHh~iRSf(o)L(HLh`5Dh(Vw-D(pY3kV<;mt2BD0R2r+GjK|MKbfvqOqOH{A>3($w!W$7jo5Io7yi#8e0KW-%7Zqr% z`bx^TI)89n=KEA%N6L3GMW;4Rdzlx1Ui1PHdXC+A%;xO=tSy;aGfad}NBAcAosGDs zd@SQ!QeRSu9x#V>zaBJuK@%gIq`l(4uM@hDY{IFj%DW2p)qJno<|f3u20koYHNH&z z{|2Q8+(E*yZLH4k&dm6VYSUGDq!Z*jH#EiL8IT2~x0TxO5jw%`VsZZPgv{$nCytZ> zye#;T_!Yv}o^Vw>{(JP9D*xcOncD6~>T7zaugQ2!HT*>1U6Gd&u%VXZ`GKjhbnuJw zGA^;9mgUvkF!1D*&E1B4_~6TP8wHN+!`;Aj0(S%Mm3_##nheA|Jnlo->j=9^bg(~T zNh06ub1cpu%*l8U$yuF_@;uVtEF@Zn@xd*rak1{&_XxTboa1UdDiQ&(WY+^4OZ zr&KbDx{z8v`f0dO(kej;`KvAQ#GTt;;o)uFm^jEB7o$K$6Hch=P_ zP&3LWbt8=SSl&Tl+#jH;W`94UPdh(U51T=E7^Ug;3J^xd#PyD~B#7&{|mXvjzC1pnV+th`~X^}&DF-ezRpMfUbcM}$lUUsR^`;92(Lc>K$Rfi1{rN0+ihwcA02 z?M2uUrdIWJ4{*lrq-~%&+y~qTpratV2=`k&90u+b#;7sQC~#Sb`&q)F4_gW0kx!I^ z2;*(|2=FHG6M)|ipNY!bPyLIvO=7^F`E}~qVcQZ$rzS7+20N}iFLQ|=rm6a8TebfkOg^1@0F(D$rOc=?gRkmI^ErSTC?qU{GMUzyX1S0(S`<5;!byzray} zMuns=&=goIuuNdRz(#>Vf!zWJ1P%(^C2&aKu)zHSM+Nc(GO|!usq$e8EEQNLuwG!J zz@Wfxfdc{u1@00!Byd>Zeu1L`jVdX>KvQ6;z%qgL0viPe1$GM@5I87sm%t%`!vgmU z92ICRlJo_d0!sy!39J{`C@?6nTi}4eL4msj4hb9vlBg>LLAOWjem}W)0_fb7ux6@nW^-Z67EvuNBrMr zqhBWUo+Bs~VxNuv3ZW0Dp*ONp^Yb5cHWse-H1w^u@?0y=EotaWZTxQ(`k>H1CF$wt zZ8rLiLf?^wew&Scv(U3sfH=3xv*sVO(QlJ*-P|>uhTpQqe?Xo)h2UXZxS5?Q&!0

MU(>Bpdr{#QcZCIrvh^0UoG|2qk9(hO~I)JAU${=1JsI>8OsE0T^| zVhqyRF#iYF>f=;8yK)um%u}#?yn^QO3JwW$a8qEZK%Fn` zCJ6cn&@GrYfkA7Ub^L9bzy@`=M%&|S{67nJOTvu;wfwXkw4O%h$|17BGQ!g1GlUdF( z{+#kHDFxFsc7~B}46dOk!{9ZnfHY4}`2B)^ivn?-gCFC?;nDN?G~On>o@n5;sK8x< z-#EMl=w72b1{2S8QS@;JR`bYwtVu{*>+mB!rcAi-8dTs01wSYi{zLII_X1D+O^@PV zb_@`>p23g!G>T&IT2bKM6@1~1xWUD1Fac?rh4?f|KDEBku#(Fq?@x4jP2k6p=QN== zg`N|^zZrtBx8chKuiwvT`c=SFeumt*!KMCVc|YSP8v6Z-rtbxw@~PkVX#AI%euAOj z$7uXLz>k%GI?##u?3a8V$Wk#LV?L)CdnG@DHlAiYq|$&JT%}SkFXC}5{(ne=Ka>Xl zb{hPLY4ADlPe^XVl22Wag}{%MpVNS+^6vT>*s#aE3Kb8pMy4iyH)rMeOK|}^?11bA@HmZLSLwY`CKf*@3P?^M^hxc8G@j2 zX*oP9{D=QTG2r!ixP1xuvE*Z>k@KsZpA(JC=PLe+oBBT%pLaOj0z4Ey_%QQy8 zD$t*RQNKgxo%H*~OMyQzm6V@)U~f)EW;#I-_Uht)2&vie1Fb)4KH1HGAE{s)*fqu_f$arCD0YySTWTXFr z;D@&;VlBfO;6H8PLq56i$k!^|m~w9gA>c{Qr8YU!H>-)IP3qRH2CL$KLOt0VVj=+J`Me!84tT5`u{meB>PxZ zzk}NqLffm!jJM`Q-OtqYgX$TNd@U4w{SNi~oZwfa;nNB{<$vhX)OHjRKBXeJCBo<1 zY4oRC==TdfuR}(ht%5h26$Abr4Y%6`Kl(L=zd-?G7vtr@0`>t<`d~=EbB=`lHVyrs z)8I3qFw~B^MgD%_bE4pf8x(@SN5<``j29vcC`p4~D16LI6)}Goirb6Q(6^<*uM<9H zG(f;rp@6YT@ImQE&=UCiir`1ZE}=)~YkM00yVKx*E_}@2sQ7xk_);4BcY!DUF{M40 zNjsvw0miboW*Yn)#^)PlVsBlN{|kX9yVPjwUzP~{E@>}s2z?{)rrcXWfcczk#BA;K zB{apc@)>3RRu1ZZ4;FNd)t}OLUZhv0zg2YntpaWbCEaf6h;)DYUBS<@<>wLj8&uxa zw(>qG{2N8D^moKh3BJ2urQ4{0@q5P0g9VHV{r;^A$={dJEgU(j$LDSN$pL;W`AklO zpTl_gbz2nwg9;dR!oTqnr6B0O`C2J>!=>Z%QRs(7|4$SC z1B{mk3%D~4{zt-R;08r}uEe3eDyV*UNxRDv{IApSc@ub&XW130`Fvmaj7tArFMN&x zL$<><`O`W)N_Rl)zzm_EEckAxLOd+^&kNpkrQ(Z)f2oZ>eP%`J?z7?PW74te{R-hf zcu4U-Mbhm`L%&(*_ctlRQ-%Jkz*D-zwsZ%De!ynm?$vxm&tt-8uiy)XzDn><3w}uK zm`Ct03%*qB|8&70V!S+9K=ybg|N2IS)N;;ayoFG1PXnIX?|$*4$^?G~<1vo2m3O-E zHy=}kX9)dyf)C!Q5T^@%nc&MVQwZ$`u1eG2IhkG}Tfo)AXHfiVuY_$(L%&Vvho!&P z{m#Fop?^^Gm;8%6V*Fb0qqnOB`Fm8{z6Lz??}I0*_A)5>d0*&U#}dIW5`3fJHGPBNjn@?+e~*US7RF0t3%E|`2gJ_LlCYZvKP2)j z5&Smm|DOaz<=KItZ$El zqK;)(PfmlM!FbsJ*Axf-mJqjRr=fQMPwi-)_{mTYzM2F-DC4Pxf{!p>9xUKX%pddK z;%|Ia!fqG5eLnEVjJGh*x4|waZKGJyY_!l;ZBC~fCkcfDU4|3ew>f1CN+)gL=Ja%W znxhdquL+yedLvHRXWA(&oKBbD%?xUZBADx5c?KVW%tFY0zfgK5S>|rreE1fr9$IN-Rhod$Ei3u}^wC1O<6hUGNj~ z4_UL!gcOH4u*i@nB|4lghQrl7&7f*E)KOzN<&94e0}byf3&?xv0pfImR_uyLv3;%x z?dXVG>{I8YlihTsV6*TDQ7G+j(j)5E?Pg_h>RpUWXj@tzb{2PqiBwesqLCn_PMqGb zlg_Ft@rFY#h{F;>TWTuJ;*C+0)tQVO^>c15i&tGG{m|FIie%S5pCO_0pBjDu+aHR4xaq zp{_zlt*%|Ru)Nl}Y|)|>RSnLD@`bfkPGd!RT|MBUWy|Zz8?dD_K&^r1>4YacA!*3 zPpAb|u59`&OgI_CS<_GlL!#RY>x zLaGKG4dQ4qXg_5RS%-c(TX2>`g2!9~$NHq^m+Z{iprv@TNN1YcQuE@dDzDV-p|WaG z`O4Y`qpqT^vbJVv6&bD8uvK|sds3BGo1H}dc@t$Lc%&d$v~1ZDnI@^$p#|11t1wz{ zf?zl$M72S0#M7P>uue`11zO|b<1n%RH#WLXiJeMGJBp`Dl+*ytnySYZPpk8+QYkr3 z@$*m{fQ5pgmwivU)#3;?-q0LfnNBa+D?8U>_je+*8iTnu6LhhoYB|p2By(cd5|T)B zO5VgOxpBxN%#(B}Tt(F4Q%K1=xTC2GVbNGBJy!^KODQp1&@56bLbV$wZEU>OqivAd zT-2b3iz6$2(9DQ0%odKe&Q5TggHDak)=MC$&LJ!5bM)5gh>m349-p^8g_4Y6AcUh( zTb*cXhHNA4T-%P^xZFvbJ?m`N?e^3HL-^QhmAhpaATqihHfOr7lom-zkb`#EF=eNm zMqPc?@;YJ~2_#!tYUZ8N=W&xnp_Jmx7Qu!BINqjR`(=hj_*B%SzNqcZjWy_EQ;M!L zDH%7=VKtOjI1DPdT~(l;C~G_R$sU^pYN@r^DZ9$TqtKp3BGYr6b1k)!(tqgY@CD_T za22CI;D;PhuPrUUXt-_cERY|nbth@56CFm_-eG8Q&T-BY!%a>N^&Rz>G&q(m)ktz2 zsW?s#|M#X!zbV>E(dZmbj=CI{2BkY9=c;j|UYytp*k`rPcrwAK6Ngaw5Oln&3QV_9_x)A37Ao*`w6qq{Jj= za;-OHbLc#s-qdcZr6YtM&lWSCSIN%Xn68`nc+sH9iF;8t`aIS^Zt3i2>C;H|a>}d` z6e(R5@Fx33QoMvSmMEzd9ExPXQ*3o|&M~HgrC~QOsova4Woq` zV4~D>$dhdDbdmdyA&X6tbPQ60lvE4#jlUEWYJu|*<%6MV7<+Tk}%a@in)U2v9 ztgevim*Mw9IZ~oq+YPw|S|e1`f~`p%cag`IW=vp|;HV8!?-Ej2hHd%|M`?IUTK&-y z8eDrqIPtqAfoh840Ph(X0WtyQCwV#yjoTw36{4FHlj-RJgoQjl7ab=fcR^poD4~(7 zQG(l&)_~mL;1Z)GLe`^%M`0y6pBU}ZDDku@H`@t|lY9a#9ZkcWWEO$r>3fPxNrMe% z*M%dYk9aFkkgQKqfa-*Rscn$CxF6Ao0lQn+gLw~$q zqN_-O#DD!hMkC5`^B%sv*Xis11~kNCD86(&d-_WdN^?(%_>>u3dS8M@nFPxQZXA9( z{v;y8wE{oNs*bPsH_%Y;XF#-c5fAG{)8n+afR2B+OrUGHNh0X{YdQ_v5TEuc(9e3m z0u6^GG0H#D===-X=)eu}*6|O^#G{6Ke*~Srj<3sqgT!Ab4E25s8tVNSDE?>gv&ZiP zj*5uo2I?wHg`{8BOXox5HsZz_-z-xh8uH%Znix>8cnsui(@nohIO&l=Ke z25bC5iLYU!=u^7*^xDB1-&iJ;0=E-^<~ z>-Djt=P5q+^z6^SMqEsRC(MpLzCAtttl`UuTVRQ=*Yh5h^fetw_|nfB{s~yhX+li0 zj@me1<=^C3$$$3ezafa)j*hR_S(*}`_G71u+CN=7T`g9cr?;j*x>Q9Ny+A?v+?L~7 z<7+;dAhX6#tj{b{@%QPB2?rfdL;C#J8egv;-Cv>NKd&AC#l#o9mVP-Oc%s-hhlg!|mY=i_57%)sW!b}(@B*7sIQUt+)1PBSy|Grgqx=W?* zfHRrN{NMA{qt>l@>(r@JrXp&8iA#f))h*td=*rf3%VZ3+1IdI6fPa07zW&}V z{cYWeP3bL(wvLYeg-9R`^47K zyQ4?!ZK+gS`&MyvByHC~TX$bqQv9?lk=ojyZ0iuPueUF;8RF)s=xLCIgy`%}4y3@# zTY1r{OIlX1iLJVb)}|m9Z(iM^?5&rr4K&)idR3FRvSsz^RjWN{Zf&$F@K!>L7kVpO zTUM|1R<4b$Y+1E#t+x{Cjgz)%Wpg}sQHys~I+;!;I#bE+L{D;u?M!q)fd1ZH2Fv>< zItLP)(_LN0gU)1kUrGoOPB9hlWX~k0aBbh(2K|z&wlUwt+Mn$1-JTTJ=FXm+&(^j9 zfrSyEt%+3Irmm#oW;qjEk}1!2i{Lgk;Lvm=yC#K^XuA5-iFV|=jwSC(bhqu=luT^x zOnDY4FAyraxAm#r(5PqobA-asmKr%ik<-|3b59S7D3w+ljLTl?oaphFzbanVVb?1TjgQi8$on>CP63$ z?Y&)H$#yiS7B5G|s>n$ror?eku)DLzR&lOT%F##}*p!deK&H{nQCh8Ra%)F_Vmm`< zqC{eIXIB@R;NI>;KVKlWS5I=M%1|Lqc6X*!n%epXk{t7E^E)24*XzZaIc9VM%uRLHt?*-`^C1Fy zvaYVS;dG_1jWe%riJw;z3ZZkUO~^aP%ew^iyr)mf+Z&a&(L2nW>#e(&9n&E5>oq~< zx53~G)g3{$Y<`Wxnj;l;BSGe8qofbA^CCf}#k+^xgNE;=;J5osFMQibkX;+8=m-|f zQ^8W@ntbr#>MJ(vdnNqNH`WE2?}D?RT?wkJrV27^Bh__J4*n*TJ}DS3*Abki9+{W! zRZ(PG8WpFy3H8XXtIoV0WL_J3G93=G>#Blme}y6(pL^Xq8oqPg(eZ3cRnY!&EW7OB z?sFEUW@L^ayUfb!!Pj?Qzhdz9RoDANBjG(iKxcFC^``L9Z(-T5VrFz>qr^vB2VbuV z-*f=>U|59@+RNS_gq}EO;=tJ;3x)UmHT=dNz(1|H;Z&q5$XpYtFiKhrXBN&NqZ8He zAF1RAX72vYKx*Fgv-Uimx?uNjx>N5Ozj*g=(y6867wmpt$V)c|og`X8a_e z1L?)P-&f+LYv4Cp${bRD%4eSPsSF0sW(vJ9#5Y# z4&IRH_{p&b_q?{|$DjVIhkazc5@Y+^U|4diZbT=&Q~6l3j@<^0SsK1^vUA5|=hn&2 z%O*Q7fHR=3M=yMHVq)U4R7Eg&O{B`ZzJMUv=17&+*4W_;mQiXNo32uBqa@6249;W^;Jp;JnS@r<7y-TAi5d z5dIG)CND;iPnJ{|oyf_q@e@s{NF5`VsWR1#jF;ato#{Z;MU9_Qb$wlPQEP)}=8dmR zBWdR4Nlin2kX1Zse8E=Hz-l$dpCkI{4d(i6DbUCFg^T zvh3BW9EsG2i)E(P0YrefDl3DbiPSm9Tf&>-7;ZkOOQX>ZGG|^etU_t--!`p@mL18j zFOd9TpXpF~sjW$0y^itn_d9#=Lf_uj<#*%d<8Mm(ZGK@aXLDTVu_Ne{v_dr2CV9C=BI52er>MdxM zB>R255dEfB)d>XKwiGGT=v4TrULU=V{o$wm4Lx49Q;fC6-~bU$-| z*`Yfl1G<1fp;l%?x`D1_5`zL5lvtDQ>*MHz-`N3`2?ORfh8w!6yr$N5O60{}H@<@V zcX_QU|50d_6RB<60;Q`DaCmj3+qJ?8`1n?&H25Ct?`($(RoWLIq`wINPFc*)B#-qb zszxxFXUr@qJGtVma}R`zy&YSV_-t)XclRk$Ren{HQ79B*Cmm{jD}Gvc-g&V&hIaar z?VX!-ti@jJZOJiIrM#_w6S6}jMQvv17umzM zhJ|sHx3X<#XLq{WYJH$|`9@#{pn*ZPL98l~jIyo0J&n4x3$?b#U%6sZO+Y>HHsPBH z1)~MuA-YX4YG1kPf>pluqz+M>7W-?GX!ug8zJW8BEZKtAE4^tkTB#+i%W6;UsoPn9 z#uELluw+N)w$3GMwd7)U1v&M_|n|J22& zEw1G|=>IS?bLRY0BLnl7MFxD-M%4lzd&Q}zomJO8zb?{o>=m`${=qvw^F@EH8Ugg- zw~T&EBVEdKCchqNTIYA1wRV12hj!)v^D_m3PSt@16vG+-r|TFLa3-+74it%pdz!Z~hCD0jmdK?xZUrm#W?8PhwbWxO zrkYZ3&J=K|Ley8;B)>BZ?v~!=2Ctt)jbdQ%D;)ehgS++oLW6(Eq1Ww4xp$9)uQT+& zcJRv$J}sYR3fXAzSJX5APrpyfy@Pp3+1{)EmwV$5AHt=cyloYc`qzj5tUs=~5kMh) zxCs8qBKW6^;9o3)-wS*$%875v4dtU5!LN`!Uk5%H<&gd~EA+$r0iMKtqX%6MJnxsl z4?_t(+7x6}UxED&ze4;u>Qe}xQv^Q@IOUhBDm7t&!7EI<1aDOMG;ckgG?GK>3|?XI z8QR-Z)4FOzyWPV#Ya-Q~=t*~bdO{dq?dbg{+c`7r=}-6pZ*jb-mjCMbZz=zs%74rF z?==29o&Os64|6B@U(bJMD0iKgI6uB>MKqpRb>4YvTGl4kMps}inlYz(F^#5&@DuG@ zw_!%DvkQ0!J{0uSv-%RitXNkcWa5+%gfg?zYCL_?wDTcMbu;na=H5O|W~EckJole& z0^I0(zIn2^yaPWDxY4Y&@ON4`>kSRwLtYp^p=Z6JA^tA>1ZTaWA%7n~!C7x;h#!xi z;NK?*Lq7Bq97S3g;wRu2Xs36ag|iF^JqJW-+;m-G(UXtRf2aul*&_J;4vw)qWxiqI ztXIPSrv`WBdDWt)JoKkIo(YBF!^W2u!Oybrxdm0D5`D{?x&n z4gX&{xRm>lX~*bIK5Gnp*uoD7Ch{C(;iO-0=udI*D-GV@;2j2!Ie5Fl*H}1pDe-PF zxT}YxMNfHze}57D!xnDq`LBxb|CB{f{-Wo-Mer|Mc*K(behVib`q4aM;cTNMz2C5K z(jP}+n16S0>2dwQ!l@&vr_WpX5x|B2FAYxG6D>SU0EQ2{Jv`gsCBW@=Z?%O}&gJ;g zY_M?3DDm#F@FOjJ(8Ae=75ZU=^Kr6;f5f6^yr<$v^9cuM+fDOn2bX%i*TI`Lx%X8E z7ykP!oZkx~=f7I`(H8!12IqryUFg4O(c60anS~!?(T`hvPO#%N+iT3|{Zxbq1IEA$(3V zc%wt#VDM%KmwFgD_+4ZO6W5QN-dYTwRtJw6T;2mB&pyLP+I7KyW$-H6KWX{_TOGX3$kXNENrU$}c&EWr4leJroeqALp})q#-(%$5?cfUxzQ@6@HGFP% z@EZ(%i-XTJd~S8{q|xU`9bDSG+a3JFhW{rVe4fGYbnp+Fcnz$&mDAuj%*0z|JeVEC zk2SdO;J(3Y9lY1zjSjxU;H?gRjlnlM_+tj&?ch%tT=XV#&N1oQ>(C!%@DT@ZGWY=p zzrf%J9eks~t4ukSc%`4=JNU;9eXWB_f1=UB?=bYO4*s~oH#)fR>2vUJ8~WW2F74i} z4(>}iad2th_B!~bhW-f$-)Qgy4t}-44?6fBgR2G!x~4bF+XoC@W%60@zc+ZTga4Dk z8y);3XAIuw;1dSl?cinRy?CpG&oKC%4t}MH5CGW!ymU8B_*L zqsdR=@(v;%Sok6W#`xyR=#2;pao-Cv4(PV4Qck?Z1G~Dpa&|Gt0@QP>*!q)QZHrS} zHMp2l_TJ)6nE73dxmq|#jzL{GNu)93P~LzSbNyhK$9X!nv|ur&oRf<&xVuPAzIxid zr6;`@^R|7-eyqIK+>%?(B65?)|H{KhV9~YBr_nf$lKe7m(%R-&I9OyFZ4h>!om4)} zfF%2w3|m1ErdmI|lkg;NveBTPcc~Mvc)0PiERd%ge!U5Ain9b)vgRDH)Zs^-dR#T{ zBjJxUY&E#wgtM z%h2(yj)xCg+pL76Q2f;=Xev8?#_h(x2^izA)#SPVw^nJZT~~>SI^?;{!gL7z|7#|^ zjnOXQ1iu;<7BN@++!ytl15JX+(Jz!s2qEL(4^`afK;$5#GO3G3$wNeVpgh06%3kR?!ti3ng)xi~W z9qHsuvuMXG^dQqsNwsVQ>$cicX{@g^OTsk8LcgnXThdQoy5{`YV!su;MzGeTKiSud zHBxOoGu1i`zb!R$Ce?rNj$3YhTkA#i@{XAh2HOg-L`biEGD}4KwoMoYS4)A~`moFk z%i+1IOs#O+)tmNrU`Y{{kL{w=NDW?NTyU}wE7T#VS~|6(w||>n3bzo6*K6&tTrBA) zQ|&&M)bOp zwOLq1>DDBwPe+SH|8Z4GE!;Hg^1uz-D+c^6NOnI~l%ewPGE32*xWD?aG$(pXj;!O; z%l1s!oKhN84#%2Rso2_vHMbz#-r14tKpWueCAAbmRRGc&J}d@UoQTzavLWcXzrID{ z?`li;Ap1zJBbUmGrEpw(DI0jWqP5q@`qTDpUAvHikN!V?tY;v-d2?rbCzj9p=*D(K zeP|_6qs)@K+yYf{XW>HG8%THba{Xz%vnRci%j0_cvFVIWVqO!)md1i{7X-q7BCJjA z!sWtS=8Ou-aD|UZstrb|FxIau~E1WEq-42xf+~nEawmS9lgnco|Dl!C9$v*vg5rV zZJA`6swoIOlMSM3oRAxxSS*WHS5_QK+Z5xnZ2h`HMsp470CpBKSM^%#g-|lqO=G?8 zu9*z3>YS;mpuk$z)X4+7qPF>{-{8iq+R?_%TU)pemr7JSiw0)uYJVOkOSRtfDX#wPBc)C4@;9}0%tT9Va!k}>$_-X|BMpWnw1m=&w<+X@og1R0K4QIs|vFz_Ca1-(&uV)&fZ%l8KmMQbMfO z*aeHg-E6RWKF_PHx}_vqS$$(^Q>Fi2wPa`omi;iSd^bzjdBAq#M?*W;5uFEG+GWhn z#>rzZ>0o58hW-yLBrm6N7JRTz`ro<6h>;drOQdn<1Cbz1X->o1E&mIxP0{p{dZE{msH+bOScN)Ca!Ohz7#qGwz(-;b`$H{NxmG_ARQO7pq zz12=!z2x29&OB)|v~Jw4VIjc~ObF4-97#KBPufG+y)C0lS(gGv(Q}827 zG%od1@FPn#?(0X6|34dC>Zj1JqQEdxKLvln;8H&YpKBD>s6y#+E<*1%O^(!0p`T;M zYNdV({&xoV4Id`QBuOF599-IM;V<;^-sp4arM=(d;L_f^b9&OQf5D-b_WLmhmv;J3 zGnW6qXZYSnI+;iLH;7JnqkecFGEaGs);7h)?(bnGf;p%ks;)6?g>g$cJ_&B(rnnkk z&--@HHre9FUu^9D2@_1>6E3d1*x0>Bip(oK=rc z+uT*9U9zT?Z58uK!i)aa0b~5N_}QkyRG_ySJAV%XNm}gi%cKI@U;)}5U{sM^ml>qq zg!k#NjL0t#>lNd7!>>2tpB6&%bi>~OypaC(nz8Q@6OQ_0pG?Aw{tCs-Ahqlu!^qqP z-w%btQ%tw)GF*juq5Qq0T*s;2eTBmJnTGCGGP8^ue#tU36mCY(-)ijqjCymNh5z=) z&Z}9;jcHWRMvRox z1SeqxnWsi6ZjfDr(-5w%!WRA33Yn>6OUvTh`dblLMD05Y9zXX z&clLiq9Vwq_4fN9^X&N8dZ^+GTNjfQ%3_*RD5A<%)ase3^ILHbt6}(1X7k$;@Rmdp|9g(4Sk@AXZDYew#KqZ*>uc)RRQSJaCUX9W#ADxlx&x#I^BMupCK9-%Y;TqB^J z{n53m;H=5~R+S_@@A7i^-TPh+U#7YrLHloC4p*@XL%Mp*$ZQTTd2nF%%RzYINDQYO zRE@vud(|Am63_fR z$owLfnT9etehmC}%$9sXE*z!31qp7ChYqj+UV*8o=!WQfbU~)5mm%^ss3{fD-EULp z6-eoKfE_aCyW_&&z??rs`mSnId2``6!mFQeOAWYD81* zrA`R4*H#B-ZSV%5rsdBD6Z=${@lmBsWp_)xR?z({?otdCU8WkjJ@ibv{Htu`p^wb7 z3jYe*OlD=paG9E3{EOFqGx%7@l-nyds8WGkmORe<234?11!Wn&Fvwnva(Y2U(C}dD z%pi*cHu_MzcA%2Jnko%4=L8Kas(AKIZIJ1&3ih8@f#?u@fX0J1SVKW(1z0pz>k^(l zzaraO)=*`&Pti-Gmqgb^*Typcu5(`>`V-IgRz$NcWzmKfFFf=~FdIDAi)VLKrLP*E z=c$A}c%U?zSyxu~ZWm2&++wGC~>GAKxGp{K;p1HbeJQBk>K0uIqt($n}B^@fWpK2=G46E-Om;Ldg*p zX-AQKV@A)_mIF-&*lnKJ^!Y!(U$lmit9_tqzi6z|zeu)=s`Wd~MmRMdZ~^y=u6&fc zK_kF78b5v`XdVIgoPx*791nJrZ#HzM%dEbg<$KS3hNyMndkx)%d|t%3FdjWv+Fa>h zRaxDvcBn@8J*wmWzM(yjPbGhoq1};-n`tXE9kSC&r{r%YU9{4_5P+7IsjG^CRa2Z#jF79+Nwys|=*t-~B$-642 zZ&`8mMbUW%Y{6DohC=3%>EqZ}GcjqY2;6@E7Lh4?sLvR&udO zM0&mMU)B|>Jj>^Wo^P=7g8KiO;hiAw{M-b}7I-78v7`9{DxGQLab+}Z`k}Dj8BE57 zS&pmtQi6Gn|G@J@B}miWxUzGAi zMZR=p{rUSUYf*q}R(t~@eiJ$LEyVC`(Tn=<0xKDlpENXP>e-^v{eRDJ@Bn8Z&%-!%Ca`LF4W~e_Q%RM&+jzdlzvC&~l)z`ST%YW*c za}8pcPc$dv$MWqOwmXGzmT~ft@Y2?-FG9b;&~uE0Mz!O3Zo!Xywh@GpxVFPiT*ly} z?Y`0AA0RP|YQOQk4L{OU z^^umJQ~L{pA8T;c9wN*?82qqlnt^H~V7<@aXBk|zZNMKhc(1{wE&F$a4;oyxXP`e| z@TDA(f{}LV1%o#mT;hGh;N1q7eDU1d_J>-xIQT*auzz?>Zq{mm{{HG%WsXt7J~+N`f#hzF)%%XBiawmn@uZyWq=F z4r$28o(q$8w=RF4^+`kA-Us;u3%BDv-Lw~^eS))|K+AVa^+83INMI)f0KpVb8SyqINL&@AGdHjy$cv2jH`z;EZo*x z!oqF+$o^V-^Ia_Q-e%FWEfM@*EZn#7_nP*R-ZuT67Jh<7e~*PnEc{y*exilz^G|hp zkF@Y#TJ+pvA#z4cdrEm&<^^A5;dZ>O7H;b&Vc~YV{>j3RxA@<0;WquN7H;dI(zMr< z)0XG*B6ypH+x)MyaGOsf_|lNST^`~^>esneIk)LokO7R#=X5JwHvLVOJT~qtE_#l{ zM5U0x!KFXb;ovRCeT#!j|LaZ%m;Tom99;Te2OM1bUouxk8Tpg`*D|9w!KMForGrcV zYo~)t|Laxg&&i~R>0kw^MpzjJWue^rstqL=Kwr-MuX>rMxk{?`{AT>4*S zrkqQ>(*LSaX#{dMafii*tBUh&s$7A zH%>(>c0Tzoqly~wvrUcu@bh*X{qbGPcq$B$^ixhUxXCW#>v`XB;uQ~5?8T|}Mv~_r z8(!wr*@oHX2@^lx6}A!i<#`S~DU%ys=HlJ(;+7}JIvBndKieEM@h`MN*d_h)TxVf6 z>2V^U4HlqH0%KIH54Q15c;5zLciCPGjIy}l8x7%llmEiT4Zjl}h04!eeA&aucoxeK z`*#vv%1@!V8SZulfRXaY**5lL-S8aGaLW!LuiDJX&vX(ddM6DH$qI$Pm7PDBoAe|1 z-wnUy40{&*t?zg^V_B^U@b>4#M~}!wZ|Z3y_SPM%=ETi8>4Os|UtbxtPsp+9*|OL7 z-O3q~x)FNK$$MqX?%B6|UC*{2+#99$j-2CRw~Idd%WMWy`&G;e^={lIq7TY8o?Op| zeOlev{3}b;5ndLg_h2b{{uuO&)OIHLsq?vUJa#+IGWL5ny%$?oy!>K&b3V6TQ*V4@ z<}`H$dg3xm74t!P@lPj?UB@;IvoFNriv|1J3cy{xNP(3+rr+M}lh^jt`s(JtP5Y;| zGp;}MGZ~WKx@Y#+$$d4s4C}!0>Np>L&Dv(%wZ%OGOFEL%1Fwcx0E9Q%pQaXjLD07r#mj~?vUGOo5ls3V<=@maAel^igPRBV`q%N@*) zooK3T_2AMmY<}qNMmfqC5Qh#sdenBk!o=V`nO<$n!x?;=uwh~fYb8m+bg$Ee)Rs6; zM|mv?)fv>ffRbUJE_dL(sw}2NTmIye>g_YTNVu=W6l^8eAKduT!6V5PnZ+-+=R@{F zy9QEZDkY#q!5GDz}dDfd} z$Xh2?DJ5mzE1`9F82^Hgcjg0&RkT((@InO89W3dWX$}&l8wheE%toP+-% z^MVU``ZBL}2GQ27w0h;ocr$W+xYAofp=qJ=(rFi#&z^R8`LqF8uPCpc7Fu2&`p5Dk zrY*HS7b(wUl;<5KAwUx$s|fp&qT@_*vjqjIR=!}2%Qgyqg`>&i6JG0 z4vcq!wD?VRXPCpeap6V^n}^R<8+be0;pCRevv<~?zUcI2{-Q1ZqE)BjH=v)J z+Xi-FA8mhcPj7l)k-i>o5l)rCogx0B&Fa{fMR+%)(7WjE$1yM<1P9~M-h~@5#hd%= z7j16eY`Oo()`;+1?KoAK%=|Cx#N?YzniE4Z<-sEQzi%#Bwck@@EwulRM2EhQPpxk( zz(3dUPx32Or|M3El%rIAVHLvB(iJeJp7pU~`ImYeRi0`}J@cVydGVXD&>T0>QoYNQ z)|_FAIJYQ9+B}vSb!N2jXfz}X;3mCqa4A20MR9+bm^+Q71-mgP(AD~NSfrGAh_sB z`~^Rqfnms#hPcQ^x6gktg8Ry)*Wn1xcv}tLXr4kJ7`HqH*DsiSIO62fY3SFRr_kSO z+#4PI9^)2%LVv*EeGYvU31D{WM=o8B2H$Prl;=j{zS+X5ANCJvL@qi>|F;BTM%4ej z-egSsF$dpk_&j0Z8gRd~|AqW2l{)^R(WH-`vhU?3{HBMG4*+b=V=ZONSU)NoaNzpgP&(` zKJ0QOXS50bsG;9%@nO7Y8{97c3?rt>SX}<~MR4EXl*N`uKFtIdx<7i3C3hB~I+`#y z+oE7jzVL`bcUxmhA7-e$lDux9vuATJ4mwo!woRM*liS6`H8X~Z_Qe@19QUv>jvfg; z;{9Je$J1c)MIMr`;x0DFv)eQb%-Ap{MLum0!%T?#8n8}pE|J!bOnP7^=#k8w-W-13z0JkP;4j??RhXCe8w&NZQoVkke~&#wG~z+9dTDRY}&qr);TE#EUP zM)JT!^gb&rtuF1S3Sgvr$b^^oH!&JF{3n1FO#eJZo_qf@{cQi;^nVeU%ab8rHiXyH zVHuZpFZ>F{f4hla$_e9Vmbmdh2#h@4bY7)Dv^=?k1SHBx{PO$;&_dy*GsgD8HWE&r z--V~xZMubaXU_6)X@3eo_6MD6>eZU?z7>{MNx#7C7m%kLzSV?p6hiZK!@mx^kpA}; z$bWv#N_feaLUA*yTTT2o8^Jhb$v%@Cp4sHKJ%GGw(|&BF!!oY^SSAXEKVanNT}-xd zoZga#rDn&le%Ldn+OGefzHFHyIODXXhUD$fkse^-HKtKL8`0;UqvlBEO6kmZQgd+K zwYmalj5KPa(pTW%cn7Od!-KgWh{H{@u?ExXgrC9M>zB zLSLSbC3;DG_%sHLu8R+IfZU6&jStVKWnG{F?jI9Tvx+v=tahuZ2m=kto;u?+`!{o* zW=ox>t(zG8Ecj1KUqLPfDKsld%4H^sDFR_ju`QnwNa;A9c@F!g&E4KvHJ&t?bnFI- zr$u^zJyYd9SqJGG3rOcgYE#sWAX*iDprbSb*sRlYi$yATpQ{`)M5$RrBPP$r>qH+G z_KpJBj9idZ^lhrl#FpC@DS0@)+D@LK`U47Z_!!{|Bv~%E5AXR^J{wFzgG?$S8!BOY z@b#IJEA6AH%ci7giqJ~i53G|cHF+SV0=tzT2{I4XjZ7{MveChGW zR^l)+${dHiLm{3wz?U)hvwe{FiVky+=PhLc_Xu8C*;razHgobG!IoLrHfL`dG+YWl z`0o4j%@wehGu0HI6PPiDSa&JPr{GlzDW9@Vj{OaeI$Ez^h3Mr|<=$fD%Ky`2>*bz$ z^W|Z+wwipjf8OI1Wi3~CndMQ>@GYP6O?IlGKf@-5z24x<9bAu*lux<0xXI9;@6g8# z&hliNRmxHBU1o#EUg$nnHjVb2gOu$y;B&3s7Kvi2iZlq~C)ljjDrq{wIEg=$|cuvoBwWe!K|& zn<98QDrzAr!MuZQ@487o$4jP zG_j)}JGA~~ z-i5=ld(^O~j2-e5<7oWk9y*Q*(GZ`3pUfRCAPB>9`wsjBKZ_uY@Oj>N<+OdT_V4X zg%juJqsYU3q%=&I&4>5y(ztw%DMEjI5qg;mA|H;CNW6=R@Ui!S*z()^-)uQoIPpq8 z%03)dex7+sct!H~b5BZ3Gjw!-N_W8Kw>fH{#w6k>%zKp`b)LS^;b0WX&AEURe z&kHPij`s-tY6~a*68vc1W8sIvE%ci$ob(*eqUpEr!{HYCYb|`9h0B<_tA|@HdRzY6 z4NiKy{M=E5{_iaMh!yWY7NP&5MQ`($vpwi-%d_9&W0&)96yfu47QM~qnIiPh6`>!q zaJ!uUq6nW~Tl99kZx*31Ek#0M=uLVl&vK8S;L@JRJ$`OJzSH3&<$0ll3!hUh+%9it z8=U-4#!t%Cg%@UiP`U>3zS&r^b>qQ-qJ)D@ZfbJJU$Wx0<-e~8AGyDn@!Is?C_?{l7H*d}Im^=J^PDA*t%os-&xwdz z^gm(I+j=M`V;CuKQV!+5W5I=w+>gg#5&Wc_yvyNpA(_G~ba3In%)y2KSr*Q3V&O0Q zaw(6!mg*uy&qu_fUuW^L^{~OBxAUc=2>mvTK4S6jFGBx5i{9pcLlOF0EZi<{a<3`9 zZ9cbK^me-LG`LIunIiOZZzT0;m$%Os;d5^hJ`WY4f6Sqme1FoxCExi?L_=@RvrE2j zBM3vBV;X|7 z_+MYHtxohR-*ZH;EX0pI-SEpzc$=1XSN=MKxoZ<7{FM#^2`|q^6Ta4=yWPZ3`D`QM zX$7LbjBl9FNfcws^-h#^$*H{z3Qmm>FP}0@k*Z3tE@rz-q&%` zC63(e2o}s!aiq#eDFJJ9q$b%rIsdydnQ>b1T>Wo^l| zoXIF`n1}y=o?~E{*DIBkCHIg02;y$S4>x$B@!CRT>wGubW9v7n@VfpL;;|V1g>b&( z3*jq)7rGzk5`&BU^4w@};Uzfx2;?JeiQpW|B<|x$BlrhkCtjn_JpR`PuXXUf2CsK; zkGH7}Oda7aa>y8#OKwYGc$n9&VbECZ`r(i<_BnNSd5JCU3GKs8Oo?{vKQen1loizd zDMQeTcUP1?uc^A-T8)ylGA}>0!HV_W|V{$aewDnDni5#n$f#dkmQQ zNJDzI??QhqK^XF9o(b;5PD6VBa^{ciL+P_b{>(inPn9#aDaHCwQhy}9q_qwEy@lSP zI|;(<)Q>~%Lp^H3xeqt~VtuH`Ou$Bmfz&5~r4Pk%1=~n?sh{E};pKS_JSdaYKcPEd zEN|6^Iv;dynizC;A+P}LrN9dHp=4i#4}aT;+=BJO!l)*dX}D#dg_|xn{Ch!LDE|+d z{1^QlY9DH&Nx$slU>}O_e2HK97VAUZV>(7(Lm1n*YMl$uLVYN`Z`$*A*@0nq!!M~j zb<)KcZ`Fr7^+>h84J*mA^NFon!;|%KbpHW$W`6ix zjadA38hs|MEy+B>Wp22nVSg~J=I;%WtjEc`l+mZ?5B@e34>v2;S8M=RoN(X2$y>r@ zAWJYuxMUk{ElqYU>FexBpdrT!k`C`%P^8Xwy*r3`Hp%o^E>+-4zH?Lquu|E-&XVkv zRg~&|>ZN+0da2%-UaI%0m+MXLr89d(V!f@cATjkHQI5Oa@1!tcrF>Xb%T1Gq{q-w2IpixRTQbrnr>q;|ff9WB0PIx!$XWmj8uitCsZvARnJt>7;lXor^~0oa=Vxq^9K;aQK5BtsgDY+%{%fto|u5I zY1)$Q2w@Q&6}s38A?>O6C_;cUs9_47j4=7|134W5{r55+aezL2{=9@~a_GTyeQ-j> z;i1yXS+n%{20ZRy#vJSDgn9&?ndgoSKODZx@i9AFj#HQ;$0<~vp-y55+ea_(;Dsvp ztc2z2SOwfJ$Fmm>%lVc!%<8G+=?mKba3+6V&Q~4mkUN(F3#%tZqfccxGV}fr)KjO9 zc{u6=?+qDW;h929d#?IbNSv z;YS+dky9!rj=4kNPD3`w8?e1yb_)R@!(rsbsvO{1*fin&SLz*r=afSnmZS&zmvnV* zTC%0ReF@GKNSwZG5q46gcP`q}lg3cPlFpv?t~AydDnjF!O1P;L%TwN(WJzjQAGU4T zgcvyKYD?ijoo;;Izzn21I+B|gZ{<5%QNxO~VnK<+G+$l~1oNpVePJt+qT=denNHGEv%fZ~4*c=!uHU%BLyI73D`x3so#G zFTJe%$g@DQ(UiSjLk`^tE-IHu<~@Mvk>a zUP3MTWbgr8y{>uTXo=1q&%D(Wn{g_T;;#r)%(?R@^nn&UYq~!z&=C~%;~X1#LBK6r!jsLxIc*w&AJ#(=k(>z!0a;e?H@wk-ooy+m(UM=l|xSJCSL zUZOqSpJ?yxN_Y2YIi?E3Xn34c`*y^`bn^5LVAQHs@pg6Cg~TBporI(q=Z+yHy_rwx z9^3}svDAuNt$ka%&*(%c-%IS6oXbM0S+E!? zeVfBcOi8rQF5#y?6zbtjDLz#Xl5wfxueGlV3rp3Zj>PFcDBt4c_(A!s^dqd&sO!pb z%#9}WzSUl6XI1Ff+0!eyR)%9o$0;8)C48!TFK`#kjl5@Nuq-xHuD)oQ#pT1e2BW3; zNF7^n%JRzz!*bH2bjp{6_AbpyIf)#o`JkE%17|8H8}?B`7QNlH7aB8T=tEg2tm#$n zjn+w~RG+^z38SM_%}h^$fitJ3n&M}NxL@jWAHY;SpyvRC6O|nHdcLeYm)Ev;p|ea* z1c5!KC@;qazzazqErPS7T!@~1^Fp}&a*!J1EKJQ+o`Q#b=w{P>C73qXeE1-4b^utq zWUPznr6Eqa1b;U{7~=eH7JLao81i5*RdDKo#^oPqSmDe~p(x_fIo`TDmiY`~Welu<{rx*+A)yIl)bBvir z_?)1fxpfYL|HR-;<|+79#vOO?uNwCn2Y=PLFL&@?8DZNT{F4UfSP+fG`>zJqBd>Y< zaW-r)yVd_ZZk*mn9bDGmebT{2U!QgGdrZ9dIQUBAe#F5gpPqE^d8Xn&>)=a``vnJ= zby&Y~a9MXI>t97bvX1g_qYuHQ-1`pxkB09e2fxdtmj~L>2%lTD(>ve6Pd4tg4*nO0 zzRkg9k8Y2H%h?lGJ2*S}G&ei=M&p)yAw*6&7yWM>`V)-)W&NnouQGIBbLg+r(Rxz8 zg#IR@pQjx92MqpW2mh*ZA9V1?4gWVC{JjRBY4}OJGWLCxgMY__pYPzO8(h|93ZLT) z-r&%$Gx$aaA2j$j2Y<@QDfLzO%X-*PJM=#??$0^6tUH!_GlY-4DDOA)6oYNC7+KdS z^fQc{KXCZJXxu+_aA}Wz@8B}le6%Sq!vFJzf5hN)nxIOgGaULmji4(mdMQ`_BvKG$tO1!fE zPxwe*_45|JonNxfPvqHPdA@1C-lJUkf3E)comMFP(?8BpbdbiV;oqEXY-R_i+ z5lGOeq|oc#cs=*8*5GFJ2dK@}qx9lT?9w2+26sOD!T!c7g=JcCIrx*QGFDD> z!L`^`yl@@o8>m1NrR#a4yxjD?I>=7rmE%Est>)RV4VON*RHVV637kfFjWsUq&a_s^ zHPd7b@LGN1h8$NgF>5`pf9ACja-Voh-A{6NTdOl60$$9X_2LbF_(s-6-b#K!cHseD zPJLKx;^FZhBIc@iHqCg`v*h0DpO61II17vT(!XF@V(qU=;?qCKA0`pg6VqRB&b*4b zX+;6qXUYi>|gjj?&E${U|fAokcu0>m1iL2FTz$y!BRqANRP|Xb;@{I zH?BoTHbCaon%tzfYJdZM07%DvvP0sGf*{5_a*lX5iO@=t_d$|)=BMLjNN0uNG=7(w ze5V$((>WmI`|GSEZG-EZ!t-6QZiNQBY!h7aJ4%{{J#vJH*xViC! zv3W{z6htT)t1t74JOTv-i#(srl~$;3{6BLPs_1C#9``1-`|I(q#WUM{N!{4LB1*M5 zG{`)UOW}k4Wf969>__pUMTrI>clHO_<&pbOv7l69_s$X@x+q268s1sLRi;=<8Vs+E z)Z3{Xe7z*SjB4kqQCwR8d(9K^kuf4wpRx(jl@?sT6m$Ovk1AOZkq!}thjX3@R zzDaUeH8Gs8^b6+HT*f>LHZ&lL0;@+Z$6+P=)Z!7f0MV=?ncuNw{(y&NCG*>TE+9Gd zp?>u~W5QU24`r;M6zfsfSKKGIf z<+5qgbkQG%@9E7zN@kx7h&Uqa{FFxrsWF^4`aHeYR%_k8q#s7c?sNiZRRCP zHs-`tOT9F@}s_QF@Efa>bVu%(|#k*V9DRIPOn0SLH|4FQmFS5)w|NM zt15Av@z~CHD zw~Zc4E%)?UEw&Hkbh#8iuY4LU0}Mey5;;9MreIhoJkNu%l=gK2-$&`y%*17s0<$1m9l-|7H>Vhehz0i{QU4 zf|sHlETlJ%0T;r#XTK1Bd=Y#paO(e@GNtC+Sv)NUztZ5kKI3Iygb&An$>%Q(y=ptb z@|q&_gGF$!Z&!kAHxpd8fb}s6+nq?XbzmLIWQfS5Di<-M*N;qI6LXHD(HkWV-eFs+F%|mqLCNv>p z!Z{V1NOt$7cI9X_talmkpdG!R!R$Jq1G^Zy`^@QS+}(h6BEnMdRY>ODJKAtskN6tS zJXB{gl!}UTp(o~P)&dDu3S3AJt-iw z$y`-$a*;^BF0oj1tU=~PvqpnWEwu$ClNfb==q^2 zINLcIJ~+lFc)LY^vW4Gb;WZZiDT@!kU4_qHi=Ll3 ze8S?-@j~Hqm>HNNA3J}Kx9}4z`ly93vhXby&UFLA|9uv|#KP}1IK^ULMCd^9wp-)8zRLm+*>4%jmp}HzT;dk^ZJZ~;XUsMGB{C^x_oe=G<~()js5$XJ8($58r{wu4BE?X z2ttpYGjYIpkDo1;Wnx(%mJ`Hsv{*1$ta4#|y0K*Lsev9cdl&Nmtz5>kuWn@DehlUs z^@K4AGOh0MV7N6DWRCnm^YqZ*0St&A`D)owjj5yJ!z)6A4^E3`rhQ=TQK7--&i%LN zW;`)|U>~Q6g6z2*OrAZ;bO!rX*65L5!sc-KHQ_J4E6BX@;E$&V?aze2RI+Cz6%IO| zX<8BXf{BNMx_#q6SK*`gP@bS+Uuqh_SmxBBIa#HM|`3YPh_OoxSa5&Rp*!?%r4%9<(K9DKMQvvIr?Ajp3CPLNL$4%1D+ z(5tEH>`jao8l0wdwlK*2C`=jjjW%pLVJz9Y|2v9$X<2E9Ux1vD2 z)%&8C#xk$R^?XzGl33q9E@kig3R|Y2_s&Eua~C3j(LUgX;HO$ zP}t|(SsikPO{jU3*6g`;PntQgx{=FKexsS!W0_x?G~eq(E}hQ7 z7iz6UHb+oGl=LDRo^}fLcJ9#}9`31Pud#UIs-1!D4)(BQ+hdJ`_I zf1jSBMRqVJF6MmkDoovAhS_9W-7}CT-u}wy_b?X|JFD#3^hu`4qLkTXPpjgJq7c3) z^SNKMtf&@Zd{!`lLigLU^ayle%|D@4i>|$QJz61|cSg3?qNvBSpScyGQNk?U?1|LC ziv{48$P>`|=&k70qb1P|NqFc(2oXa@?PbWTF&h_;Y`u3Y2!rew%2a>^0-z8~*HUeY zK_xqbv%1Q*reDl9VgCAO!SFS#jo-_ZMTAq3-J=5Y3>kfW0n&GWbnt5tw#KE`&q*Ic z2ixk3>%y_p&qeM*{6Xfk>VamBLQkm>H$2Q_sun{xyuZT4of2yw>c9;z@VjPG(qD&? zSUo4wGZonwS4o;m9AxjNw$$|VtEmmp8dcN>f_rzPO&HyZ+=9^81=*p;NQV^BQ>7?7 z#AIN0^14b^f)#`Jsgzvb0OBV|TsnBC;yuXpjGurg@1h%Rz+x3qJW`=sff5}HT9y>+ z-m{}8>SkfOY7)kg!_SVsA2dPc(O|f7A{c!A&G1dNpfPz>q4H|IK8ge_a7@B~&mx5J7V>-ooIuiqYr>#UuS525V%nl|!#$l}B!g z0>2~18-wBHk5pM~e6WdUeiYBVj2Yl#G>ginM{y&3Y~ouhGikhscuD{klRU3tMiLmo z`U1_(Mstz?+e+0V8GQcS;KA`}snWr3?>1LaG={%4p;Lfq=~bM0n3;lnT!H?=*`- zCa}IC3alAo&&DhKE9zZ}ne{!9yAV#jm|1Yaz*nL{BY2;L_smA12lBeg#v}g9>@|@Z zQsQ60MK~*m5aUBAo-N@ocSV+Ojt;&V3g5`%HPumqrg<0Tv7KQ#|1kWcA83x0Ef0nF zd<)VqF9{D_Lood1c;rm{Z;qUE;qbiWu%#mP;kzHqd_A}wdatTxr4v}SQdRe4kQohL+fmEMDNj(3Rp@(!_f#P$mq64* z0bXHEzl- zUlx>J6A3OHE?X84J>N8V&9bstq-jkpw_k2(q~56t&V{AT%c_D>cI-e^dttVDnNM4N zyfhWr7;i{L8h8Bt`0*xekhvzZRTmhjek-{ALp<9RX&wL0%Aps-dpKseGMlbOQ4jAq z3yx^`?hQ5Z&~IDnCSt>7$HgUij9qYvQd`Dkc=6yFKg9TlJqfDB!sOyJW|KgOK{vC<9ojb}4IUpc&NUNn0S zbF>w?86CW?Y*y+FCm)YR)a#?A=grRLVtq7w-aMt~(yqwH26W zu`ngP=O&2RoE@lY!As{lN)x`jsT!HLVt8Qs!;w5A#o zzT|T2hXPi3RsO~+gaDBF$U+msAZ}X-!7G9r9%dV6^@yUE8inMVIefF-@WRb#WwPg5 z?~OeXo`i%B0pjx|8*9X(YG^KFN3Z`idTVIBhR}5ad5|fKq?apstuKqW>_FV*)-t9hHi)Fc0-pThrlxA5&Dqj8dhqL27nliXY9wqr#;Q+@ zWq5!C!a7nV>-~4JsfD`oUr20#Zv=WT$4JmT%lbXld?giTZW5CtYpyAO6)m^ohh}2h zibtF0zsfcYrAgIsyb|j1no|Ud3pZsje|>h!cgCSVEbrs$u13C9gOz+MGAFGCZNsf^ zvMY5V8lTj8vB7J7bZ0@5{-hy_n|BTb$m)ntJR8w;XTB4h@*wL=fOa^RS&7$fOU+mZ zlsxog_=8tc**Y3|RUk)ogV`7~H1%yS!`jR~Hc$baI|u08$x?f|N`NgQ0;YWS%NiAV z^nnUy0UQ4{cnvhwkG+IW*6wQ}JFf`u`3bB{K+Q@7Iw4LiKd;hkw1`)m>XBVzy2m0M zp2!lNK&Yq&afbKs8#NfbPmO7XhdzZ^ueE1(pqe4$&G^DbctawcT zQ6sKo{%n-YLS`C@ocruGh*ibe3eeE^Xhx+J9_)8<1p;|!Up;9G27D#4> zd=;_@FYcilSjR@OT+r3|;C)q5B(NjM_Nz@nJOq+TCk=ci%$b}UzL~iPDTR*lARL9zjnvcwdjbMb16TW;ggu-4dXhDY;`-~uatFLKm@Q8+Qdl(V=pohMtk+~*PJNmH~ zpmlsk){Ndl8(ue~gRr3U!T#}UO0DcxQc3PR0q+B9Y4UJ_dJ|;cG=n2}8SF*+CcO&o z0>Fi`C8?Ph97M)ZJDvr!wt=lazFnS4hVClCSHt6`?8>>Z3z<*9+*CO0z#b)bdmaqH03^-pn<4-QxIIOTFlW){V%* zT)em{SS%~RO_fi`?0Eo?Yp60uvO3@`n|UOKk1jQ^hc87_IwsJ1vLWUu8wS+y3MoU2 zr9|iFHgbhAilPfJ|E&BcSXfTkDDJyQn zdrp9lT+x_WkCZf;SNLvz+|#7va;)2HV=TvK6AUtV=OcMPnF@VpDs(>~9U``WPvjs% zs(!-oX}7`W0Zvg6f1NC0qTwn{M38MAiD_5;EKP8W@`#4#?uk@^0+w4MJ}gR0vCN*x zW1xfL({oT5pfjz#GbZ1oNV+F9UFzxk`=;5Q3HsGiIcV6hH2lEj+zY`Xg4NM_V3|Yb zJAHO%rki2C>{%S)yJ8G1)nez1G32Kg!DCEHjRQn6f?KI%8LTfwgMxPsMgr9rY|K>X z!54xc%ZYlsM&moDZlq&>3t6`E;0sy!{G4|I&1V`JGbvbv&L zMeyLW#yl7D$+4$sG<&TY;j2}HPq|mc=rxFe{fKXo5LE`RtVc8Z!(aOM;K5&1J@`^J z#ui>3`!qaFqSLd)^3HKg*2K|ILCwSfy=oXnA5pDb`kC=g`Yog%2vVq^i%^)MkBdd6 z)h@woO;9TY$4uqEMR8B5JVLs?F>ZB2M`9tG#2PdekLQtK#Q$EIUjYW7%~z%sYNv>+zI|(Z_zK z^X%^xr%#iNSywgmOu7s~@u_}*Y^vi89g*to349U`wd?q+!Eae4b&M(!kf+v5of&IL z_w87Q|2ucQb9^CvP6m_OpyA+-cd~vT9V>0A4;mgFz%GoJMz2HDO0~U4*#;l382`&? z_NE$epV-Gi(W5yeH#5EfZTZ;gDDu(42RJ@D^ZFN}gV$mQ(4N0z=P`e=^VrAWHd(`p zrgupo>zF8-*x-F5e5Q9O2I)@8tKrs;Aaex01krVS$GQ1Tie)#{Le=%L(D!2v-{1Ze z#weMFdr{Krp2WCT>OR!?8L`Y?kAThi5y9ZTit(q|0GtT7YLdn`81ATrPpi1bFN|g9 zVVv+bhOtv}ERu^C6e5T%_~LGQV(g`2bfqfR;h}FMq1ZeYTX389!?e%%SJ(^2gx8cK z(F5xfm(A+MnKLpw%0>^WvEJ0tW&?|ouw(GdlhT;9C}G{dT))#eL!@@PallrU_)H7u zr_MD2g9fFz^x>x3$~z1kP?D4%JGPl$sIU)Q57=-i!!S3SFBa4>99`OoqKHXEUwN}* zxAG5(csv-X>vENtc&`sWFapR!TW{VTH@wga?ie&j?Xa8pJu;bv;<+%$-oZ-4;q=Up z?rlVX(J%fOWlg3w*ijlhK#DTR`%x8iijr@5m@(uZ!O#o8Z zvY#Je1e`XxB~p#XNKMPcv!7=v39_G6wL0_2=y4E?4Zs%=8Kz31zP%t=dCIRjtH5xo z5&95(vunz@Gpv@)rO*0XgYGYeqG11?QZMQvcEG6@7~a_(*?NPrZ#}ja9O_;)mlUTk zSorK{3!+BX3ZLq^OP2K{$mj2b2ywKR^{DOPJ>Q|kgKtiRhxRLcP$~E7GBp{(GFHbN?ma^obMSOdk_0(0nq_WU^Kllf2!{CwujBxn^}tx z6aGhU_{{_e!$XhaKQrXZ03{g80R~G{k4NqW*SBM*F0jUSG?e?W(oS<9HizgdnmO+B zbNdc(Q;^5LPK8*z8l7ve#c5Hz1;eLID)>>AOGP{~dvYq8Rl-bC*1hedfR9d!5~4tQ za(&qG&4-$*x8!Y7gA%CamMg%kRGT)2^rG-RtX---%btr@+RXd8_DuOI5U;uLo(v#1 z&T7xFHRh)~nAk38bua_d!3=Z{CjXkmSN?ABp@AO@)FeLw#_tI{Rciq{)>Cblt%pi1 zPP_eDRbI@!s}YA>JIGebOqpiCJc8hpTSa)+jhMO3zR?9gWP_LP%QtrDo0RpjCvhd3 zIIc#(&;!P>Sg>)-^*nq^>-G&HKm_F$ZsNw@A>})J7W#Yj(TVR0fM7r77{Ox@rBFo$ zoU5q0#J=e!w*7|~EO`2m5_`@MRAR3(&3Z1S8>88Eg_4Ros8s(}NkyCgQh*QK`t)=~ zKOR;Or1NJwZw@WB`AI-MI*x)7&pyD`X;Mz^1df+-_NK?+gA7IIjcryb^UA+|b~+}# zx$Dl2Wk1euPqy=XQ;(kZeWi$oSoS7`!a?DoY} z&*<--VZc-c-e_b0S;i3O;|s5=tR9wD6r6fpPJVt5dRLek$G~#%;DM^(!RM-i;~xsz zRUzU89LVsV;J+D9n2xfZVlWerL)UK}3PE zt>EueJM}^@me5GGwWZp2SO+Wct-ciZBu#9;4ces|xEU6vK&!`_N}u6p2`Qc$`K8fZ)E3R&Z9I3gQSW6`YDAyX@xCxo`bob5!eyOUS(B_3DDG(pR*?B~mE} z=iD?ht9>PUnpQsR)@82W1=WVtWgYKy?S7SK0es^rYwKEE_LbqBB5zUq!K|%C*edOK zEB|0A9xdkM;(#Y%1w0igSWtK{Ya9DIo-GeYUH9+lN^3hhsO?DBqRu5VQ)Xqx7tYGU zH6SW6YuJW@S%b5-eG2QicTw6IA9y0xS&VlMafbpcsl{0jE*TzdmS#P;X?W_%y~QW@ zAG?KlhsS4kSMciO;up{z_a6TJ7gFK_I}ZKsMSAy*;*=kB6=27_&QW+jS0B za0M}}xMP3!h0J?epS;NEDumi~<4XME6d1-w)&-@vY#N?Y(((J^wxd~lev#hxlR=$J z_NEl4ya36vLOiT(#0T!q?pVsPU*m+Rq~lkm9j_eQ2V+fdpkHH{^3|`WIk&UPIu5lR zNy%z|4f5JzH)TOd^9|BSgEwDyxEiPJ*jxCopIw+-FWc(V0=I%B|dr>=7(W8W6anz%3&!*TTg5p z7-UQN6|67*d(jdzgS#FnQOqQI6m|ERmyt^#R99c$Xxm=djvaE#}Hf6|0wk!)|xl5M2 zWyu+kX&L4Mn>62c3@(>snQMix&WV=kK(Oc&i%IpA%l@b=^JJ-ZmB@D3qE&XNKN@7a zMV7u@0{_MS%lI??%MpbSsA6wf;Q22@_SdT4WEmBlGaS>QV3G8nZ2rv`t)lV0`cM6= z^^bab>SX(W{U?s_7XMxT)ArHgwf|)cko$M;e>zT=i#>in9Ib|X%fHlr4|)0g`m28) z@Z@w;_v^NwpYN73sT^-yG3m0vn?HiRf4jdeqH4J;|5E?8ioDt#m2K6xNw#;(QWt*x zHUBOZ@Rqt?VL!j(f3Ib$6uexP>Ob{^_A}X?T(ruX)-NjCn(u%87dYgv_1`Y>tH1F3 z(N14LwzYk9x1Ue(zt>XzQ!WN9lcn~<}X_WRJKs!Uzg_3ojWs{d)W#Y*Bs5ypO`-}Z$f^v+sxlQId5X# z)Num4a?nbFSlg_*fK2K4ZdU6i>qE%n~af|5QYX~@qq^tzGjFT$7$5itzJ=@Q+ua#y3scm)LEm6(J|VIbDm1O235)LF#B02=}v#-7>wzzM{UEgbP``F&YS8$ z)|9l@LVV6LzAL&WVx8sJq72sIXM*2H+||95bw^dVrKjGZwGH}rwtKD;@cd9bir;9# zT>o|TnxAZrdbzAEa&l`K)Jt#TZFT z+Zi%OSaeuoxBai=V&szv%f+b2O4`#O=i*r+uW}rlOq1}Vo9W&i@Y0i!1yW^(m}{4N`xp6zz^l zkmo~s;wuIRhA2nLi-O-N_z|h!F9nZh2J-HBfz^@lFZFyv9H#b66#Vj>K;F%pSS9Bb z{Zkac#v|;9J^qp1Zjsi9nKRfM9v5x6&FO z$)+m=Kiz^@XJlQL%lX}=;9O∋rjV)gnLBf>^H?oa4!+Cc)=e5bG_1bN;evv)~t7 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 literal 0 HcmV?d00001 diff --git a/src/ddprof_process.cc b/src/ddprof_process.cc index dad321947..0305c7ba5 100644 --- a/src/ddprof_process.cc +++ b/src/ddprof_process.cc @@ -27,9 +27,11 @@ Process::~Process() { } } -austin_handle_t Process::get_austin_handle() { +austin_handle_t Process::get_austin_handle(pid_t tid) { if (!_austin_handle) { + LG_NTC("Attaching to PID %d/%d", _pid, tid); _austin_handle = austin_attach(_pid); + LG_NTC("Result of attach %p", _austin_handle); } return _austin_handle; } diff --git a/src/ddprof_worker.cc b/src/ddprof_worker.cc index da10aca4f..cfb4e76e6 100644 --- a/src/ddprof_worker.cc +++ b/src/ddprof_worker.cc @@ -234,7 +234,7 @@ static DDRes ddprof_unwind_sample(DDProfContext &ctx, perf_event_sample *sample, ddprof_stats_add(STATS_UNWIND_AVG_STACK_SIZE, sample->size_stack, nullptr); // copy the sample context into the unwind structure - unwind_init_sample(us, sample->regs, sample->pid, sample->size_stack, + unwind_init_sample(us, sample->regs, sample->pid, sample->tid, sample->size_stack, sample->data_stack); // If a sample has a PID, it has a TID. Include it for downstream labels diff --git a/src/pprof/ddprof_pprof.cc b/src/pprof/ddprof_pprof.cc index 796d48aec..60d28ff73 100644 --- a/src/pprof/ddprof_pprof.cc +++ b/src/pprof/ddprof_pprof.cc @@ -268,8 +268,9 @@ DDRes pprof_aggregate(const UnwindOutput *uw_output, ddog_prof_Profile_AddResult add_res = ddog_prof_Profile_add(profile, sample); if (add_res.tag == DDOG_PROF_PROFILE_ADD_RESULT_ERR) { defer { ddog_Error_drop(&add_res.err); }; - DDRES_RETURN_ERROR_LOG(DD_WHAT_PPROF, "Unable to add profile: %s", - add_res.err.message.ptr); + LG_NTC("Unable to add profile - continue execution."); +// DDRES_RETURN_ERROR_LOG(DD_WHAT_PPROF, "Unable to add profile: %s", +// add_res.err.message.ptr); } return ddres_init(); } diff --git a/src/unwind.cc b/src/unwind.cc index 5adcd2fae..8bcca9143 100644 --- a/src/unwind.cc +++ b/src/unwind.cc @@ -44,13 +44,14 @@ static void add_container_id(UnwindState *us) { } void unwind_init_sample(UnwindState *us, uint64_t *sample_regs, - pid_t sample_pid, uint64_t sample_size_stack, + pid_t sample_pid, pid_t sample_tid, uint64_t sample_size_stack, char *sample_data_stack) { us->output.clear(); memcpy(&us->initial_regs.regs[0], sample_regs, K_NB_REGS_UNWIND * sizeof(uint64_t)); us->current_ip = us->initial_regs.regs[REGNAME(PC)]; us->pid = sample_pid; + us->tid = sample_tid; us->stack_sz = sample_size_stack; us->stack = sample_data_stack; } diff --git a/src/unwind_dwfl.cc b/src/unwind_dwfl.cc index bb5f28182..52fe382ef 100644 --- a/src/unwind_dwfl.cc +++ b/src/unwind_dwfl.cc @@ -406,10 +406,14 @@ static DDRes add_python_frame(UnwindState *us, SymbolIdx_t symbol_idx, ElfAddress_t pc, Dwfl_Frame *dwfl_frame) { SymbolHdr &unwind_symbol_hdr = us->symbol_hdr; SymbolTable &symbol_table = unwind_symbol_hdr._symbol_table; - Process &p = us->process_hdr.get_process(us->pid); - austin_handle_t handle = p.get_austin_handle(); - if (handle && (symbol_table.at(symbol_idx)._is_python_frame)) { + if (symbol_table.at(symbol_idx)._is_python_frame) { + // only attempt to attach on python frames + Process &p = us->process_hdr.get_process(us->pid); + austin_handle_t handle = p.get_austin_handle(us->tid); + if (!handle) { + return ddres_error(1); + } AustinSymbolLookup &austin_symbol_lookup = unwind_symbol_hdr._austin_symbol_lookup; // The register we are interested in is RSI, but it doesn't seem to be diff --git a/test/allocation_tracker-ut.cc b/test/allocation_tracker-ut.cc index 6bca2f83e..94a5bd556 100644 --- a/test/allocation_tracker-ut.cc +++ b/test/allocation_tracker-ut.cc @@ -75,7 +75,7 @@ TEST(allocation_tracker, start_stop) { ASSERT_EQ(sample->addr, 0xdeadbeef); UnwindState state; - ddprof::unwind_init_sample(&state, sample->regs, sample->pid, + ddprof::unwind_init_sample(&state, sample->regs, sample->pid, sample->tid, sample->size_stack, sample->data_stack); ddprof::unwindstate__unwind(&state); diff --git a/test/savecontext-ut.cc b/test/savecontext-ut.cc index 354586b19..be3c77705 100644 --- a/test/savecontext-ut.cc +++ b/test/savecontext-ut.cc @@ -29,7 +29,7 @@ void funcB() { uint64_t regs[K_NB_REGS_UNWIND]; size_t stack_size = save_context(retrieve_stack_bounds(), regs, stack); - ddprof::unwind_init_sample(&state, regs, getpid(), stack_size, + ddprof::unwind_init_sample(&state, regs, getpid(), 0, stack_size, reinterpret_cast(stack)); ddprof::unwindstate__unwind(&state); @@ -98,7 +98,7 @@ TEST(getcontext, unwind_from_sighandler) { t.join(); UnwindState state; - ddprof::unwind_init_sample(&state, regs, getpid(), stack_size, + ddprof::unwind_init_sample(&state, regs, getpid(), 0, stack_size, reinterpret_cast(stack)); ddprof::unwindstate__unwind(&state); From 156abae290d5573099cd8344a5515f4bcaa7a474 Mon Sep 17 00:00:00 2001 From: r1viollet Date: Fri, 1 Sep 2023 15:18:05 +0200 Subject: [PATCH 8/9] Adjust the libaustin install process --- CMakeLists.txt | 6 ++++-- app/base-env/Dockerfile | 4 ++-- tools/launch_local_build.sh | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81643969b..ffeb95c10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,9 +265,11 @@ target_link_libraries( target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt) # add libaustin -add_library(austin SHARED IMPORTED) +find_library(austin_location NAMES libaustin.a) +message(STATUS "libaustin location at:" ${austin_location}) +add_library(austin STATIC IMPORTED) set_target_properties( - austin PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib_debug/libaustin.a" + austin PROPERTIES IMPORTED_LOCATION ${austin_location} INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libaustin") set(LIBDD_PROFILING_EMBEDDED_OBJECT "${CMAKE_BINARY_DIR}/libdd_profiling-embedded.o") diff --git a/app/base-env/Dockerfile b/app/base-env/Dockerfile index 9715735b4..d51490f20 100644 --- a/app/base-env/Dockerfile +++ b/app/base-env/Dockerfile @@ -186,10 +186,10 @@ RUN apt-get update \ # possibly useful command # CFLAGS="-g -O0 -DTRACE" ./configure # Valid commit : git checkout a7a292b3f5a1058051b017c0d339a678efdec704 -RUN git clone --branch r1viollet/libaustin_v2 https://github.com/r1viollet/austin.git && \ +RUN git clone --branch libaustin https://github.com/P403n1x87/austin.git && \ cd austin && \ autoreconf --install && \ - CFLAGS="-g -O0 -DTRACE" ./configure && \ + CFLAGS="-O2" ./configure && \ make && \ make install diff --git a/tools/launch_local_build.sh b/tools/launch_local_build.sh index 8e7a02546..f5a9f2b4a 100755 --- a/tools/launch_local_build.sh +++ b/tools/launch_local_build.sh @@ -126,7 +126,7 @@ if [ $PERFORM_CLEAN -eq 1 ]; then echo "Clean image : ${DOCKER_NAME}" # if docker image does not exist, we should not fail docker image rm "${DOCKER_NAME}" || true - CACHE_OPTION="--no-cache" + #CACHE_OPTION="--no-cache" fi # Check if base image exists From bc67e196b55572b0d5af35b16fdfa3b40c09e299 Mon Sep 17 00:00:00 2001 From: r1viollet Date: Wed, 6 Sep 2023 10:56:51 +0200 Subject: [PATCH 9/9] ddprof python support Adjust the log to be more comprehensive --- src/ddprof_process.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ddprof_process.cc b/src/ddprof_process.cc index 0305c7ba5..411508a9b 100644 --- a/src/ddprof_process.cc +++ b/src/ddprof_process.cc @@ -31,7 +31,7 @@ austin_handle_t Process::get_austin_handle(pid_t tid) { if (!_austin_handle) { LG_NTC("Attaching to PID %d/%d", _pid, tid); _austin_handle = austin_attach(_pid); - LG_NTC("Result of attach %p", _austin_handle); + LG_NTC("Result of attach %s", _austin_handle?"Success":"Failure"); } return _austin_handle; }