pax_global_header00006660000000000000000000000064150406144370014516gustar00rootroot0000000000000052 comment=3db8da80111171c219ab5839905771386bee06b3 cpptrace-1.0.4/000077500000000000000000000000001504061443700133215ustar00rootroot00000000000000cpptrace-1.0.4/.bazeliskrc000066400000000000000000000000301504061443700154440ustar00rootroot00000000000000USE_BAZEL_VERSION=7.2.1 cpptrace-1.0.4/.bazelrc000066400000000000000000000000711504061443700147420ustar00rootroot00000000000000test --strip=never test --test_output=all test --copt=-g cpptrace-1.0.4/.github/000077500000000000000000000000001504061443700146615ustar00rootroot00000000000000cpptrace-1.0.4/.github/FUNDING.yml000066400000000000000000000000261504061443700164740ustar00rootroot00000000000000github: jeremy-rifkin cpptrace-1.0.4/.github/workflows/000077500000000000000000000000001504061443700167165ustar00rootroot00000000000000cpptrace-1.0.4/.github/workflows/ci.yml000066400000000000000000000575411504061443700200500ustar00rootroot00000000000000name: ci on: push: pull_request: jobs: test-linux: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: compiler: [gcc, clang] shared: [--shared, ""] steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev ninja-build pip3 install colorama - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config test-linux-arm: runs-on: ubuntu-22.04-arm strategy: fail-fast: false matrix: compiler: [gcc, clang] shared: [--shared, ""] steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev ninja-build pip3 install colorama - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config test-macos: runs-on: macos-14 strategy: fail-fast: false matrix: compiler: [gcc, clang] shared: [--shared, ""] steps: - uses: actions/checkout@v4 - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: dependencies run: | python3 -m venv env env/bin/pip install colorama - name: build and test run: | env/bin/python ci/test-all-configs.py --${{matrix.compiler}} --default-config test-windows: runs-on: windows-2022 strategy: fail-fast: false matrix: compiler: [msvc, clang, gcc] shared: [--shared, ""] steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: dependencies run: | pip3 install colorama - name: libdwarf run: | if("${{matrix.compiler}}" -eq "gcc") { cd .. cpptrace/ci/setup-prerequisites-mingw.ps1 } - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config test-windows-old: runs-on: windows-2022 strategy: fail-fast: false matrix: compiler: [msvc] shared: [--shared, ""] steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 with: toolset: 14.29 # vc 2019 - name: dependencies run: | pip3 install colorama - name: libdwarf run: | if("${{matrix.compiler}}" -eq "gcc") { cd .. cpptrace/ci/setup-prerequisites-mingw.ps1 } - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config test-linux-all-configurations: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: compiler: [gcc, clang] shared: [--shared, ""] needs: test-linux steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev ninja-build pip3 install colorama - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} test-macos-all-configurations: runs-on: macos-14 strategy: fail-fast: false matrix: compiler: [gcc, clang] shared: [--shared, ""] needs: test-macos steps: - uses: actions/checkout@v4 - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: dependencies run: | python3 -m venv env env/bin/pip install colorama - name: build and test run: | env/bin/python ci/test-all-configs.py --${{matrix.compiler}} test-windows-all-configurations: runs-on: windows-2022 strategy: fail-fast: false matrix: compiler: [msvc, clang, gcc] shared: [--shared, ""] needs: test-windows steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: dependencies run: | pip3 install colorama - name: libdwarf run: | if("${{matrix.compiler}}" -eq "gcc") { cd .. cpptrace/ci/setup-prerequisites-mingw.ps1 } - name: build and test run: | python3 ci/test-all-configs.py --${{matrix.compiler}} build-linux-all-remaining-configurations: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: compiler: [gcc, clang] needs: test-linux-all-configurations steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev ninja-build pip3 install colorama - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: build run: | python3 ci/build-in-all-remaining-configs.py --${{matrix.compiler}} build-macos-all-remaining-configurations: runs-on: macos-14 strategy: fail-fast: false matrix: compiler: [gcc, clang] needs: test-macos-all-configurations steps: - uses: actions/checkout@v4 - name: dependencies run: | brew install ninja python3 -m venv env env/bin/pip install colorama - name: libdwarf run: | cd .. cpptrace/ci/setup-prerequisites.sh - name: build run: | env/bin/python ci/build-in-all-remaining-configs.py --${{matrix.compiler}} build-windows-all-remaining-configurations: runs-on: windows-2022 strategy: fail-fast: false matrix: compiler: [msvc, clang, gcc] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: dependencies run: | pip3 install colorama - name: libdwarf run: | if("${{matrix.compiler}}" -eq "gcc") { cd .. cpptrace/ci/setup-prerequisites-mingw.ps1 } - name: build run: | python3 ci/build-in-all-remaining-configs.py --${{matrix.compiler}} performancetest-linux: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: compiler: [g++-11, clang++-14] config: [ -DSPEEDTEST_DWARF4=On, -DSPEEDTEST_DWARF5=On ] needs: test-linux-all-configurations steps: - uses: actions/checkout@v4 - name: dependencies run: sudo apt install gcc-11 g++-11 libgcc-11-dev ninja-build - name: build run: | mkdir -p build cd build cmake .. -GNinja -DCMAKE_CXX_COMPILER=${{matrix.compiler}} -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=foo ninja ninja install mkdir -p ../test/speedtest/build cd ../test/speedtest/build cmake .. \ -GNinja \ -DCMAKE_BUILD_TYPE=Debug \ ${{matrix.config}} ninja - name: speedtest working-directory: test/speedtest/build run: | ./speedtest | python3 ../../../ci/speedtest.py ${{matrix.compiler}} ${{matrix.config}} test-linux-fetchcontent: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-linux-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | tag=$(git rev-parse --abbrev-ref HEAD) echo $tag cd .. cp -rv cpptrace/test/fetchcontent-integration . mkdir fetchcontent-integration/build cd fetchcontent-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG=$tag -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On ninja ./main test-linux-findpackage: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-linux-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | tag=$(git rev-parse --abbrev-ref HEAD) mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On sudo ninja install cd ../.. cp -rv cpptrace/test/findpackage-integration . mkdir findpackage-integration/build cd findpackage-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug ninja ./main test-linux-add_subdirectory: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-linux-all-configurations steps: - uses: actions/checkout@v4 - name: build run: | cd .. cp -rv cpptrace/test/add_subdirectory-integration . cp -rv cpptrace add_subdirectory-integration mkdir add_subdirectory-integration/build cd add_subdirectory-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On ninja ./main test-macos-fetchcontent: runs-on: macos-14 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-macos-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | tag=$(git rev-parse --abbrev-ref HEAD) echo $tag cd .. cp -rv cpptrace/test/fetchcontent-integration . mkdir fetchcontent-integration/build cd fetchcontent-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG=$tag -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On ninja ./main test-macos-findpackage: runs-on: macos-14 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-macos-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | tag=$(git rev-parse --abbrev-ref HEAD) echo $tag mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On sudo ninja install cd ../.. cp -rv cpptrace/test/findpackage-integration . mkdir findpackage-integration/build cd findpackage-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug ninja ./main test-macos-add_subdirectory: runs-on: macos-14 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-macos-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | cd .. cp -rv cpptrace/test/add_subdirectory-integration . cp -rv cpptrace add_subdirectory-integration mkdir add_subdirectory-integration/build cd add_subdirectory-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On ninja ./main test-mingw-fetchcontent: runs-on: windows-2022 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | $tag=$(git rev-parse --abbrev-ref HEAD) echo $tag cd .. cp -Recurse cpptrace/test/fetchcontent-integration . mkdir fetchcontent-integration/build cd fetchcontent-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG="$tag" -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On ninja .\main.exe test-mingw-findpackage: runs-on: windows-2022 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | $tag=$(git rev-parse --abbrev-ref HEAD) echo $tag mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCMAKE_INSTALL_PREFIX=C:/foo -DCPPTRACE_WERROR_BUILD=On ninja install cd ../.. mv cpptrace/test/findpackage-integration . mkdir findpackage-integration/build cd findpackage-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=C:/foo ninja ./main test-mingw-add_subdirectory: runs-on: windows-2022 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: test run: | cd .. cp -Recurse cpptrace/test/add_subdirectory-integration . cp -Recurse cpptrace add_subdirectory-integration mkdir add_subdirectory-integration/build cd add_subdirectory-integration/build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On ninja .\main.exe test-windows-fetchcontent: runs-on: windows-2022 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: test run: | $tag=$(git rev-parse --abbrev-ref HEAD) echo $tag cd .. cp -Recurse cpptrace/test/fetchcontent-integration . mkdir fetchcontent-integration/build cd fetchcontent-integration/build cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG="$tag" -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On msbuild demo_project.sln .\Debug\main.exe test-windows-findpackage: runs-on: windows-2022 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: test run: | $tag=$(git rev-parse --abbrev-ref HEAD) echo $tag mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCMAKE_INSTALL_PREFIX=C:/foo -DCPPTRACE_WERROR_BUILD=On msbuild cpptrace.sln msbuild INSTALL.vcxproj cd ../.. mv cpptrace/test/findpackage-integration . mkdir findpackage-integration/build cd findpackage-integration/build cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=C:/foo msbuild demo_project.sln .\Debug\main.exe test-windows-add_subdirectory: runs-on: windows-2022 strategy: fail-fast: false matrix: shared: [On, Off] needs: test-windows-all-configurations steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: test run: | cd .. cp -Recurse cpptrace/test/add_subdirectory-integration . cp -Recurse cpptrace add_subdirectory-integration mkdir add_subdirectory-integration/build cd add_subdirectory-integration/build cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On msbuild demo_project.sln .\Debug\main.exe unittest-linux: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: compiler: [g++-10, clang++-18] stdlib: [libstdc++, libc++] dwarf_version: [4, 5] split_dwarf: [OFF, ON] exclude: - compiler: g++-10 stdlib: libc++ steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install gcc-10 g++-10 libgcc-10-dev ninja-build libc++-dev ninja-build cd .. cpptrace/ci/setup-prerequisites-unittest.sh - name: build and test run: | python3 ci/unittest.py \ --slice=compiler:${{matrix.compiler}} \ --slice=stdlib:${{matrix.stdlib}} \ --slice=dwarf_version:${{matrix.dwarf_version}} \ --slice=split_dwarf:${{matrix.split_dwarf}} unittest-linux-bazel: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install -y libtool libncurses5 - name: test dbg run: | bazel test //... -c dbg - name: test opt run: | bazel test //... -c opt unittest-linux-arm: runs-on: ubuntu-24.04-arm strategy: fail-fast: false matrix: compiler: [g++-10, clang++-18] stdlib: [libstdc++, libc++] dwarf_version: [4, 5] split_dwarf: [OFF, ON] exclude: - compiler: g++-10 stdlib: libc++ steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install gcc-10 g++-10 libgcc-10-dev ninja-build libc++-dev ninja-build cd .. cpptrace/ci/setup-prerequisites-unittest.sh - name: build and test run: | python3 ci/unittest.py \ --slice=compiler:${{matrix.compiler}} \ --slice=stdlib:${{matrix.stdlib}} \ --slice=dwarf_version:${{matrix.dwarf_version}} \ --slice=split_dwarf:${{matrix.split_dwarf}} unittest-macos: runs-on: macos-14 steps: - uses: actions/checkout@v4 - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: "15.4" - name: dependencies run: | brew install ninja python3 -m venv env env/bin/pip install colorama cd .. cpptrace/ci/setup-prerequisites-unittest-macos.sh - name: build and test run: | env/bin/python ci/unittest.py unittest-windows: runs-on: windows-2022 strategy: fail-fast: false matrix: compiler: [cl, clang++] shared: [OFF] # TODO: Re-enable shared build_type: [Debug, RelWithDebInfo] steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: build and test run: | mkdir build cd build cmake .. ` -DCMAKE_CXX_COMPILER=${{matrix.compiler}} ` -DCMAKE_C_COMPILER=${{matrix.compiler == 'clang++' && 'clang' || matrix.compiler}} ` -DBUILD_SHARED_LIBS=${{matrix.shared}} ` -DCPPTRACE_WERROR_BUILD=On ` -DCPPTRACE_BUILD_TESTING=On cmake --build . --config ${{matrix.build_type}} ./${{matrix.build_type}}/unittest # TODO: Macos, mingw unittest-linux-modules: runs-on: ubuntu-24.04 needs: unittest-linux strategy: fail-fast: false steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt install ninja-build clang-19 clang-tools-19 - name: build and test run: | mkdir build cd build cmake .. \ -GNinja \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_COMPILER=clang++-19 \ -DCMAKE_C_COMPILER=clang-19 \ -DCMAKE_CXX_STANDARD=20 \ -DCPPTRACE_WERROR_BUILD=On \ -DCPPTRACE_STD_FORMAT=Off \ -DCPPTRACE_BUILD_TESTING=On ninja ./unittest_module unittest-linux-32-bit: runs-on: ubuntu-24.04 needs: unittest-linux strategy: fail-fast: false matrix: compiler: [g++-10, clang++-18] steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt update sudo apt install gcc-10 g++-10 libgcc-10-dev clang-19 clang-tools-19 ninja-build gcc-multilib g++-multilib g++-10-multilib - name: build and test run: | mkdir build cd build cmake .. \ -GNinja \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ -DCMAKE_C_COMPILER=${{matrix.compiler == 'clang++-18' && 'clang' || 'gcc'}} \ -DCPPTRACE_WERROR_BUILD=On \ -DCPPTRACE_STD_FORMAT=Off \ -DCPPTRACE_BUILD_TESTING=On \ -DCMAKE_C_FLAGS=-m32 \ -DCMAKE_CXX_FLAGS=-m32 ninja ./unittest unittest-linux-gcc-4-8-5: runs-on: ubuntu-24.04 needs: unittest-linux steps: - uses: actions/checkout@v4 - name: dependencies run: | sudo apt update wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/g++-4.8_4.8.5-4ubuntu8_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/libstdc++-4.8-dev_4.8.5-4ubuntu8_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/gcc-4.8-base_4.8.5-4ubuntu8_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/gcc-4.8_4.8.5-4ubuntu8_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/libgcc-4.8-dev_4.8.5-4ubuntu8_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/cpp-4.8_4.8.5-4ubuntu8_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gcc-4.8/libasan0_4.8.5-4ubuntu8_amd64.deb sudo apt install ./gcc-4.8_4.8.5-4ubuntu8_amd64.deb ./gcc-4.8-base_4.8.5-4ubuntu8_amd64.deb ./libstdc++-4.8-dev_4.8.5-4ubuntu8_amd64.deb ./cpp-4.8_4.8.5-4ubuntu8_amd64.deb ./libgcc-4.8-dev_4.8.5-4ubuntu8_amd64.deb ./libasan0_4.8.5-4ubuntu8_amd64.deb ./g++-4.8_4.8.5-4ubuntu8_amd64.deb - name: build and test run: | mkdir build cd build cmake .. \ -GNinja \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_COMPILER=g++-4.8 \ -DCMAKE_C_COMPILER=gcc-4.8 \ -DCPPTRACE_WERROR_BUILD=On \ -DCPPTRACE_STD_FORMAT=Off ninja unittest-windows-32-bit: runs-on: windows-2022 needs: unittest-windows strategy: fail-fast: false matrix: build_type: [Debug] steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: build and test run: | mkdir build cd build cmake .. ` -A Win32 ` -DCPPTRACE_WERROR_BUILD=On ` -DCPPTRACE_BUILD_TESTING=On cmake --build . --config ${{matrix.build_type}} ./${{matrix.build_type}}/unittest unittest-windows-clangcl: runs-on: windows-2022 needs: unittest-windows strategy: fail-fast: false matrix: build_type: [Debug] arch: [x64, Win32] steps: - uses: actions/checkout@v4 - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 - name: build and test run: | mkdir build cd build cmake .. ` -T ClangCL ` -A ${{matrix.arch}} ` -DCPPTRACE_WERROR_BUILD=On ` -DCPPTRACE_BUILD_TESTING=On cmake --build . --config ${{matrix.build_type}} ./${{matrix.build_type}}/unittest cpptrace-1.0.4/.github/workflows/sonarlint.yml000066400000000000000000000015721504061443700214570ustar00rootroot00000000000000on: # Trigger analysis when pushing in master or pull requests, and when creating # a pull request. push: branches: - main - dev name: sonarlint jobs: sonarcloud: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: # Disabling shallow clone is recommended for improving relevancy of reporting fetch-depth: 0 - name: Install sonar-scanner and build-wrapper uses: sonarsource/sonarcloud-github-c-cpp@v2 - name: Run build-wrapper run: | mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_BUILD_TESTING=On -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCMAKE_CXX_STANDARD=11 ninja cd .. - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: sonar-scanner cpptrace-1.0.4/.gitignore000066400000000000000000000001651504061443700153130ustar00rootroot00000000000000.vscode/ .idea/ .cache a.out build*/ out/ repro*/ __pycache__/ scratch tmp/ bazel-*/ cmake-build-*/ test/jank/data cpptrace-1.0.4/BUILD.bazel000066400000000000000000000013121504061443700151740ustar00rootroot00000000000000cc_library( name = "cpptrace", srcs = glob([ "src/**/*.hpp", "src/**/*.cpp", ]), local_defines = [ "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", "CPPTRACE_DEMANGLE_WITH_CXXABI", "CPPTRACE_UNWIND_WITH_LIBUNWIND" ], hdrs = glob([ "include/cpptrace/*.hpp", "include/ctrace/*.h", ]), includes = [ "include", "src" ], deps = [ "@libdwarf//:libdwarf", "@libunwind//:libunwind" ], copts = [ "-Wall", "-Wextra", "-Werror=return-type", "-Wundef", "-Wuninitialized", "-fPIC", "-std=c++11" ], visibility = ["//visibility:public"], ) cpptrace-1.0.4/CHANGELOG.md000066400000000000000000000604721504061443700151430ustar00rootroot00000000000000# Changelog - [Changelog](#changelog) - [v1.0.4](#v104) - [v1.0.3](#v103) - [v1.0.2](#v102) - [v1.0.1](#v101) - [v1.0.0](#v100) - [v0.8.3](#v083) - [v0.8.2](#v082) - [v0.8.1](#v081) - [v0.8.0](#v080) - [v0.7.5](#v075) - [v0.7.4](#v074) - [v0.7.3](#v073) - [v0.7.2](#v072) - [v0.7.1](#v071) - [v0.7.0](#v070) - [v0.6.3](#v063) - [v0.6.2](#v062) - [v0.6.1](#v061) - [v0.6.0](#v060) - [v0.5.4](#v054) - [v0.5.3](#v053) - [v0.5.2](#v052) - [v0.5.1](#v051) - [v0.5.0](#v050) - [v0.4.1](#v041) - [v0.4.0](#v040) - [v0.3.1](#v031) - [v0.3.0](#v030) - [v0.2.1](#v021) - [v0.2.0](#v020) - [v0.1.1](#v011) - [v0.1](#v01) # v1.0.4 Added - Added filtering for C++ exception handling internals during printing. This cleans up output from current exceptions and std::terminate traces. Fixed: - Fixed reported version number for cpptrace Other: - Bumped default libdwarf to 2.1.0 - Updated stacktrace output for the cpptrace terminate handler - Updated demo program # v1.0.3 Added: - Added line indicator to make source code snippets in case colors aren't being used - Added column indicator to source code snippets - Added 32-bit ARM support for StackWalk64 https://github.com/jeremy-rifkin/cpptrace/pull/271 (@Xottab-DUTY) # v1.0.2 Added: - Added `break_before_filename` formatting option https://github.com/jeremy-rifkin/cpptrace/issues/259 (@codeinred) Fixes: - Fixed 32-bit clang-cl build - Fixed build on gcc 4.8.5 - Fixed StackWalk64 for 64-bit arm https://github.com/jeremy-rifkin/cpptrace/pull/270 (@mcourteaux) - Fixed compatibility issue with cmake versions before 3.23 Other: - Added a couple notes to the README # v1.0.1 Added: - Added from-current-exception utility for SEH on windows (`CPPTRACE_SEH_TRY`/`CPPTRACE_SEH_EXCEPT`) Fixes: - Fixed a static assert without a message causing issues on some C++11 builds - Fixed build 32-bit build on linux - Fixed from-current-exception system for 32-bit windows where SEH behaves much differently - Fixed clang-cl build # v1.0.0 Major changes: - Overhauled how the from-current-exception system and `CPPTRACE_TRY`/`CPPTRACE_CATCH` macros work. They now check the thrown exception type against the type the catch accepts to decide whether or not to collect a trace. This eliminates the need for The `TRYZ`/`CATCHZ` variants of the macros as now the normal macro is equally zero-overhead. As such, the `Z` variants have been removed. Breaking changes: - `CPPTRACE_TRYZ` and `CPPTRACE_CATCHZ` have been removed, change uses to `CPPTRACE_TRY`/`CPPTRACE_CATCH` - `CPPTRACE_TRY`/`CPPTRACE_CATCH` macros no longer support multiple handlers and `CPPTRACE_CATCH_ALT` has been removed. Instead, use `cpptrace::try_catch`. > **Details** > > Author's note: I apologize for this non-trivial breaking change, however, it allows for a much improved > implementation of the from-current-exception system. The impact should be [very minimal](https://github.com/search?type=code&q=%2F%5CbCPPTRACE_CATCH_ALT%5Cb%2F+language%3Ac%2B%2B+-is%3Afork+-repo%3Ajeremy-rifkin%2Fcpptrace+-path%3Afrom_current.hpp) > for most codebases. > > Transitioning to `cpptrace::try_catch` is straightforward: > > > > > > > > > > > > > >
> Before > > Now >
> ```cpp > CPPTRACE_TRY { > foo(); > } CPPTRACE_CATCH(const std::logic_error& e) { > handle_logic_error(e); > } CPPTRACE_CATCH_ALT(const std::exception& e) { > handle_exception(e); > } CPPTRACE_CATCH_ALT(...) { > handle_unknown_exception(); > } > ``` > > ```cpp > cpptrace::try_catch( > [&] { // try block > foo(); > }, > [&] (const std::runtime_error& e) { > handle_logic_error(e); > }, > [&] (const std::exception& e) { > handle_exception(e); > }, > [&] () { // `catch(...)` > handle_unknown_exception(); > } > ); > ``` >
> > Please note as well that code such as the following, which was valid before, will still compile now but may report a > misleading trace. The second catch handler will work but it cpptrace won't know about the handler's type and won't > collect a trace that doesn't match the first handler's type, `std::logic_error`. > > ```cpp > CPPTRACE_TRY { > foo(); > } CPPTRACE_CATCH(const std::logic_error& e) { > ... > } catch(const std::exception& e) { > ... > } > ``` Potentially-breaking changes: - This version of cpptrace reworks the public interface to use an inline ABI versioning namespace. All symbols in the public interface are now secretly in the `cpptrace::v1` namespace. This is an ABI break, but any ABI mismatch will result in linker errors instead of silent bugs. This change is an effort to allow future evolution of cpptrace in a way that respects ABI. - This version fixes a problem with returns from `CPPTRACE_TRY` blocks on windows. Unfortunately, this macro has to use an IILE on windows and as such `return` statements won't properly return from the enclosing function. This was a footgun and now `return` statements in `CPPTRACE_TRY` blocks are prevented from compiling on windows. Added - Added `cpptrace::try_catch` for handling multiple alternatives with access to current exception traces - Added `cpptrace::rethrow` utility for rethrowing exceptions from `CPPTRACE_CATCH` while preserving the stacktrace https://github.com/jeremy-rifkin/cpptrace/issues/214 - Added utilities for getting the current trace from the last rethrow point (`cpptrace::raw_trace_from_current_exception_rethrow`, `cpptrace::from_current_exception_rethrow`, `cpptrace::current_exception_was_rethrown`) - Added a logger system to allow cpptrace to report errors in a configurable manner. By default, cpptrace doesn't log anything. The following functions can be used to change this: (`cpptrace::set_log_level`, `cpptrace::set_log_callback`, and `cpptrace::use_default_stderr_logger`) - Added `cpptrace::basename` utility - Added `cpptrace::prettify_type` utility - Added `cpptrace::prune_symbol` utility - Added formatter option for symbol formatting (`cpptrace::formatter::symbols`) - Added `cpptrace::detail::lazy_trace_holder::is_resolved` - Added support for C++20 modules https://github.com/jeremy-rifkin/cpptrace/pull/248 - Added `cpptrace::load_symbols_for_file` to support DLLs loaded at runtime when using dbghelp https://github.com/jeremy-rifkin/cpptrace/pull/247 Removed - Removed `CPPTRACE_TRYZ` and `CPPTRACE_CATCHZ` macros - Removed the `CPPTRACE_CATCH_ALT` macro Fixed - Fixed a problem where `CPPTRACE_TRY` blocks could contain `return` statements but not return on windows due to an IILE. This is now an error. https://github.com/jeremy-rifkin/cpptrace/issues/245 - Fixed cases where cpptrace could print to stderr on internal errors without the user desiring so https://github.com/jeremy-rifkin/cpptrace/issues/234 - Fixed a couple internal locking mistakes - Fixed a couple of code paths that could be susceptible to static init order issues - Fixed bug with loading elf symbol tables that contain zero-sized entries - Fixed an incorrect assertion regarding looking up symbols at program counters that reside before any seen subprogram DIE https://github.com/jeremy-rifkin/cpptrace/issues/250 - Fixed issue with `cpptrace::stacktrace::to_string()` ending with a newline on empty traces Other - Marked some paths in `CPPTRACE_CATCH` and `CPPTRACE_CATCHZ` as unreachable to improve usability in cases where the compiler may warn about missing returns. - Improved resilience to libdwarf errors https://github.com/jeremy-rifkin/cpptrace/pull/251 - Removed internal use of `std::is_trivial` which is deprecated in C++26 https://github.com/jeremy-rifkin/cpptrace/issues/236 - Bumped libdwarf to 2.0.0 - Added `--address` flag for internal symbol table tool - Various internal work to improve the codebase and reduce complexity # v0.8.3 Added: - Added basic JIT support https://github.com/jeremy-rifkin/cpptrace/issues/226 - Added `cpptrace::formatter::transform` https://github.com/jeremy-rifkin/cpptrace/issues/227 - Added support for gcc 4.8.5 https://github.com/jeremy-rifkin/cpptrace/issues/220 Fixed: - Fixed bug related to calling `dwarf_dealloc` on strings from `dwarf_formstring` and `dwarf_diename` https://github.com/davea42/libdwarf-code/issues/279 - Fixed incorrect cmake version variable https://github.com/jeremy-rifkin/cpptrace/issues/231 - Fixed `address_mode::none` not working https://github.com/jeremy-rifkin/cpptrace/issues/221 - Fixed use of `-Wall` for clang-cl Other: - Added ARM CI - Miscellaneous work on supporting old compilers - Updated cpptrace cmake target configuration to not add public compile definitions - Internal refactoring, cleanup, and code improvements # v0.8.2 Fixed: - Fixed printing of internal error messages when an object file can't be loaded, mainly affecting MacOS https://github.com/jeremy-rifkin/cpptrace/issues/217 Other: - Bumped zstd via FetchContent to 1.5.7 # v0.8.1 Fixed: - Fixed compile error on msvc https://github.com/jeremy-rifkin/cpptrace/issues/215 Added: - Added `cpptrace::can_get_safe_object_frame()` Breaking changes: - Renamed ctrace's `can_signal_safe_unwind` to `ctrace_can_signal_safe_unwind`. This was an oversight. Apologies for including a breaking change in a patch release. Github code search suggests this API isn't used in public code, at least. Other: - Added CI workflow to test on old msvc - Made some internal improvements on robustness and cleanliness # v0.8.0 Added: - Added support for resolving symbols from elf and mach-o symbol tables, allowing function names to be resolved even in a build that doesn't include debug information https://github.com/jeremy-rifkin/cpptrace/issues/201 - Added a configurable stack trace formatter https://github.com/jeremy-rifkin/cpptrace/issues/164 - Added configuration options for the libdwarf back-end that can be used to lower memory usage on memory-constrained systems https://github.com/jeremy-rifkin/cpptrace/issues/193 - Added `cpptrace::nullable::null_value` - Made `cpptrace::nullable` member functions conditionally `constexpr` where possible Fixed: - Fixed handling of `SymInitialize` when other code has already called `SymInitialize`. `SymInitialize` must only be called once per handle and cpptrace now attempts to duplicate the current process handle to avoid conflicts. https://github.com/jeremy-rifkin/cpptrace/issues/204 - Fixed a couple of locking edge cases surrounding dbghelp functions - Fixed improper deallocation of `dwarf_errmsg` in the libdwarf back-end Breaking changes: - `cpptrace::get_snippet` previously included a newline at the end but it now does not. This also affects the behavior of trace formatting with snippets enabled. Other: - Significantly improved memory usage and performance of the libdwarf back-end - Improved implementation and organization of internal utility types, such as `optional` and `Result` - Improved trace printing and formatting implementation - Added unit tests for library internal utilities - Added logic to the cxxabi demangler to ensure external names begin with `_Z` or `__Z` before attempting to demangle - Added various internal tools and abstractions to improve maintainability and clarity - Various internal improvements for robustness - Added a small handful of utility tool programs that are useful for continued development, maintenance, and debugging - Improved library CI setup - Marked the `CPPTRACE_BUILD_BENCHMARK` option as advanced # v0.7.5 Fixed: - Fixed missing `` include https://github.com/jeremy-rifkin/cpptrace/pull/202 - Added `__cdecl` to a terminate handler to appease MSVC under some configurations https://github.com/jeremy-rifkin/cpptrace/issues/197 - Set C++ standard for cmake support checks https://github.com/jeremy-rifkin/cpptrace/issues/200 - Changed hyphens to underscores for cmake component names due to cpack issue https://github.com/jeremy-rifkin/cpptrace/issues/203 # v0.7.4 Added: - Added `` header with version macros Fixes: - Bumped libdwarf to 0.11.0 which fixes a number of dwarf 5 debug fission issues Other: - Various improvements to internal testing setup # v0.7.3 Fixed: - Fixed missing include affecting macos https://github.com/jeremy-rifkin/cpptrace/pull/183 - Fixed issue with cmake not using the ccache program found by `find_program` https://github.com/jeremy-rifkin/cpptrace/pull/184 - Fixed missing include and warnings affecting mingw https://github.com/jeremy-rifkin/cpptrace/pull/186 - Fixed issue with identifying inlined call frames when the `DW_TAG_inlined_subroutine` is under a `DW_TAG_lexical_block` - Fixed a typo in the README - Improved unittest support on various configurations - Improved unittest robustness under LTO - Fixed bug signal_demo in the event `fork()` fails Added: - Added color overload for `stacktrace_frame::to_string` - Added CMake `export()` definition for cpptrace as well as a definition for libdwarf which currently doesn't provide one Changed: - Updated documentation surrounding the signal safe API # v0.7.2 Changes: - Better support for older CMake with using `FetchContent_Declare` from a URL https://github.com/jeremy-rifkin/cpptrace/pull/176 - Better portability for page size detection https://github.com/jeremy-rifkin/cpptrace/pull/177 - Improved compile times https://github.com/jeremy-rifkin/cpptrace/pull/172 - Split up `cpptrace.hpp` into finer-grained headers for lower compile time impact - Some minor readme restructuring # v0.7.1 Added - Better support for finding libunwind on macos https://github.com/jeremy-rifkin/cpptrace/pull/162 - Support for libbacktrace under mingw https://github.com/jeremy-rifkin/cpptrace/pull/166 Fixed - Computation of object address for safe object frames https://github.com/jeremy-rifkin/cpptrace/issues/169 - Nested microfmt in cpptrace's namespace due to an ODR problem with libassert https://github.com/jeremy-rifkin/libassert/issues/103 - Compilation on iOS https://github.com/jeremy-rifkin/cpptrace/pull/167 - Compilation on old MSVC https://github.com/jeremy-rifkin/cpptrace/pull/165 - Dbghelp use on 32 bit https://github.com/jeremy-rifkin/cpptrace/issues/170 - Warning in brand new cmake due to `FetchContent_Populate` being deprecated https://github.com/jeremy-rifkin/cpptrace/issues/171 Other changes - Bumped the buffer size for execinfo and CaptureStackBackTrace to 400 frames - Switched to execinfo.h for unwinding on clang/apple clang on macos due to `_Unwind` not working with `-fno-exceptions` https://github.com/jeremy-rifkin/cpptrace/issues/161 # v0.7.0 Added - Added `cpptrace::from_current_exception()` and associated exception handler macros to allow tracing of all exceptions, even without cpptrace traced exception objects. Fixes: - Fixed issue with using `resolve_safe_object_frame` on `safe_object_frame`s with empty paths - Fixed handling of dwarf 4 rangelist base addresses when a `DW_AT_low_pc` is not present - Fixed use of `-g` with MSVC Other changes: - Bazel is now supported on linux (https://github.com/jeremy-rifkin/cpptrace/pull/153) - More work on testing - Some internal refactoring # v0.6.3 Added: - Added a flag to disable inclusion of `` by cpptrace.hpp and the definition of formatter specializations Fixes: - Fixed use after free during cleanup of split dwarf information https://github.com/jeremy-rifkin/cpptrace/issues/141 - Fixed an issue with TCO by clang on arm interfering with unwinding skip counts for internal methods - Fixed issue with incorrect object addresses being reported on macos when debug maps are used - Fixed issue with handling of split dwarf emitted by clang under dwarf4 mode Other changes: - Added note about signal-safe tracing requiring `_dl_find_object` to documentation and fixed errors in the signal-safe tracing docs - Added more configurations to unittest ci setup - Optimized unittest ci matrix setup - Added options for zstd and libdwarf sources if FetchContent is being used to bring the dependencies in - Optimized includes in cpptrace.hpp # v0.6.2 Fixes: - Fix an issue with unwinding to collect stack traces during exception creation on arm https://github.com/jeremy-rifkin/cpptrace/issues/134 - Fix issue where `dladdr1` wasn't being used even when detected Robustness: - Setup more robust unit tests and added them to CI # v0.6.1 Fixes: - Fix for detection of `dladdr1` and `_dl_find_object` support # v0.6.0 New: - Added a `cpptrace::system_error` utility - Added support for musl https://github.com/jeremy-rifkin/cpptrace/issues/128 - Added support for split dwarf / debug fission Fixes: - Fixed address formatting in stack traces - Fixed frame pointer calculation for signal frames from libunwind https://github.com/jeremy-rifkin/cpptrace/issues/123 - Fixed dwarf_ranges handling of lowpc == pc causing erroneous symbol resolution - Fixed implementation of the exception helper system/reference implementation's `lazy_trace_holder` # v0.5.4 Fixes: - Fixed bug with resolving object information when `dladdr` is used and an unexpected `argv[0]` is provided to the binary. # v0.5.3 Fixes: - Fixed bug with formatting of hex values on MSVC - Fixed error handling for libbacktrace back-end when debug info is not present - Fixed bug with cmake resolution of zstd when no zstd cmake config file is installed Other changes: - Added error handling for an edge case in the signal tracing demo - Updated conan recipe to allow libunwind to be chosen - Improved msvc support in internal formatting system - Bumped libdwarf to 0.9.2 # v0.5.2 Fixes: - Fixed bug with resolution of inlined calls Other changes: - Improved internal string formatting - Improved internal error handling # v0.5.1 Fixes: - Fix MSVC warning treated as error for 32-bit windows - Fix MSVC issue with min/max macros - Fix potential null dereference issue identified by eyalgolan1337 # v0.5.0 New: - Traces with source code snippets with `cpptrace::stacktrace::print_with_snippets` - Added `cpptrace::get_snippet` utility - Added `cpptrace::can_signal_safe_unwind` utility - Added `stacktrace_frame::get_object_info` Changes: - The library is now compiled with position-independent code by default Fixes: - Fixed issue with `_dl_find_object` implementation Misc: - Various refactoring, cleanup, and improvements # v0.4.1 Changes: - Renamed `stacktrace_frame.address` -> `stacktrace_frame.raw_address` - Added `stacktrace_frame.object_address` - Fixed segfault due to an edge case with dwarf file table indices - For the libdwarf back-end: At least show object frame information if resolution fails - Extremely small performance improvements - Small documentation updates - Small fix for conan - Updated cmake to not FetchContent zstd when using CPPTRACE_USE_EXTERNAL_LIBDWARF - CI improvements - Test the default configuration first before doing the exhaustive and slow matrix of all configurations. - Cleanup of duplicated prerequisite installation code - Cleanup of built and test python scripts # v0.4.0 What's new: - Cpptrace now has a C API! 🎉 - Cpptrace is now able to parse macOS debug maps and resolve stack traces without dSYM files Most notable improvements: - Updated cpptrace exception objects to generate traces at the callsite for improved consistency with trace output. As part of this cpptrace exception objects have had their constructors updated. - Improved dwarf back-end robustness - Fallback to the compilation-unit cache or walking compilation-units if aranges lookup fails - Eliminated reliance on a CMake-generated export header - Added a configuration to control resolution of inlined function calls - Made architecture selection in Mach-O universal binaries Other improvements: - Improved documentation for installation and usage - Generally improved README content and organization - Fixed an MSVC workaround producing dozens of warnings - Better handle compiler color diagnostic arguments if compiler families differ - Improvements for handling libdwarf's header placement - Fixed issue with libunwind resolution - `-Werror` is now used in CI - More library configurations are now tested in CI - Updated to libdwarf 9 - Updated the library's CMake to acquire zstd through FetchContent - Fixed minor issue with stacktrace printing always trying to enable virtual terminal processing, even when not actually printing to the terminal. # v0.3.1 Tiny patch: - Fix `CPPTRACE_EXPORT` annotations - Add workaround for [msvc bug][msvc bug] affecting msvc 19.38. [msvc bug]: https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565 # v0.3.0 Interface Changes: - Overhauled the API for traced `cpptrace::exception` objects - Added `cpptrace::isatty` utility - Added specialized `std::terminate` handler and `cpptrace::register_terminate_handler` utility - Added `cpptrace::frame_ptr` as an alias for the appropriate type capable of representing an instruction pointer - Added signal-safe tracing support and a guide for [how to trace safely](signal-safe-tracing.md) - Added `cpptrace::nullable` utility for better indicating when line / column information is not present - Added `CPPTRACE_FORCE_NO_INLINE` utility macro to cpptrace.hpp - Added `CPPTRACE_WRAP` and `CPPTRACE_WRAP_BLOCK` utilities to catch non-`cpptrace::exception`s and rethrow wrapped in a traced exception. - Updated `cpptrace::stacktrace::to_string` to take a `bool color` parameter - Eliminated uses of `std::uint_least32_t` in favor of other types - Updated `object_frame` data member names Other changes: - Added object resolution with `_dl_find_object` which is much faster than `dladdr` - Added column support for dwarf - Added inlined call resolution - Added `cpptrace::stacktrace_frame::is_inline` - Added libunwind as a back-end - Unbundled libdwarf - Increased hard max frame count, used by some back-end requiring fixed buffers, from 100 to 200 - Improved libgcc unwind backend - Improved trace output when information is missing - Added a lookup table for faster dwarf line information lookup News: - The library is now on conan and vcpkg Minor changes: - Assorted bug fixes - Various code quality improvements - CI improvements - Documentation improvements - CMake improvements - Internal refactoring # v0.2.1 Patches: - Fixed uintptr_t implicit conversion issue for msvc - Better handling for PIC and static linkage in CMake - Added gcc 5 support - Various warning fixes - Added stackwalk64 support for 32-bit x86 mingw/clang and architecture detection - Added check for stackwalk64 support and CaptureStackBacktrace as a fallback - Various cmake cleanup and changes to use cpptrace through package managers - Added sonarlint and implemented some sonarlint fixes # v0.2.0 Key changes: - Added libdwarf as a back-end so cpptrace doesn't have to rely on addr2line or libbacktrace - Overhauled library's public-facing interface to make the library more useful - Added `raw_trace` interface - Added `object_trace` interface - Added `stacktrace` interface - Updated `generate_trace` to return a `stacktrace` rather than a vector of frames - Added `generate_trace` counterparts for raw and object traces - Added `generate_trace` overloads with max_depth - Added interface for internal demangling utility - Added cache mode configuration - Added option to absorb internal trace exceptions (by default it absorbs) - Added `cpptrace::exception`, which automatically generates and stores a stacktrace when thrown - Added `exception_with_message` - Added traced analogs for stdexcept errors: `logic_error`, `domain_error`, `invalid_argument`, `length_error`, `out_of_range`, `runtime_error`, `range_error`, `overflow_error`, and `underflow_error`. Other changes: - Bundled libdwarf with cpptrace so the library can essentially be self-contained and not have to rely on libraries that might not already be on a system - Added StackWalk64 as an unwinding back-end on windows - Added system for multiple symbol back-ends to be used, mainly for more complete stack traces on mingw - Fixed sporadic line number reporting errors due to not adjusting the program counter from the unwinder - Improved addr2line/atos invocation back-end on macos - Lots of error handling improvements - Performance improvements - Updated default back-ends for most systems - Removed full tracing backends - Cleaned up library cmake - Lots of internal cleanup and refactoring - Improved library usage instructions in README # v0.1.1 Fixed: - Handle errors when object files don't exist or can't be opened for reading - Handle paths with spaces when using addr2line on windows # v0.1 Initial release of the library 🎉 cpptrace-1.0.4/CMakeLists.txt000066400000000000000000000471021504061443700160650ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...4.0) include(cmake/PreventInSourceBuilds.cmake) # ---- Initialize Project ---- # Used to support find_package set(package_name "cpptrace") project( cpptrace VERSION 1.0.4 DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer " HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace" LANGUAGES C CXX ) # Don't change include order, OptionVariables checks if project is top level include(cmake/ProjectIsTopLevel.cmake) include(cmake/OptionVariables.cmake) include(GNUInstallDirs) include(CheckCXXSourceCompiles) include(CheckCXXCompilerFlag) if(PROJECT_IS_TOP_LEVEL) find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) endif() endif() if(PROJECT_IS_TOP_LEVEL) if(CMAKE_GENERATOR STREQUAL "Ninja") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-fdiagnostics-color=always HAS_CXX_FDIAGNOSTICS_COLOR) if(HAS_CXX_FDIAGNOSTICS_COLOR) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") endif() if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") include(CheckCCompilerFlag) check_c_compiler_flag(-fdiagnostics-color=always HAS_C_FDIAGNOSTICS_COLOR) if(HAS_C_FDIAGNOSTICS_COLOR) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always") endif() elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") endif() endif() endif() if(CPPTRACE_SANITIZER_BUILD) add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) endif() if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") # quotes used over <> because of a macro substitution issue where # # is expanded to # string(CONCAT CPPTRACE_BACKTRACE_PATH "\"" ${CPPTRACE_BACKTRACE_PATH}) string(CONCAT CPPTRACE_BACKTRACE_PATH ${CPPTRACE_BACKTRACE_PATH} "\"") #message(STATUS ${CPPTRACE_BACKTRACE_PATH}) string(CONCAT CPPTRACE_BACKTRACE_PATH_DEFINITION "-DCPPTRACE_BACKTRACE_PATH=" ${CPPTRACE_BACKTRACE_PATH}) #message(STATUS ${CPPTRACE_BACKTRACE_PATH_DEFINITION}) else() set(CPPTRACE_BACKTRACE_PATH_DEFINITION "") endif() # ========================================== Platform Support and Auto-config ========================================== include(cmake/Autoconfig.cmake) # =================================================== Library Setup ==================================================== if(NOT CPPTRACE_BUILD_NO_SYMBOLS) set( debug $<$>:-g> $<$:/DEBUG> ) else() add_compile_options($<$>:-g0>) set( debug ) endif() # Target that we can modify (can't modify ALIAS targets) # Target name should not be the same as ${PROJECT_NAME}, causes add_subdirectory issues set(target_name "cpptrace-lib") add_library(${target_name} ${build_type}) # Alias to cause error at configuration time instead of link time if target is missing add_library(cpptrace::cpptrace ALIAS ${target_name}) # Add /include files to target # This is solely for IDE benefit, doesn't affect building target_sources( ${target_name} PRIVATE include/cpptrace/cpptrace.hpp include/ctrace/ctrace.h ) # add /src files to target target_sources( ${target_name} PRIVATE # src src/binary/elf.cpp src/binary/mach-o.cpp src/binary/module_base.cpp src/binary/object.cpp src/binary/pe.cpp src/binary/safe_dl.cpp src/cpptrace.cpp src/ctrace.cpp src/exceptions.cpp src/from_current.cpp src/formatting.cpp src/logging.cpp src/options.cpp src/utils.cpp src/prune_symbol.cpp src/demangle/demangle_with_cxxabi.cpp src/demangle/demangle_with_nothing.cpp src/demangle/demangle_with_winapi.cpp src/jit/jit_objects.cpp src/snippets/snippet.cpp src/symbols/dwarf/debug_map_resolver.cpp src/symbols/dwarf/dwarf_options.cpp src/symbols/dwarf/dwarf_resolver.cpp src/symbols/symbols_core.cpp src/symbols/symbols_with_addr2line.cpp src/symbols/symbols_with_dbghelp.cpp src/symbols/symbols_with_dl.cpp src/symbols/symbols_with_libbacktrace.cpp src/symbols/symbols_with_libdwarf.cpp src/symbols/symbols_with_nothing.cpp src/unwind/unwind_with_dbghelp.cpp src/unwind/unwind_with_execinfo.cpp src/unwind/unwind_with_libunwind.cpp src/unwind/unwind_with_nothing.cpp src/unwind/unwind_with_unwind.cpp src/unwind/unwind_with_winapi.cpp src/utils/io/file.cpp src/utils/io/memory_file_view.cpp src/utils/error.cpp src/utils/microfmt.cpp src/utils/replace_all.cpp src/utils/string_view.cpp src/utils/utils.cpp src/platform/dbghelp_utils.cpp src/platform/memory_mapping.cpp ) if(HAS_CXX20_MODULES AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.23") target_sources( ${target_name} PUBLIC FILE_SET CXX_MODULES FILES "src/cpptrace.cppm" BASE_DIRS "src" ) endif() target_include_directories( ${target_name} PUBLIC $ ) target_include_directories( ${target_name} PRIVATE src ) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND "${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC") set(IS_CLANG_CL 1) else() set(IS_CLANG_CL 0) endif() set( warning_options $<$,$>>:-Wall -Wextra -Werror=return-type -Wundef> $<$:-Wuseless-cast -Wmaybe-uninitialized> $<$:/W4 /permissive-> ) if(CPPTRACE_WERROR_BUILD) set( warning_options ${warning_options} $<$>:-Werror -Wpedantic> $<$:/WX> ) endif() target_compile_options( ${target_name} PRIVATE ${warning_options} ) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) # https://godbolt.org/z/qYh89E6rq target_compile_options(${target_name} PRIVATE -Wno-missing-field-initializers) endif() set(CPPTRACE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPPTRACE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPPTRACE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) configure_file("${PROJECT_SOURCE_DIR}/cmake/in/version-hpp.in" "${PROJECT_BINARY_DIR}/include/cpptrace/version.hpp") # ---- Generate Build Info Headers ---- if(build_type STREQUAL "STATIC") target_compile_definitions(${target_name} PUBLIC CPPTRACE_STATIC_DEFINE) set(CPPTRACE_STATIC_DEFINE TRUE) endif() # ---- Library Properties ---- # Hide all symbols by default # Use SameMajorVersion versioning for shared library runtime linker lookup set_target_properties( ${target_name} PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}" EXPORT_NAME "cpptrace" OUTPUT_NAME "cpptrace" POSITION_INDEPENDENT_CODE ${CPPTRACE_POSITION_INDEPENDENT_CODE} ) # Header files generated by CMake target_include_directories( ${target_name} SYSTEM PUBLIC "$" ) # Header files from /include target_include_directories( ${target_name} ${warning_guard} PUBLIC "$" ) # Require C++11 support target_compile_features( ${target_name} PRIVATE cxx_std_11 ) target_compile_definitions(${target_name} PRIVATE NOMINMAX) if(HAS_ATTRIBUTE_PACKED) target_compile_definitions(${target_name} PRIVATE HAS_ATTRIBUTE_PACKED) endif() if(NOT CPPTRACE_STD_FORMAT) target_compile_definitions(${target_name} PUBLIC CPPTRACE_NO_STD_FORMAT) endif() if(CPPTRACE_UNPREFIXED_TRY_CATCH) target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNPREFIXED_TRY_CATCH) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") SET(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") SET(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") endif() # =================================================== Back-end setup =================================================== if(HAS_CXX_EXCEPTION_TYPE) target_compile_definitions(${target_name} PRIVATE CPPTRACE_HAS_CXX_EXCEPTION_TYPE) endif() if(HAS_DL_FIND_OBJECT) target_compile_definitions(${target_name} PRIVATE CPPTRACE_HAS_DL_FIND_OBJECT) endif() if(HAS_DLADDR1) target_compile_definitions(${target_name} PRIVATE CPPTRACE_HAS_DLADDR1) endif() if(HAS_MACH_VM) target_compile_definitions(${target_name} PRIVATE CPPTRACE_HAS_MACH_VM) endif() # Symbols if(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) if(NOT HAS_BACKTRACE) if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly.") else() message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.") endif() endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) target_link_libraries(${target_name} PRIVATE backtrace ${CMAKE_DL_LIBS}) endif() if(CPPTRACE_GET_SYMBOLS_WITH_LIBDL) target_compile_definitions(${target_name} PRIVATE CPPTRACE_GET_SYMBOLS_WITH_LIBDL) target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS}) endif() if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) # set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.") # option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF) if(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH) target_compile_definitions(${target_name} PRIVATE CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH) else() if("${CPPTRACE_ADDR2LINE_PATH}" STREQUAL "") if(APPLE) find_program(CPPTRACE_ADDR2LINE_PATH_FINAL atos PATHS ENV PATH REQUIRED) else() find_program(CPPTRACE_ADDR2LINE_PATH_FINAL addr2line PATHS ENV PATH REQUIRED) endif() else() set(CPPTRACE_ADDR2LINE_PATH_FINAL "${CPPTRACE_ADDR2LINE_PATH}") endif() message(STATUS "Cpptrace: Using ${CPPTRACE_ADDR2LINE_PATH_FINAL} for addr2line path") target_compile_definitions(${target_name} PRIVATE CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}") endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) if(UNIX) target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS}) endif() endif() if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) target_compile_definitions(${target_name} PRIVATE CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) if(CPPTRACE_USE_EXTERNAL_LIBDWARF) if(NOT CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG) find_package(libdwarf REQUIRED) else() find_package(PkgConfig) pkg_check_modules(LIBDWARF REQUIRED libdwarf) endif() else() include(FetchContent) # First, dependencies: Zstd and zlib (currently relying on system zlib) if(CPPTRACE_USE_EXTERNAL_ZSTD) find_package(zstd) else() cmake_policy(SET CMP0074 NEW) set(ZSTD_BUILD_PROGRAMS OFF) set(ZSTD_BUILD_CONTRIB OFF) set(ZSTD_BUILD_TESTS OFF) set(ZSTD_BUILD_STATIC ON) set(ZSTD_BUILD_SHARED OFF) set(ZSTD_LEGACY_SUPPORT OFF) FetchContent_Declare( zstd SOURCE_SUBDIR build/cmake DOWNLOAD_EXTRACT_TIMESTAMP TRUE URL "${CPPTRACE_ZSTD_URL}" ) FetchContent_MakeAvailable(zstd) endif() # Libdwarf itself set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(PIC_ALWAYS TRUE) set(BUILD_DWARFDUMP FALSE) FetchContent_Declare( libdwarf GIT_REPOSITORY ${CPPTRACE_LIBDWARF_REPO} GIT_TAG ${CPPTRACE_LIBDWARF_TAG} GIT_SHALLOW ${CPPTRACE_LIBDWARF_SHALLOW} ) FetchContent_MakeAvailable(libdwarf) target_include_directories( dwarf PRIVATE ${zstd_SOURCE_DIR}/lib ) if(CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF) export( TARGETS dwarf NAMESPACE libdwarf:: FILE "${PROJECT_BINARY_DIR}/libdwarf-targets.cmake" ) endif() endif() if(CPPTRACE_CONAN) set(dwarf_lib libdwarf::libdwarf) target_link_libraries(${target_name} PRIVATE libdwarf::libdwarf) elseif(CPPTRACE_VCPKG) set(dwarf_lib libdwarf::dwarf) target_link_libraries(${target_name} PRIVATE libdwarf::dwarf) elseif(CPPTRACE_USE_EXTERNAL_LIBDWARF) if(DEFINED LIBDWARF_LIBRARIES) set(dwarf_lib ${LIBDWARF_LIBRARIES}) target_link_libraries(${target_name} PRIVATE ${LIBDWARF_LIBRARIES}) else() # if LIBDWARF_LIBRARIES wasn't set by find_package, try looking for libdwarf::dwarf-static, # libdwarf::dwarf-shared, libdwarf::dwarf, then libdwarf # libdwarf v0.8.0 installs with the target libdwarf::dwarf somehow, despite creating libdwarf::dwarf-static or # libdwarf::dwarf-shared under fetchcontent if(TARGET libdwarf::dwarf-static) set(LIBDWARF_LIBRARIES libdwarf::dwarf-static) elseif(TARGET libdwarf::dwarf-shared) set(LIBDWARF_LIBRARIES libdwarf::dwarf-shared) elseif(TARGET libdwarf::dwarf) set(LIBDWARF_LIBRARIES libdwarf::dwarf) elseif(TARGET libdwarf) set(LIBDWARF_LIBRARIES libdwarf) else() message(FATAL_ERROR "Couldn't find libdwarf target name to link against") endif() set(dwarf_lib ${LIBDWARF_LIBRARIES}) target_link_libraries(${target_name} PRIVATE ${LIBDWARF_LIBRARIES}) endif() # There seems to be no consistency at all about where libdwarf decides to place its headers........ Figure out if # it's libdwarf/libdwarf.h and libdwarf/dwarf.h or just libdwarf.h and dwarf.h include(CheckIncludeFileCXX) # libdwarf's cmake doesn't properly set variables to indicate where its libraries live if(NOT CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG) get_target_property(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES) else() target_include_directories(${target_name} PRIVATE ${LIBDWARF_INCLUDE_DIRS}) endif() set(CMAKE_REQUIRED_INCLUDES ${LIBDWARF_INCLUDE_DIRS}) CHECK_INCLUDE_FILE_CXX("libdwarf/libdwarf.h" LIBDWARF_IS_NESTED) CHECK_INCLUDE_FILE_CXX("libdwarf.h" LIBDWARF_IS_NOT_NESTED) # check_include_file("libdwarf/libdwarf.h" LIBDWARF_IS_NESTED) # check_support(LIBDWARF_IS_NESTED nested_libdwarf_include.cpp "" "" "") if(${LIBDWARF_IS_NESTED}) target_compile_definitions(${target_name} PRIVATE CPPTRACE_USE_NESTED_LIBDWARF_HEADER_PATH) elseif(NOT LIBDWARF_IS_NOT_NESTED) message(FATAL_ERROR "Couldn't find libdwarf.h") endif() else() set(dwarf_lib libdwarf::dwarf-static) target_link_libraries(${target_name} PRIVATE libdwarf::dwarf-static) endif() if(UNIX) target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS}) endif() endif() if(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) target_compile_definitions(${target_name} PRIVATE CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) target_link_libraries(${target_name} PRIVATE dbghelp) endif() if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING) target_compile_definitions(${target_name} PRIVATE CPPTRACE_GET_SYMBOLS_WITH_NOTHING) endif() # Unwinding if(CPPTRACE_UNWIND_WITH_UNWIND) if(NOT HAS_UNWIND) message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_UNWIND specified but libgcc unwind doesn't seem to be available.") endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_UNWIND) endif() if(CPPTRACE_UNWIND_WITH_LIBUNWIND) find_package(PkgConfig) if(PkgConfig_FOUND) pkg_check_modules(LIBUNWIND QUIET libunwind) if(libunwind_FOUND) target_compile_options(${target_name} PRIVATE ${LIBUNWIND_CFLAGS_OTHER}) target_include_directories(${target_name} PRIVATE ${LIBUNWIND_INCLUDE_DIRS}) target_link_libraries(${target_name} PRIVATE ${LIBUNWIND_LDFLAGS}) endif() endif() if(NOT libunwind_FOUND) if (NOT APPLE) # set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON) # set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS ON) find_path(LIBUNWIND_INCLUDE_DIRS NAMES "libunwind.h") find_library(LIBUNWIND NAMES unwind libunwind libunwind8 libunwind.so.8 REQUIRED PATHS "/usr/lib/x86_64-linux-gnu/") if(LIBUNWIND) set(libunwind_FOUND TRUE) endif() if(NOT libunwind_FOUND) # message(FATAL_ERROR "Unable to locate libunwind") # Try to link with it if it's where it should be # This path can be entered if libunwind was installed via the system package manager, sometimes. I probably messed # up the find_library above. set(LIBUNWIND_LDFLAGS "-lunwind") endif() if(NOT LIBUNWIND_LDFLAGS) set(LIBUNWIND_LDFLAGS "${LIBUNWIND}") endif() target_compile_options(${target_name} PRIVATE ${LIBUNWIND_CFLAGS_OTHER}) target_include_directories(${target_name} PRIVATE ${LIBUNWIND_INCLUDE_DIRS}) target_link_libraries(${target_name} PRIVATE ${LIBUNWIND_LDFLAGS}) endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_LIBUNWIND UNW_LOCAL_ONLY) endif() endif() if(CPPTRACE_UNWIND_WITH_EXECINFO) if(NOT HAS_EXECINFO) message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.") endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_EXECINFO) endif() if(CPPTRACE_UNWIND_WITH_WINAPI) target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_WINAPI) endif() if(CPPTRACE_UNWIND_WITH_DBGHELP) if(NOT HAS_STACKWALK) message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_DBGHELP specified but dbghelp stackwalk64 doesn't seem to be available.") endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_DBGHELP) target_link_libraries(${target_name} PRIVATE dbghelp) endif() if(CPPTRACE_UNWIND_WITH_NOTHING) target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_NOTHING) endif() # Demangling if(CPPTRACE_DEMANGLE_WITH_CXXABI) if(NOT HAS_CXXABI) message(WARNING "Cpptrace: CPPTRACE_DEMANGLE_WITH_CXXABI specified but cxxabi.h doesn't seem to be available.") endif() target_compile_definitions(${target_name} PRIVATE CPPTRACE_DEMANGLE_WITH_CXXABI) endif() if(CPPTRACE_DEMANGLE_WITH_WINAPI) target_compile_definitions(${target_name} PRIVATE CPPTRACE_DEMANGLE_WITH_WINAPI) target_link_libraries(${target_name} PRIVATE dbghelp) endif() if(CPPTRACE_DEMANGLE_WITH_NOTHING) target_compile_definitions(${target_name} PRIVATE CPPTRACE_DEMANGLE_WITH_NOTHING) endif() if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") target_compile_definitions(${target_name} PRIVATE CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH}) endif() if(NOT "${CPPTRACE_HARD_MAX_FRAMES}" STREQUAL "") target_compile_definitions(${target_name} PRIVATE CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES}) endif() # ====================================================== Install ======================================================= if(NOT CMAKE_SKIP_INSTALL_RULES) include(cmake/InstallRules.cmake) endif() # ================================================== Demo/test/tools =================================================== if(CPPTRACE_BUILD_TESTING) if(PROJECT_IS_TOP_LEVEL) enable_testing() endif() add_subdirectory(test) endif() if(CPPTRACE_BUILD_BENCHMARKING) add_subdirectory(benchmarking) endif() if(CPPTRACE_BUILD_TOOLS) add_subdirectory(tools) endif() cpptrace-1.0.4/CONTRIBUTING.md000066400000000000000000000037361504061443700155630ustar00rootroot00000000000000# Contributing Welcome, thank you for your interest in the project! ## Getting started Contributions are always welcome. If you have not already, consider joining the community discord (linked in the README). There is discussion about library development there as well as a development roadmap. Github issues are also a good place to start. I'm happy to merge fixes, improvements, and features as well as help with getting pull requests (PRs) over the finish line. That being said, I can't merge stylistic changes, premature-optimizations, or micro-optimizations. When contributing, please try to match the current code style in the codebase. Style doesn't matter too much ultimately but consistency within a codebase is important. Please base changes against the `dev` branch, which is used for development. ## Code organization The library's public interface is defined in headers in `include/`. Declarations for the public interface have definitions in .cpp files in the top-level of `src/`. Implementation for various actions such as unwinding, demangling, symbol resolution, etc. are put in various sub-directories of `src/`. ## Local development The easiest way to get started with local development is running `make debug` (which automatically configures cmake and builds). Note: This requires ninja at the moment. For more control over how the library is built you can manually build with cmake: `cmake ..` in a `build/` folder along with any cmake configurations you desire. Then run `make -j` or `ninja` or `msbuild cpptrace.sln`. Some useful configurations: - `-DCMAKE_BUILD_TYPE=Debug|Release|RelWithDebInfo`: Build in debug / release / etc. - `-DBUILD_SHARED_LIBS=On`: Build shared library - `-DCPPTRACE_SANITIZER_BUILD=On`: Turn on sanitizers - `-DCPPTRACE_BUILD_TESTING=On`: Build small test and demo program ## Testing Unfortunately because this library is so platform-dependent the best way to test thoroughly is with github's CI. This will happen automatically when you open a PR. cpptrace-1.0.4/LICENSE000066400000000000000000000020751504061443700143320ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2023-2025 Jeremy Rifkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cpptrace-1.0.4/MODULE.bazel000066400000000000000000000052661504061443700153360ustar00rootroot00000000000000module( name = "cpptrace", ) bazel_dep(name = "googletest", version = "1.14.0") bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "rules_foreign_cc", version = "0.11.1") bazel_dep(name = "zstd", version = "1.5.6") bazel_dep(name = "zlib", version = "1.3.1") bazel_dep(name = "xz", version = "5.4.5.bcr.2") bazel_dep(name = "toolchains_llvm", version = "1.1.2") # Configure and register the toolchain. llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) llvm.toolchain( llvm_versions = { "": "18.1.8", }, sha256 = { "": "54ec30358afcc9fb8aa74307db3046f5187f9fb89fb37064cdde906e062ebf36", }, strip_prefix = { "": "clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04", }, urls = { "": ["https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz"], }, ) use_repo(llvm, "llvm_toolchain") register_toolchains("@llvm_toolchain//:all", dev_dependency = True) http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "libdwarf", build_file_content = """ package(default_visibility = ["//visibility:public"]) load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") filegroup( name = "sources", srcs = glob(["**/*"]), ) cmake( name = "libdwarf", build_args = ["-j12"], lib_source = ":sources", out_static_libs = ["libdwarf.a"], copts = ["-Wall", "-Werror"], deps = [ "@zstd", "@zlib" ] ) """, sha256 = "4ab8ae7b4b7aa42453725054b348f4fdb2460d5ba644199a1305311c718ff416", strip_prefix = "libdwarf-code-0.10.1", url = "https://github.com/davea42/libdwarf-code/archive/refs/tags/v0.10.1.tar.gz", ) http_archive( name = "libunwind", build_file_content = """ package(default_visibility = ["//visibility:public"]) load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") filegroup( name = "sources", srcs = glob(["**/*"]), ) configure_make( name = "libunwind", args = ["-j12"], autoreconf = True, configure_in_place = True, autoreconf_options = [ "-i", ], lib_source = ":sources", out_static_libs = [ "libunwind.a", "libunwind-coredump.a", "libunwind-ptrace.a", "libunwind-x86_64.a", "libunwind-generic.a", "libunwind-setjmp.a" ], deps = [ "@xz//:lzma" ] ) """, sha256 = "38833b7b1582db7d76485a62a213706c9252b3dab7380069fea5824e823d8e41", strip_prefix = "libunwind-1.8.1", url = "https://github.com/libunwind/libunwind/archive/refs/tags/v1.8.1.tar.gz", ) cpptrace-1.0.4/MODULE.bazel.lock000066400000000000000000001430471504061443700162650ustar00rootroot00000000000000{ "lockFileVersion": 11, "registryFileHashes": { "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/source.json": "06cc0842d241da0c5edc755edb3c7d0d008d304330e57ecf2d6449fb0b633a82", "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef", "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5", "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6", "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", "https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b", "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430", "https://bcr.bazel.build/modules/rules_foreign_cc/0.11.1/MODULE.bazel": "beeb0dd8d488d3cff57fa12ab3378051a7299aa9de2476d61c1d46f664d6398d", "https://bcr.bazel.build/modules/rules_foreign_cc/0.11.1/source.json": "be2106be697115c10c03c6505a07bd4e259719c6608f08a61d600a560b8cf172", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", "https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35", "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a", "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", "https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c", "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9", "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", "https://bcr.bazel.build/modules/rules_python/0.23.1/source.json": "a6d9965700e3bd75df4e19140c0e651851bb720d8b9eb280ecd1ee44b92d7646", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29", "https://bcr.bazel.build/modules/toolchains_llvm/1.1.2/MODULE.bazel": "402101d6f73115ec49a3a765a3361c1dd90ba3959fa688ccdcd465c36dbbbc52", "https://bcr.bazel.build/modules/toolchains_llvm/1.1.2/source.json": "27f3cf531bc654c719b50411cac94613b7676d63e60962243d485af63e13b9ff", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459", "https://bcr.bazel.build/modules/xz/5.4.5.bcr.2/MODULE.bazel": "463976fb85f578a2535421ba4c38fe90657ab348e4b5d5404b75c061602705d0", "https://bcr.bazel.build/modules/xz/5.4.5.bcr.2/source.json": "e735da8a3f396bf200ed06c585f670f7667e08c4e1ed2849bae7c2691bcb10cf", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d", "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198", "https://bcr.bazel.build/modules/zstd/1.5.6/MODULE.bazel": "471ebe7d3cdd8c6469390fcf623eb4779ff55fbee0a87f1dc57a1def468b96d4", "https://bcr.bazel.build/modules/zstd/1.5.6/source.json": "02010c3333fc89b44fe861db049968decb6e688411f7f9d4f6791d74f9adfb51" }, "selectedYankedVersions": {}, "moduleExtensions": { "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { "general": { "bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=", "usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "local_config_apple_cc": { "bzlFile": "@@apple_support~//crosstool:setup.bzl", "ruleClassName": "_apple_cc_autoconf", "attributes": {} }, "local_config_apple_cc_toolchains": { "bzlFile": "@@apple_support~//crosstool:setup.bzl", "ruleClassName": "_apple_cc_autoconf_toolchains", "attributes": {} } }, "recordedRepoMappingEntries": [ [ "apple_support~", "bazel_tools", "bazel_tools" ] ] } }, "@@platforms//host:extension.bzl%host_platform": { "general": { "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", "usagesDigest": "meSzxn3DUCcYEhq4HQwExWkWtU4EjriRBQLsZN+Q0SU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "host_platform": { "bzlFile": "@@platforms//host:extension.bzl", "ruleClassName": "host_platform_repo", "attributes": {} } }, "recordedRepoMappingEntries": [] } }, "@@rules_foreign_cc~//foreign_cc:extensions.bzl%tools": { "general": { "bzlTransitiveDigest": "5Xt39wqg6Xufojy5gN4ke9V2Mv5ANvdeLlAL1hp6+ic=", "usagesDigest": "WnYOMrYXMz7KcDu0mMpDP5Eue8sHuFMA1dmDT6I124Q=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "cmake-3.23.2-linux-aarch64": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz" ], "sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e", "strip_prefix": "cmake-3.23.2-linux-aarch64", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" } }, "rules_foreign_cc_framework_toolchain_macos": { "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", "ruleClassName": "framework_toolchain_repository", "attributes": { "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl", "exec_compatible_with": [ "@platforms//os:macos" ] } }, "ninja_1.12.0_mac": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-mac.zip" ], "sha256": "19806019c9623a062c3d9fa0d5f45b633a3d150f88e73fbd6c0ff6ea5534df10", "strip_prefix": "", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" } }, "ninja_1.12.0_mac_aarch64": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-mac.zip" ], "sha256": "19806019c9623a062c3d9fa0d5f45b633a3d150f88e73fbd6c0ff6ea5534df10", "strip_prefix": "", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" } }, "gnumake_src": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", "sha256": "dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3", "strip_prefix": "make-4.4.1", "urls": [ "https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz", "http://ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz" ] } }, "gettext_runtime": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", "sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079", "urls": [ "https://mirror.bazel.build/download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip", "https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip" ] } }, "cmake_src": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", "sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa", "strip_prefix": "cmake-3.23.2", "urls": [ "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz" ], "patches": [ "@@rules_foreign_cc~//toolchains:cmake-c++11.patch" ] } }, "bazel_skylib": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz" ], "sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728" } }, "cmake-3.23.2-macos-universal": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz" ], "sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88", "strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" } }, "ninja_1.12.0_win": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-win.zip" ], "sha256": "51d99be9ceea8835edf536d52d47fa4c316aa332e57f71a08df5bd059da11417", "strip_prefix": "", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" } }, "meson_src": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n", "sha256": "d04b541f97ca439fb82fab7d0d480988be4bd4e62563a5ca35fadb5400727b1c", "strip_prefix": "meson-1.1.1", "urls": [ "https://mirror.bazel.build/github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz", "https://github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz" ] } }, "rules_foreign_cc_framework_toolchain_freebsd": { "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", "ruleClassName": "framework_toolchain_repository", "attributes": { "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl", "exec_compatible_with": [ "@platforms//os:freebsd" ] } }, "rules_foreign_cc_framework_toolchain_linux": { "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", "ruleClassName": "framework_toolchain_repository", "attributes": { "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl", "exec_compatible_with": [ "@platforms//os:linux" ] } }, "rules_python": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841", "strip_prefix": "rules_python-0.23.1", "url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz" } }, "pkgconfig_src": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", "sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591", "strip_prefix": "pkg-config-0.29.2", "patches": [ "@@rules_foreign_cc~//toolchains:pkgconfig-detectenv.patch", "@@rules_foreign_cc~//toolchains:pkgconfig-makefile-vc.patch", "@@rules_foreign_cc~//toolchains:pkgconfig-builtin-glib-int-conversion.patch" ], "urls": [ "https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz", "https://mirror.bazel.build/pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz" ] } }, "ninja_build_src": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", "integrity": "sha256-iyyGzUg9x/y3l1xexzKRNdIQCZqJvH2wWQoHsLv+SaU=", "strip_prefix": "ninja-1.12.0", "urls": [ "https://mirror.bazel.build/github.com/ninja-build/ninja/archive/v1.12.0.tar.gz", "https://github.com/ninja-build/ninja/archive/v1.12.0.tar.gz" ] } }, "glib_src": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ", "sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e", "strip_prefix": "glib-2.26.1", "urls": [ "https://mirror.bazel.build/download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz", "https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz" ] } }, "cmake-3.23.2-windows-x86_64": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip" ], "sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0", "strip_prefix": "cmake-3.23.2-windows-x86_64", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" } }, "glib_runtime": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ", "sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973", "urls": [ "https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip", "https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip" ] } }, "rules_foreign_cc_framework_toolchains": { "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", "ruleClassName": "framework_toolchain_repository_hub", "attributes": {} }, "glib_dev": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", "sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369", "urls": [ "https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip", "https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip" ] } }, "cmake_3.23.2_toolchains": { "bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl", "ruleClassName": "prebuilt_toolchains_repository", "attributes": { "repos": { "cmake-3.23.2-linux-aarch64": [ "@platforms//cpu:aarch64", "@platforms//os:linux" ], "cmake-3.23.2-linux-x86_64": [ "@platforms//cpu:x86_64", "@platforms//os:linux" ], "cmake-3.23.2-macos-universal": [ "@platforms//os:macos" ], "cmake-3.23.2-windows-i386": [ "@platforms//cpu:x86_32", "@platforms//os:windows" ], "cmake-3.23.2-windows-x86_64": [ "@platforms//cpu:x86_64", "@platforms//os:windows" ] }, "tool": "cmake" } }, "ninja_1.12.0_linux-aarch64": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux-aarch64.zip" ], "sha256": "375a49c79095334c88338ff15f90730e08a4d03997ef660f48f11ee7e450db7a", "strip_prefix": "", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" } }, "cmake-3.23.2-windows-i386": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip" ], "sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07", "strip_prefix": "cmake-3.23.2-windows-i386", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" } }, "ninja_1.12.0_linux": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux.zip" ], "sha256": "ddc96efa3c7c9d41de733d15e2eda07a8a212555cb43f35d727e080d2ca687ab", "strip_prefix": "", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" } }, "cmake-3.23.2-linux-x86_64": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { "urls": [ "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz" ], "sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708", "strip_prefix": "cmake-3.23.2-linux-x86_64", "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n" } }, "ninja_1.12.0_toolchains": { "bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl", "ruleClassName": "prebuilt_toolchains_repository", "attributes": { "repos": { "ninja_1.12.0_linux": [ "@platforms//cpu:x86_64", "@platforms//os:linux" ], "ninja_1.12.0_linux-aarch64": [ "@platforms//cpu:aarch64", "@platforms//os:linux" ], "ninja_1.12.0_mac": [ "@platforms//cpu:x86_64", "@platforms//os:macos" ], "ninja_1.12.0_mac_aarch64": [ "@platforms//cpu:aarch64", "@platforms//os:macos" ], "ninja_1.12.0_win": [ "@platforms//cpu:x86_64", "@platforms//os:windows" ] }, "tool": "ninja" } }, "rules_foreign_cc_framework_toolchain_windows": { "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", "ruleClassName": "framework_toolchain_repository", "attributes": { "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl", "exec_compatible_with": [ "@platforms//os:windows" ] } } }, "recordedRepoMappingEntries": [ [ "rules_foreign_cc~", "bazel_tools", "bazel_tools" ], [ "rules_foreign_cc~", "rules_foreign_cc", "rules_foreign_cc~" ] ] } }, "@@rules_python~//python/extensions:python.bzl%python": { "general": { "bzlTransitiveDigest": "XaaZIw4dO4l6naftU5IBdrfCE1mOmelaT/Sq9uyBnhs=", "usagesDigest": "XRXGQ1YSlgZzzO0pux+3DEHfP/c70L/kznvRIwakvlw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "python_3_11_aarch64-unknown-linux-gnu": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "debf15783bdcb5530504f533d33fda75a7b905cec5361ae8f33da5ba6599f8b4", "patches": [], "platform": "aarch64-unknown-linux-gnu", "python_version": "3.11.1", "release_filename": "20230116/cpython-3.11.1+20230116-aarch64-unknown-linux-gnu-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-aarch64-unknown-linux-gnu-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9": { "bzlFile": "@@rules_python~//python/private:toolchains_repo.bzl", "ruleClassName": "toolchain_aliases", "attributes": { "python_version": "3.9.16", "user_repository_name": "python_3_9" } }, "python_3_11_aarch64-apple-darwin": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "4918cdf1cab742a90f85318f88b8122aeaa2d04705803c7b6e78e81a3dd40f80", "patches": [], "platform": "aarch64-apple-darwin", "python_version": "3.11.1", "release_filename": "20230116/cpython-3.11.1+20230116-aarch64-apple-darwin-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-aarch64-apple-darwin-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "pythons_hub": { "bzlFile": "@@rules_python~//python/extensions/private:pythons_hub.bzl", "ruleClassName": "hub_repo", "attributes": { "toolchain_prefixes": [ "_0000_python_3_9_", "_0001_python_3_11_" ], "toolchain_python_versions": [ "3.9", "3.11" ], "toolchain_set_python_version_constraints": [ "True", "False" ], "toolchain_user_repository_names": [ "python_3_9", "python_3_11" ] } }, "python_3_11_x86_64-pc-windows-msvc": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "edc08979cb0666a597466176511529c049a6f0bba8adf70df441708f766de5bf", "patches": [], "platform": "x86_64-pc-windows-msvc", "python_version": "3.11.1", "release_filename": "20230116/cpython-3.11.1+20230116-x86_64-pc-windows-msvc-shared-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64-pc-windows-msvc-shared-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9_aarch64-apple-darwin": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "c1de1d854717a6245f45262ef1bb17b09e2c587590e7e3f406593c143ff875bd", "patches": [], "platform": "aarch64-apple-darwin", "python_version": "3.9.16", "release_filename": "20230507/cpython-3.9.16+20230507-aarch64-apple-darwin-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-aarch64-apple-darwin-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9_x86_64-pc-windows-msvc": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "cdabb47204e96ce7ea31fbd0b5ed586114dd7d8f8eddf60a509a7f70b48a1c5e", "patches": [], "platform": "x86_64-pc-windows-msvc", "python_version": "3.9.16", "release_filename": "20230507/cpython-3.9.16+20230507-x86_64-pc-windows-msvc-shared-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-x86_64-pc-windows-msvc-shared-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9_ppc64le-unknown-linux-gnu": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "ff3ac35c58f67839aff9b5185a976abd3d1abbe61af02089f7105e876c1fe284", "patches": [], "platform": "ppc64le-unknown-linux-gnu", "python_version": "3.9.16", "release_filename": "20230507/cpython-3.9.16+20230507-ppc64le-unknown-linux-gnu-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-ppc64le-unknown-linux-gnu-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9_aarch64-unknown-linux-gnu": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "f629b75ebfcafe9ceee2e796b7e4df5cf8dbd14f3c021afca078d159ab797acf", "patches": [], "platform": "aarch64-unknown-linux-gnu", "python_version": "3.9.16", "release_filename": "20230507/cpython-3.9.16+20230507-aarch64-unknown-linux-gnu-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-aarch64-unknown-linux-gnu-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_aliases": { "bzlFile": "@@rules_python~//python/private:toolchains_repo.bzl", "ruleClassName": "multi_toolchain_aliases", "attributes": { "python_versions": { "3.9": "python_3_9", "3.11": "python_3_11" } } }, "python_3_11": { "bzlFile": "@@rules_python~//python/private:toolchains_repo.bzl", "ruleClassName": "toolchain_aliases", "attributes": { "python_version": "3.11.1", "user_repository_name": "python_3_11" } }, "python_3_11_x86_64-apple-darwin": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "20a4203d069dc9b710f70b09e7da2ce6f473d6b1110f9535fb6f4c469ed54733", "patches": [], "platform": "x86_64-apple-darwin", "python_version": "3.11.1", "release_filename": "20230116/cpython-3.11.1+20230116-x86_64-apple-darwin-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64-apple-darwin-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9_x86_64-apple-darwin": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "3abc4d5fbbc80f5f848f280927ac5d13de8dc03aabb6ae65d8247cbb68e6f6bf", "patches": [], "platform": "x86_64-apple-darwin", "python_version": "3.9.16", "release_filename": "20230507/cpython-3.9.16+20230507-x86_64-apple-darwin-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-x86_64-apple-darwin-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_9_x86_64-unknown-linux-gnu": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "2b6e146234a4ef2a8946081fc3fbfffe0765b80b690425a49ebe40b47c33445b", "patches": [], "platform": "x86_64-unknown-linux-gnu", "python_version": "3.9.16", "release_filename": "20230507/cpython-3.9.16+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } }, "python_3_11_x86_64-unknown-linux-gnu": { "bzlFile": "@@rules_python~//python:repositories.bzl", "ruleClassName": "python_repository", "attributes": { "sha256": "02a551fefab3750effd0e156c25446547c238688a32fabde2995c941c03a6423", "patches": [], "platform": "x86_64-unknown-linux-gnu", "python_version": "3.11.1", "release_filename": "20230116/cpython-3.11.1+20230116-x86_64-unknown-linux-gnu-install_only.tar.gz", "urls": [ "https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64-unknown-linux-gnu-install_only.tar.gz" ], "distutils_content": "", "strip_prefix": "python", "ignore_root_user_error": false } } }, "recordedRepoMappingEntries": [ [ "rules_python~", "bazel_tools", "bazel_tools" ] ] } }, "@@toolchains_llvm~//toolchain/extensions:llvm.bzl%llvm": { "general": { "bzlTransitiveDigest": "y9h5L2NtWbogyWSOJgqnUaU50MTPWAW+waelXSirMVg=", "usagesDigest": "cWIs+RUBBwv1tE2cNVULmSLCA/wGvHWaQzdrujSEO74=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "llvm_toolchain": { "bzlFile": "@@toolchains_llvm~//toolchain:rules.bzl", "ruleClassName": "toolchain", "attributes": { "absolute_paths": false, "archive_flags": {}, "compile_flags": {}, "coverage_compile_flags": {}, "coverage_link_flags": {}, "cxx_builtin_include_directories": {}, "cxx_flags": {}, "cxx_standard": {}, "dbg_compile_flags": {}, "exec_arch": "", "exec_os": "", "link_flags": {}, "link_libs": {}, "llvm_versions": { "": "18.1.8" }, "opt_compile_flags": {}, "opt_link_flags": {}, "stdlib": {}, "target_settings": {}, "unfiltered_compile_flags": {}, "toolchain_roots": {}, "sysroot": {} } }, "llvm_toolchain_llvm": { "bzlFile": "@@toolchains_llvm~//toolchain:rules.bzl", "ruleClassName": "llvm", "attributes": { "alternative_llvm_sources": [], "auth_patterns": {}, "distribution": "auto", "exec_arch": "", "exec_os": "", "llvm_mirror": "", "llvm_version": "", "llvm_versions": { "": "18.1.8" }, "netrc": "", "sha256": { "": "54ec30358afcc9fb8aa74307db3046f5187f9fb89fb37064cdde906e062ebf36" }, "strip_prefix": { "": "clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04" }, "urls": { "": [ "https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz" ] } } } }, "recordedRepoMappingEntries": [ [ "toolchains_llvm~", "bazel_skylib", "bazel_skylib~" ], [ "toolchains_llvm~", "bazel_tools", "bazel_tools" ], [ "toolchains_llvm~", "toolchains_llvm", "toolchains_llvm~" ] ] } } } } cpptrace-1.0.4/Makefile000066400000000000000000000037471504061443700147740ustar00rootroot00000000000000default: help # The general philosophy and functionality of this makefile is shamelessly stolen from compiler explorer help: # with thanks to Ben Rady @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' .PHONY: build build: debug ## build in debug mode build/configured-debug: cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On rm -f build/configured-release touch build/configured-debug build/configured-release: cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On rm -f build/configured-debug touch build/configured-release .PHONY: configure-debug configure-debug: build/configured-debug .PHONY: configure-release configure-release: build/configured-release .PHONY: debug debug: configure-debug ## build in debug mode cmake --build build .PHONY: release release: configure-release ## build in release mode (with debug info) cmake --build build .PHONY: debug-msvc debug-msvc: ## build in debug mode cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On cmake --build build --config Debug .PHONY: release-msvc release-msvc: ## build in release mode (with debug info) cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On cmake --build build --config RelWithDebInfo .PHONY: clean clean: ## clean rm -rf build .PHONY: test test: debug ## test cd build && ninja test .PHONY: test-release test-release: release ## test-release cd build && ninja test # .PHONY: test-msvc # test-msvc: debug-msvc ## test # cmake --build build --target RUN_TESTS --config Debug # .PHONY: test-msvc-release # test-msvc-release: release-msvc ## test-release # cmake --build build --target RUN_TESTS --config Release cpptrace-1.0.4/README.md000066400000000000000000002317661504061443700146170ustar00rootroot00000000000000# Cpptrace [![CI](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/ci.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jeremy-rifkin_cpptrace&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=jeremy-rifkin_cpptrace)
[![Community Discord Link](https://img.shields.io/badge/Chat%20on%20the%20(very%20small)-Community%20Discord-blue?labelColor=2C3239&color=7289DA&style=flat&logo=discord&logoColor=959DA5)](https://discord.gg/frjaAZvqUZ)
[![Try on Compiler Explorer](https://img.shields.io/badge/-Compiler%20Explorer-brightgreen?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAYAAAAmlE46AAAACXBIWXMAAACwAAAAsAEUaqtpAAABSElEQVQokYVTsU7DMBB9QMTCEJbOMLB5oF0tRfUPIPIJZctYJkZYu3WMxNL+ARUfQKpImcPgDYnsXWBgYQl61TkYyxI3Wef37j3fnQ/6vkcsikY9AbiWq0mpbevDBmLRqDEAA4CEHMADgFRwrwDmch6X2i73RCFVHvC/WCeCMAFpC2AFoPPu5x4md4rnAN4luS61nYWSgauNU8ydkr0bLTMYAoIYtWqxM4LtEumeERDtfUjlMDrp7L67iddyyJtOvUIu2rquVn4iiVSOKXYhiMSJWLwUJZLuQ2CWmVldV4MT11UmXgB8fr0dX3WP6VHMiVrscim6Da2mJxffzwSU2v6xWzSKmzQ4cUTOaCBTvWgU14xkzjhckKm/q3wnrRAcAhksxMZNAdxEf0fRKI6E8zqT1C0X28ccRpqAUltW5pu4sxv5Mb8B4AciE3bHMxz/+gAAAABJRU5ErkJggg==&labelColor=2C3239&style=flat&label=Try+it+on&color=30C452)](https://godbolt.org/z/aP8PsxxeY) Cpptrace is a simple and portable C++ stacktrace library supporting C++11 and greater on Linux, macOS, and Windows including MinGW and Cygwin environments. The goal: Make stack traces simple for once. In addition to providing access to stack traces, cpptrace also provides a mechanism for getting stacktraces from thrown exceptions which is immensely valuable for debugging and triaging. More info [below](#traces-from-all-exceptions-cpptrace_try-and-cpptrace_catch). Cpptrace also has a C API, docs [here](docs/c-api.md). ## Table of Contents - [30-Second Overview](#30-second-overview) - [CMake FetchContent Usage](#cmake-fetchcontent-usage) - [Prerequisites](#prerequisites) - [Basic Usage](#basic-usage) - [`namespace cpptrace`](#namespace-cpptrace) - [Stack Traces](#stack-traces) - [Object Traces](#object-traces) - [Raw Traces](#raw-traces) - [Utilities](#utilities) - [Formatting](#formatting) - [Transforms](#transforms) - [Configuration](#configuration) - [Logging](#logging) - [Traces From All Exceptions (`CPPTRACE_TRY` and `CPPTRACE_CATCH`)](#traces-from-all-exceptions-cpptrace_try-and-cpptrace_catch) - [Removing the `CPPTRACE_` prefix](#removing-the-cpptrace_-prefix) - [How it works](#how-it-works) - [Performance](#performance) - [Rethrowing Exceptions](#rethrowing-exceptions) - [`cpptrace::try_catch`](#cpptracetry_catch) - [Traces from SEH exceptions](#traces-from-seh-exceptions) - [Traced Exception Objects](#traced-exception-objects) - [Wrapping std::exceptions](#wrapping-stdexceptions) - [Exception handling with cpptrace exception objects](#exception-handling-with-cpptrace-exception-objects) - [Terminate Handling](#terminate-handling) - [Signal-Safe Tracing](#signal-safe-tracing) - [Utility Types](#utility-types) - [Headers](#headers) - [Libdwarf Tuning](#libdwarf-tuning) - [JIT Support](#jit-support) - [Loading Libraries at Runtime](#loading-libraries-at-runtime) - [ABI Versioning](#abi-versioning) - [Supported Debug Formats](#supported-debug-formats) - [How to Include The Library](#how-to-include-the-library) - [CMake FetchContent](#cmake-fetchcontent) - [System-Wide Installation](#system-wide-installation) - [Local User Installation](#local-user-installation) - [Use Without CMake](#use-without-cmake) - [Installation Without Package Managers or FetchContent](#installation-without-package-managers-or-fetchcontent) - [Package Managers](#package-managers) - [Conan](#conan) - [Vcpkg](#vcpkg) - [C++20 Modules](#c20-modules) - [Platform Logistics](#platform-logistics) - [Windows](#windows) - [macOS](#macos) - [Library Back-Ends](#library-back-ends) - [Summary of Library Configurations](#summary-of-library-configurations) - [Testing Methodology](#testing-methodology) - [Notes About the Library](#notes-about-the-library) - [FAQ](#faq) - [What about C++23 ``?](#what-about-c23-stacktrace) - [What does cpptrace have over other C++ stacktrace libraries?](#what-does-cpptrace-have-over-other-c-stacktrace-libraries) - [I'm getting undefined standard library symbols like `std::__1::basic_string` on MacOS](#im-getting-undefined-standard-library-symbols-like-std__1basic_string-on-macos) - [Contributing](#contributing) - [License](#license) # 30-Second Overview Generating stack traces is as easy as: ```cpp #include void trace() { cpptrace::generate_trace().print(); } ``` ![Demo](res/demo.png) Cpptrace can also retrieve function inlining information on optimized release builds: ![Inlining](res/inlining.png) Cpptrace provides access to resolved stack traces as well as fast and lightweight raw traces (just addresses) that can be resolved later: ```cpp const auto raw_trace = cpptrace::generate_raw_trace(); // then later raw_trace.resolve().print(); ``` One of the most important features cpptrace offers is the ability to retrieve stack traces on arbitrary exceptions. More information on this system [below]((#traces-from-all-exceptions-cpptrace_try-and-cpptrace_catch)). ```cpp #include #include #include void foo() { throw std::runtime_error("foo failed"); } int main() { CPPTRACE_TRY { foo(); } CPPTRACE_CATCH(const std::exception& e) { std::cerr<<"Exception: "< void trace() { throw cpptrace::logic_error("This wasn't supposed to happen!"); } ``` ![Exception](res/exception.png) Additional notable features: - Utilities for demangling - Utilities for catching `std::exception`s and wrapping them in traced exceptions - Signal-safe stack tracing - As far as I can tell cpptrace is the only library which can truly do this in a signal-safe manner - Source code snippets in traces - Extensive configuration options for [trace formatting](#formatting) and pretty-printing ![Snippets](res/snippets.png) ## CMake FetchContent Usage ```cmake include(FetchContent) FetchContent_Declare( cpptrace GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git GIT_TAG v1.0.4 # ) FetchContent_MakeAvailable(cpptrace) target_link_libraries(your_target cpptrace::cpptrace) # Needed for shared library builds on windows: copy cpptrace.dll to the same directory as the # executable for your_target if(WIN32) add_custom_command( TARGET your_target POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endif() ``` Be sure to configure with `-DCMAKE_BUILD_TYPE=Debug` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` for symbols and line information. On macOS it is recommended to generate a `.dSYM` file, see [Platform Logistics](#platform-logistics) below. For other ways to use the library, such as through package managers, a system-wide installation, or on a platform without internet access see [How to Include The Library](#how-to-include-the-library) below. # Prerequisites > [!IMPORTANT] > Debug info (`-g`/`/Z7`/`/Zi`/`/DEBUG`/`-DBUILD_TYPE=Debug`/`-DBUILD_TYPE=RelWithDebInfo`) is required for complete > trace information. # Basic Usage `cpptrace::generate_trace()` can be used to generate a `stacktrace` object at the current call site. Resolved frames can be accessed from this object with `.frames` and the trace can be printed with `.print()`. Cpptrace also provides a method to get light-weight raw traces with `cpptrace::generate_raw_trace()`, which are just vectors of program counters, which can be resolved at a later time. # `namespace cpptrace` All functions are thread-safe unless otherwise noted. ## Stack Traces The core resolved stack trace object. Generate a trace with `cpptrace::generate_trace()` or `cpptrace::stacktrace::current()`. On top of a set of helper functions `struct stacktrace` allows direct access to frames as well as iterators. `cpptrace::stacktrace::print` can be used to print a stacktrace. `cpptrace::stacktrace::print_with_snippets` can be used to print a stack trace with source code snippets. ```cpp namespace cpptrace { // Some type sufficient for an instruction pointer, currently always an alias to std::uintptr_t using frame_ptr = std::uintptr_t; struct stacktrace_frame { frame_ptr raw_address; // address in memory frame_ptr object_address; // address in the object file // nullable represents a nullable integer. More docs later. nullable line; nullable column; std::string filename; std::string symbol; bool is_inline; bool operator==(const stacktrace_frame& other) const; bool operator!=(const stacktrace_frame& other) const; object_frame get_object_info() const; // object_address is stored but if the object_path is needed this can be used std::string to_string() const; /* operator<<(ostream, ..) and std::format support exist for this object */ }; struct stacktrace { std::vector frames; // here as a drop-in for std::stacktrace static stacktrace current(std::size_t skip = 0); static stacktrace current(std::size_t skip, std::size_t max_depth); void print() const; void print(std::ostream& stream) const; void print(std::ostream& stream, bool color) const; void print_with_snippets() const; void print_with_snippets(std::ostream& stream) const; void print_with_snippets(std::ostream& stream, bool color) const; std::string to_string(bool color = false) const; void clear(); bool empty() const noexcept; /* operator<<(ostream, ..), std::format support, and iterators exist for this object */ }; stacktrace generate_trace(std::size_t skip = 0); stacktrace generate_trace(std::size_t skip, std::size_t max_depth); } ``` ## Object Traces Object traces contain the most basic information needed to construct a stack trace outside the currently running executable. It contains the raw address, the address in the binary (ASLR and the object file's memory space and whatnot is resolved), and the path to the object the instruction pointer is located in. ```cpp namespace cpptrace { struct object_frame { std::string object_path; frame_ptr raw_address; frame_ptr object_address; }; struct object_trace { std::vector frames; static object_trace current(std::size_t skip = 0); static object_trace current(std::size_t skip, std::size_t max_depth); stacktrace resolve() const; void clear(); bool empty() const noexcept; /* iterators exist for this object */ }; object_trace generate_object_trace(std::size_t skip = 0); object_trace generate_object_trace(std::size_t skip, std::size_t max_depth); } ``` ## Raw Traces Raw trace access: A vector of program counters. These are ideal for fast and cheap traces you want to resolve later. Note it is important executables and shared libraries in memory aren't somehow unmapped otherwise libdl calls (and `GetModuleFileName` in windows) will fail to figure out where the program counter corresponds to. ```cpp namespace cpptrace { struct raw_trace { std::vector frames; static raw_trace current(std::size_t skip = 0); static raw_trace current(std::size_t skip, std::size_t max_depth); object_trace resolve_object_trace() const; stacktrace resolve() const; void clear(); bool empty() const noexcept; /* iterators exist for this object */ }; raw_trace generate_raw_trace(std::size_t skip = 0); raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth); } ``` ## Utilities `cpptrace::demangle` is a helper function for name demangling, since it has to implement that helper internally anyways. `cpptrace::basename` is a helper for custom formatters that extracts a base file name from a path. `cpptrace::prettify_symbol` is a helper for custom formatters that applies a number of transformations to clean up long symbol names. For example, it turns `std::__cxx11::basic_string, std::allocator >` into `std::string`. `cpptrace::prune_symbol` is a helper for custom formatters that prunes demangled symbols by removing return types, template arguments, and function parameters. It also does some minimal normalization. For example, it prunes `ns::S::~S()` to `ns::S::~S`. If cpptrace is unable to parse the symbol it will return the original symbol. `cpptrace::get_snippet` gets a text snippet, if possible, from for the given source file for +/- `context_size` lines around `line`. `cpptrace::isatty` and the fileno definitions are useful for deciding whether to use color when printing stack traces. `cpptrace::register_terminate_handler()` is a helper function to set a custom `std::terminate` handler that prints a stack trace from a cpptrace exception (more info below) and otherwise behaves like the normal terminate handler. ```cpp namespace cpptrace { std::string demangle(const std::string& name); std::string basename(const std::string& path); std::string prettify_symbol(std::string symbol); std::string prune_symbol(const std::string& symbol); std::string get_snippet( const std::string& path, std::size_t line, std::size_t context_size, bool color = false ); std::string get_snippet( const std::string& path, std::size_t line, nullable column, std::size_t context_size, bool color = false ); bool isatty(int fd); extern const int stdin_fileno; extern const int stderr_fileno; extern const int stdout_fileno; void register_terminate_handler(); } ``` ## Formatting Cpptrace provides a configurable formatter for stack trace printing which supports some common options. Formatters are configured with a sort of builder pattern, e.g.: ```cpp auto formatter = cpptrace::formatter{} .header("Stack trace:") .addresses(cpptrace::formatter::address_mode::object) .snippets(true); ``` This API is available through the `` header. Synopsis: ```cpp namespace cpptrace { class formatter { formatter& header(std::string); enum class color_mode { always, none, automatic }; formatter& colors(color_mode); enum class address_mode { raw, object, none }; formatter& addresses(address_mode); enum class path_mode { full, basename }; formatter& paths(path_mode); formatter& snippets(bool); formatter& snippet_context(int); formatter& columns(bool); enum class symbol_mode { full, pretty, pruned }; formatter& symbols(symbol_mode); formatter& filtered_frame_placeholders(bool); formatter& filter(std::function); formatter& transform(std::function); formatter& break_before_filename(bool do_break = true); formatter& hide_exception_machinery(bool do_hide = true); std::string format(const stacktrace_frame&) const; std::string format(const stacktrace_frame&, bool color) const; std::string format(const stacktrace&) const; std::string format(const stacktrace&, bool color) const; void print(const stacktrace_frame&) const; void print(const stacktrace_frame&, bool color) const; void print(std::ostream&, const stacktrace_frame&) const; void print(std::ostream&, const stacktrace_frame&, bool color) const; void print(std::FILE*, const stacktrace_frame&) const; void print(std::FILE*, const stacktrace_frame&, bool color) const; void print(const stacktrace&) const; void print(const stacktrace&, bool color) const; void print(std::ostream&, const stacktrace&) const; void print(std::ostream&, const stacktrace&, bool color) const; void print(std::FILE*, const stacktrace&) const; void print(std::FILE*, const stacktrace&, bool color) const; }; } ``` Options: | Setting | Description | Default | | ----------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ | | `header` | Header line printed before the trace | `Stack trace (most recent call first):` | | `colors` | Default color mode for the trace | `automatic`, which attempts to detect if the target stream is a terminal | | `addresses` | Raw addresses, object addresses, or no addresses | `raw` | | `paths` | Full paths or just filenames | `full` | | `snippets` | Whether to include source code snippets | `false` | | `snippet_context` | How many lines of source context to show in a snippet | `2` | | `columns` | Whether to include column numbers if present | `true` | | `symbols` | Full demangled symbols, pruned symbol names, or prettified symbols | `full` | | `filtered_frame_placeholders` | Whether to still print filtered frames as just `#n (filtered)` | `true` | | `filter` | A predicate to filter frames with | None | | `transform` | A transformer which takes a stacktrace frame and modifies it | None | | `break_before_filename` | Print symbol and line source location on different lines | `false` | | `hide_exception_machinery` | Hide exception internals for current exception traces | `true` | The `automatic` color mode attempts to detect if a stream that may be attached to a terminal. As such, it will not use colors for the `formatter::format` method and it may not be able to detect if some ostreams correspond to terminals or not. For this reason, `formatter::format` and `formatter::print` methods have overloads taking a color parameter. This color parameter will override configured color mode. The `symbols` option provides a few settings for pretty-printing symbol names: - `symbol_mode::full` default, uses the full demangled name - `symbol_mode::pretty` applies a number of transformations to clean up long symbol names. For example, it turns `std::__cxx11::basic_string, std::allocator >` into `std::string`. This is equivalent to `cpptrace::prettify_symbol`. - `symbol_mode::pruned` prunes demangled symbols by removing return types, template arguments, and function parameters. It also does some minimal normalization. For example, it prunes `ns::S::~S()` to `ns::S::~S`. If cpptrace is unable to parse the symbol it will use the full symbol. This is equivalent to `cpptrace::prune_symbol`. Recommended practice with formatters: It's generally preferable to create formatters objects that are long-lived rather than to create them on the fly every time a trace needs to be formatted. Cpptrace provides access to a formatter with default settings with `get_default_formatter`: ```cpp namespace cpptrace { const formatter& get_default_formatter(); } ``` ### Transforms A transform function can be specified for the formatter. This function is called before the configured `filter` is checked. For example: ```cpp auto formatter = cpptrace::formatter{} .transform([](cpptrace::stacktrace_frame frame) { frame.symbol = replace_all(frame, "std::__cxx11::", "std::"); return frame; }); ``` ## Configuration `cpptrace::absorb_trace_exceptions`: Configure whether the library silently absorbs internal exceptions and continues. Default is true. `cpptrace::enable_inlined_call_resolution`: Configure whether the library will attempt to resolve inlined call information for release builds. Default is true. `cpptrace::experimental::set_cache_mode`: Control time-memory tradeoffs within the library. By default speed is prioritized. If using this function, set the cache mode at the very start of your program before any traces are performed. ```cpp namespace cpptrace { void absorb_trace_exceptions(bool absorb); void enable_inlined_call_resolution(bool enable); enum class cache_mode { // Only minimal lookup tables prioritize_memory, // Build lookup tables but don't keep them around between trace calls hybrid, // Build lookup tables as needed prioritize_speed }; namespace experimental { void set_cache_mode(cache_mode mode); } } ``` ### Logging Cpptrace attempts to gracefully recover from any internal errors in order to provide the best information it can and not interfere with user applications. However, sometimes it's important to see what's going wrong inside cpptrace if anything does go wrong. To facilitate this, cpptrace has an internal logger. By default it doesn't log anything out. The following configurations that can be used to set a custom logging callback or enable logging to stderr: ```cpp namespace cpptrace { enum class log_level { debug, info, warning, error }; void set_log_level(log_level level); void set_log_callback(std::function); void use_default_stderr_logger(); } ``` `cpptrace::set_log_level`: Set cpptrace's internal log level. Default: `error`. Cpptrace currently only uses this log level internally. `cpptrace::set_log_callback`: Set the callback cpptrace uses for logging messages, useful for custom loggers. `cpptrace::use_default_stderr_logger`: Set's the logging callback to print to stderr. ## Traces From All Exceptions (`CPPTRACE_TRY` and `CPPTRACE_CATCH`) Cpptrace provides `CPPTRACE_TRY` and `CPPTRACE_CATCH` macros that allow a stack trace to be collected from the current thrown exception object, with no overhead in the non-throwing (happy) path: ```cpp #include #include void foo() { throw std::runtime_error("foo failed"); } int main() { CPPTRACE_TRY { foo(); } CPPTRACE_CATCH(const std::exception& e) { std::cerr<<"Exception: "<`. Any declarator `catch` accepts works with `CPPTRACE_CATCH`, including `...`. This works with any thrown object, not just `std::exceptions`. It even works with `throw 0;`! ![from_current](res/from_current.png) API functions: - `cpptrace::raw_trace_from_current_exception`: Returns `const raw_trace&` from the current exception. - `cpptrace::from_current_exception`: Returns a resolved `const stacktrace&` from the current exception. Invalidates references to traces returned by `cpptrace::raw_trace_from_current_exception`. In order to provide stack traces, cpptrace has to do some magic to be able to intercept C++ exception handling internals before the stack is unwound. For a simple `try`/`catch`, `CPPTRACE_TRY`/`CPPTRACE_CATCH` macros can be used. For a `try`/`catch` that has multiple handlers, `cpptrace::try_catch` can be used. I wish I could make a macro work, however, for multiple handlers this is the best way for cpptrace to inject the appropriate magic. E.g.: ```cpp cpptrace::try_catch( [&] { // try block foo(); }, [&] (const std::runtime_error& e) { std::cerr<<"Runtime error: "< [!IMPORTANT] > There is an unfortunate limitation with `return` statements in these try/catch macros: The implementation on Windows > requires wrapping the try body in an immediately-invoked lambda and as such `return` statements would return from the > lambda not the enclosing function. Cpptrace guards against misleading `return`s compiling by requiring the lambdas to > return a special internal type, but, if you're writing code that will be compiled on windows it's important to not > write `return` statements within CPPTRACE_TRY. For example, this is invalid: > ```cpp > CPPTRACE_TRY { > if(condition) return 40; // error, type int doesn't match cpptrace::detail::dont_return_from_try_catch_macros > } CPPTRACE_CATCH(const std::exception& e) { > ... > } > ``` > [!IMPORTANT] > There is a footgun which is mainly relevant for code that was written on an older version of cpptrace: It's possible > to write the following without getting errors > ```cpp > CPPTRACE_TRY { > ... > } CPPTRACE_CATCH(const std::runtime_error& e) { > ... > } catch(const std::exception& e) { > ... > } > ``` > This code will compile and the second catch handler will work, however, cpptrace won't know about the handler and as > such it won't be able to correctly collect a trace when a type that does not match `std::runtime_error` is thrown. No > run-time errors will occur, however, `from_current_exception` will report a misleading trace. ### Removing the `CPPTRACE_` prefix `CPPTRACE_TRY` is a little cumbersome to type. To remove the `CPPTRACE_` prefix you can use the `CPPTRACE_UNPREFIXED_TRY_CATCH` cmake option or the `CPPTRACE_UNPREFIXED_TRY_CATCH` preprocessor definition: ```cpp TRY { foo(); } CATCH(const std::exception& e) { std::cerr<<"Exception: "< Source Expansion ```cpp CPPTRACE_TRY { foo(); } CPPTRACE_CATCH(const std::exception& e) { ... } ``` ```cpp try { [&]() { __try { [&]() { foo(); }(); } __except(exception_filter( GetExceptionInformation() )) {} }(); } catch(const std::exception& e) { ... } ``` SEH's design actually makes it fairly easy to run code during the search phase. The exception filter will collect a trace if it detects the catch will match. Unfortunately, MSVC does not allow mixing C++ `try`/`catch` and SEH `__try`/`__except` in the same function so a double-IILE is needed. This has implications for returning from try blocks. On systems which use the Itanium ABI (linux, mac, etc), cpptrace's try/catch macros expand along the lines of:
Source Expansion
```cpp CPPTRACE_TRY { foo(); } CPPTRACE_CATCH(const std::exception& e) { ... } ``` ```cpp try { try { foo(); } catch(const unwind_interceptor_for&) {...} } catch(const std::exception& e) { ... } ```
Cpptrace does some magic to hook vtables of `unwind_interceptor_for` type_info objects during static-init time. N.b.: This mechanism is also discussed in [P2490R3][P2490R3]. ### Performance The performance impact in the non-throwing happy path is zero (or as close to zero as practical) on modern architectures. In the unhappy throwing path, a little more work may be done during the search phase to consider handlers cpptrace inserts but this is low-impact. Generating the trace itself is fast: Cpptrace collects a raw trace during exception handling and it is resolved only when requested. In my benchmarking I have found generation of raw traces to take on the order of `100ns` per frame. On some older architectures/ABIs (e.g., 32-bit windows), `try`/`catch` itself has some overhead due to how it is implemented with SEH. Cpptrace's `try`/`catch` macro adds one extra layer of handler which may be relevant on such systems but should not be a problem outside of hot loops, where using any `try`/`catch` is presumably already a problem on such architectures. ## Rethrowing Exceptions By default `cpptrace::from_current_exception` will correspond to a trace for the last `throw` intercepted by a `CPPTRACE_CATCH`. In order to rethrow an exception while preserving the original trace, `cpptrace::rethrow()` can be used. ```cpp namespace cpptrace { void rethrow(); void rethrow(std::exception_ptr exception = std::current_exception()); } ``` > [!NOTE] > It's important to use `cpptrace::rethrow()` from within a `CPPTRACE_CATCH`. If it is not, then no trace for the > exception origin will have been collected. Example: ```cpp void bar() { throw std::runtime_error("critical error in bar"); } void foo() { CPPTRACE_TRY { bar(); } CPPTRACE_CATCH(const std::exception& e) { std::cerr<<"Exception in foo: "< foo -> bar } } ``` Sometimes it may be desirable to see both the trace for the exception's origin as well as the trace for where it was rethrown. Cpptrace provides an interface for getting the last rethrow location: ```cpp namespace cpptrace { const raw_trace& raw_trace_from_current_exception_rethrow(); const stacktrace& from_current_exception_rethrow(); bool current_exception_was_rethrown(); } ``` If the current exception was not rethrown, these functions return references to empty traces. `current_exception_was_rethrown` can be used to check if the current exception was rethrown and a non-empty rethrow trace exists. Example usage, utilizing `foo` and `bar` from the above example: ```cpp int main() { CPPTRACE_TRY { foo(); } CPPTRACE_CATCH(const std::exception& e) { std::cerr<<"Exception encountered while running foo: "< foo -> bar std::cerr<<"Rethrown from:"< foo } } ``` ## `cpptrace::try_catch` As mentioned above, in order to facilitate `try`/`catch` blocks with multiple handlers while still being able to perform the magic necessary to collect stack traces on exceptions, cpptrace provides a `cpptrace::try_catch` utility that can take multiple handlers: ```cpp cpptrace::try_catch( [&] { // try block foo(); }, [&] (const std::runtime_error& e) { std::cerr<<"Runtime error: "< void try_catch(F&& f, Catches&&... catches); } ``` Similar to a language `try`/`catch`, `catch` handlers will be considered in the order they are listed. Handlers should take exactly one argument, equivalent to what would be written for a catch handler, except for `catch(...)` which can be achieved by a handler taking no arguments. ## Traces from SEH exceptions Similar to the above section on collecting [traces from C++ exceptions](#traces-from-all-exceptions-cpptrace_try-and-cpptrace_catch), cpptrace provides `CPPTRACE_SEH_TRY` and `CPPTRACE_SEH_EXCEPT` macros that collect traces from SEH exceptions on windows with no overhead in the non-throwing (happy) path: ```cpp #include #include #include void foo(int x, int y) { return x / y; } int divide_zero_filter(int code) { if(code == STATUS_INTEGER_DIVIDE_BY_ZERO || code == EXCEPTION_FLT_DIVIDE_BY_ZERO) { return EXCEPTION_EXECUTE_HANDLER; } return EXCEPTION_CONTINUE_SEARCH; } int main() { CPPTRACE_SEH_TRY { foo(10, 0); } CPPTRACE_SEH_EXCEPT(divide_zero_filter(GetExceptionCode())) { std::cerr<<"Division by zero happened!"<, more docs later mutable detail::lazy_trace_holder trace_holder; mutable std::string what_string; public: explicit lazy_exception( raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : trace_holder(std::move(trace)) {} const char* what() const noexcept override; const char* message() const noexcept override; const stacktrace& trace() const noexcept override; }; } ``` `cpptrace::lazy_exception` can be freely thrown or overridden. Generally `message()` is the only field to override. Lastly cpptrace provides an exception class that takes a user-provided message, `cpptrace::exception_with_message`, as well as a number of traced exception classes resembling ``: ```cpp namespace cpptrace { class exception_with_message : public lazy_exception { mutable std::string user_message; public: explicit exception_with_message( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {} const char* message() const noexcept override; }; // All stdexcept errors have analogs here. All but system_error have the constructor: // explicit the_error( // std::string&& message_arg, // raw_trace&& trace = detail::get_raw_trace_and_absorb() // ) noexcept // : exception_with_message(std::move(message_arg), std::move(trace)) {} class logic_error : public exception_with_message { ... }; class domain_error : public exception_with_message { ... }; class invalid_argument : public exception_with_message { ... }; class length_error : public exception_with_message { ... }; class out_of_range : public exception_with_message { ... }; class runtime_error : public exception_with_message { ... }; class range_error : public exception_with_message { ... }; class overflow_error : public exception_with_message { ... }; class underflow_error : public exception_with_message { ... }; class system_error : public runtime_error { public: explicit system_error( int error_code, std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept; const std::error_code& code() const noexcept; }; } ``` ### Wrapping std::exceptions > [!NOTE] > This section is largely obsolete now that cpptrace provides a better mechanism for collecting > [traces from exceptions](#traces-from-exceptions) Cpptrace exceptions can provide great information for user-controlled exceptions. For non-cpptrace::exceptions that may originate outside of code you control, e.g. the standard library, cpptrace provides some wrapper utilities that can rethrow these exceptions nested in traced cpptrace exceptions. The trace won't be perfect, the trace will start where the wrapper caught it, but these utilities can provide good diagnostic information. Unfortunately this is the best solution for this problem, as far as I know. ```cpp std::vector foo = {1, 2, 3}; CPPTRACE_WRAP_BLOCK( foo.at(4) = 2; foo.at(5)++; ); std::cout< [!NOTE] > This section pertains to cpptrace traced exception objects and not the mechanism for collecting > [traces from arbitrary exceptions](#traces-from-exceptions) Working with cpptrace exceptions in your code: ```cpp try { foo(); } catch(cpptrace::exception& e) { // Prints the exception info and stack trace, conditionally enabling color codes depending on // whether stderr is a terminal std::cerr << "Error: " << e.message() << '\n'; e.trace().print(std::cerr, cpptrace::isatty(cpptrace::stderr_fileno)); } catch(std::exception& e) { std::cerr << "Error: " << e.what() << '\n'; } ``` ## Terminate Handling Cpptrace provides a custom `std::terminate` handler that prints stacktraces while otherwise behaving like the normal `std::terminate` handler. If a cpptrace exception object reaches `std::terminate` the trace from that exception is printed, otherwise a stack trace is generated at the point of the terminate handler. Often `std::terminate` is called directly without unwinding so the trace is preserved. To register this custom handler: ```cpp cpptrace::register_terminate_handler(); ``` ## Signal-Safe Tracing Stack traces from signal handlers can provide very helpful information for debugging application crashes, e.g. from SIGSEGV or SIGTRAP handlers. Signal handlers are really restrictive environments as your application could be interrupted by a signal at any point, including in the middle of malloc or buffered IO or while holding a lock. Doing a stack trace in a signal handler is possible but it requires a lot of care. This is difficult to do correctly and most examples online do this incorrectly. Cpptrace offers an API to walk the stack in a signal handler and produce a raw trace safely. The library also provides an interface for producing a object frame safely: ```cpp namespace cpptrace { std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip = 0); std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth); struct safe_object_frame { frame_ptr raw_address; frame_ptr address_relative_to_object_start; char object_path[CPPTRACE_PATH_MAX + 1]; object_frame resolve() const; // To be called outside a signal handler. Not signal safe. }; void get_safe_object_frame(frame_ptr address, safe_object_frame* out); bool can_signal_safe_unwind(); bool can_get_safe_object_frame(); } ``` It is not possible to resolve debug symbols safely in the process from a signal handler without heroic effort. In order to produce a full trace there are three options: 1. Carefully save the object trace information to be resolved at a later time outside the signal handler 2. Write the object trace information to a file to be resolved later 3. Spawn a new process, communicate object trace information to that process, and have that process do the trace resolution For traces on segfaults, e.g., only options 2 and 3 are viable. For more information an implementation of approach 3, see the comprehensive overview and demo at [signal-safe-tracing.md](docs/signal-safe-tracing.md). > [!IMPORTANT] > Currently signal-safe stack unwinding is only possible with `libunwind`, which must be > [manually enabled](#library-back-ends). If signal-safe unwinding isn't supported, `safe_generate_raw_trace` will just > produce an empty trace. `can_signal_safe_unwind` can be used to check for signal-safe unwinding support and > `can_get_safe_object_frame` can be used to check `get_safe_object_frame` support. If object information can't be > resolved in a signal-safe way then `get_safe_object_frame` will not populate fields beyond the `raw_address`. > [!IMPORTANT] > `_dl_find_object` is required for signal-safe stack tracing. This is a relatively recent addition to glibc, added in > glibc 2.35. > [!CAUTION] > Calls to shared objects can be lazy-loaded where the first call to the shared object invokes non-signal-safe functions > such as `malloc()`. To avoid this, call these routines in `main()` ahead of a signal handler to "warm up" the library. ## Utility Types A couple utility types are used to provide the library with a good interface. `nullable` is used for a nullable integer type. Internally the maximum value for `T` is used as a sentinel. `std::optional` would be used if this library weren't c++11. But, `nullable` provides an `std::optional`-like interface and it's less heavy-duty for this use than an `std::optional`. `detail::lazy_trace_holder` is a utility type for `lazy_exception` used in place of an `std::variant`. ```cpp namespace cpptrace { template::value, int>::type = 0> struct nullable { T raw_value; // all members are constexpr for c++17 and beyond, some are constexpr before c++17 nullable& operator=(T value) bool has_value() const noexcept; T& value() noexcept; const T& value() const noexcept; T value_or(T alternative) const noexcept; void swap(nullable& other) noexcept; void reset() noexcept; bool operator==(const nullable& other) const noexcept; bool operator!=(const nullable& other) const noexcept; constexpr static T null_value() noexcept; // returns the raw null value constexpr static nullable null() noexcept; // returns a null instance }; namespace detail { class lazy_trace_holder { bool resolved; union { raw_trace trace; stacktrace resolved_trace; }; public: // constructors lazy_trace_holder() : trace() {} explicit lazy_trace_holder(raw_trace&& _trace); explicit lazy_trace_holder(stacktrace&& _resolved_trace); // logistics lazy_trace_holder(const lazy_trace_holder& other); lazy_trace_holder(lazy_trace_holder&& other) noexcept; lazy_trace_holder& operator=(const lazy_trace_holder& other); lazy_trace_holder& operator=(lazy_trace_holder&& other) noexcept; ~lazy_trace_holder(); // access const raw_trace& get_raw_trace() const; stacktrace& get_resolved_trace(); const stacktrace& get_resolved_trace() const; // throws if not already resolved bool is_resolved() const; private: void clear(); }; } } ``` ## Headers Cpptrace provides a handful of headers to make inclusion more minimal. | Header | Contents | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cpptrace/forward.hpp` | `cpptrace::frame_ptr` and a few trace class forward declarations | | `cpptrace/basic.hpp` | Definitions for trace classes and the basic tracing APIs ([Stack Traces](#stack-traces), [Object Traces](#object-traces), [Raw Traces](#raw-traces), and [Signal-Safe Tracing](#signal-safe-tracing)) | | `cpptrace/exceptions.hpp` | [Traced Exception Objects](#traced-exception-objects) and related utilities ([Wrapping std::exceptions](#wrapping-stdexceptions)) | | `cpptrace/from_current.hpp` | [Traces From All Exceptions](#traces-from-all-exceptions) | | `cpptrace/io.hpp` | `operator<<` overloads for `std::ostream` and `std::formatter`s | | `cpptrace/formatting.hpp` | Configurable formatter API | | `cpptrace/utils.hpp` | Utility functions, configuration functions, and terminate utilities ([Utilities](#utilities), [Configuration](#configuration), and [Terminate Handling](#terminate-handling)) | | `cpptrace/version.hpp` | Library version macros | | `cpptrace/gdb_jit.hpp` | Provides a special utility related to [JIT support](#jit-support) | The main cpptrace header is `cpptrace/cpptrace.hpp` which includes everything other than `from_current.hpp` and `version.hpp`. ## Libdwarf Tuning For extraordinarily large binaries (multiple gigabytes), cpptrace's internal caching can result in a lot of memory usage. Cpptrace provides some options to reduce memory usage in exchange for performance in memory-constrained applications. Synopsis: ```cpp namespace cpptrace { namespace experimental { void set_dwarf_resolver_line_table_cache_size(nullable max_entries); void set_dwarf_resolver_disable_aranges(bool disable); } } ``` Explanation: - `set_dwarf_resolver_line_table_cache_size` can be used to set a limit to the cache size with evictions done LRU. Cpptrace loads and caches line tables for dwarf compile units. These can take a lot of space for large binaries with lots of debug info. Passing `nullable::null()` will disable the cache size (which is the default behavior). - `set_dwarf_resolver_disable_aranges` can be used to disable use of dwarf `.debug_aranges`, an accelerated range lookup table for compile units emitted by many compilers. Cpptrace uses these by default if they are present since they can speed up resolution, however, they can also result in significant memory usage. ## JIT Support Cpptrace has support for resolving symbols from frames in JIT-compiled code. To do this, cpptrace relies on in-memory object files (elf on linux or mach-o on mac) that contain symbol tables and dwarf debug information. The main reason for this is many JIT implementations already produce these for debugger support. These in-memory object files must be set up in such a way that the symbol table and debug symbol addresses match the run-time addresses of the JIT code. The basic interface for informing cpptrace about these in-memory object files is as follows: ```cpp namespace cpptrace { void register_jit_object(const char*, std::size_t); void unregister_jit_object(const char*); void clear_all_jit_objects(); } ``` Many JIT implementations follow the GDB [JIT Compilation Interface][jitci] so that JIT code can be debugged. The interface, at a high level, entails adding in-memory object files to a linked list of object files that GDB and other debuggers can reference (stored in the `__jit_debug_descriptor`). Cpptrace provides, as a utility, a mechanism for loading all in-memory object files present in the `__jit_debug_descriptor` linked list via ``: ```cpp namespace cpptrace { namespace experimental { void register_jit_objects_from_gdb_jit_interface(); } } ``` Note: Your program must be able to link against a global C symbol `__jit_debug_descriptor`. Note: Calling `cpptrace::experimental::register_jit_objects_from_gdb_jit_interface` clears all jit objects previously registered with cpptrace. [jitci]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html ## Loading Libraries at Runtime This section only applies to the dbghelp backend (`CPPTRACE_GET_SYMBOLS_WITH_DBGHELP`) on Windows. When loading a DLL at runtime with `LoadLibrary` after a stacktrace has already been generated, symbols from that library may not be resolved correctly for subsequent stacktraces. To fix this, call `cpptrace::load_symbols_for_file` with the same path that was passed to `LoadLibrary`. ```cpp HMODULE hModule = LoadLibrary("mydll.dll"); if (hModule) { cpptrace::load_symbols_for_file("mydll.dll"); } ``` For backends other than dbghelp, `load_symbols_for_file` does nothing. For platforms other than Windows, it is not declared. ```cpp namespace cpptrace { void load_symbols_for_file(const std::string& filename); } ``` # ABI Versioning Since cpptrace v1.0.0, the library uses an inline ABI versioning namespace and all symbols part of the public interface are secretly under the namespace `cpptrace::v1`. This is done to allow for potential future library evolution in an ABI-friendly manner. # Supported Debug Formats | Format | Supported | | ---------------------------- | --------- | | DWARF in binary | âœ”ï¸ | | GNU debug link | ï¸ï¸âœ”ï¸ | | Split dwarf (debug fission) | âœ”ï¸ | | DWARF in dSYM | âœ”ï¸ | | DWARF via Mach-O debug map | âœ”ï¸ | | Windows debug symbols in PDB | âœ”ï¸ | DWARF5 added DWARF package files. As far as I can tell no compiler implements these yet. # How to Include The Library ## CMake FetchContent With CMake FetchContent: ```cmake include(FetchContent) FetchContent_Declare( cpptrace GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git GIT_TAG v1.0.4 # ) FetchContent_MakeAvailable(cpptrace) target_link_libraries(your_target cpptrace::cpptrace) ``` It's as easy as that. Cpptrace will automatically configure itself for your system. Note: On windows and macos some extra work is required, see [Platform Logistics](#platform-logistics) below. Be sure to configure with `-DCMAKE_BUILD_TYPE=Debug` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` for symbols and line information. ## System-Wide Installation ```sh git clone https://github.com/jeremy-rifkin/cpptrace.git git checkout v1.0.4 mkdir cpptrace/build cd cpptrace/build cmake .. -DCMAKE_BUILD_TYPE=Release make -j sudo make install ``` Using through cmake: ```cmake find_package(cpptrace REQUIRED) target_link_libraries( cpptrace::cpptrace) ``` Be sure to configure with `-DCMAKE_BUILD_TYPE=Debug` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` for symbols and line information. Or compile with `-lcpptrace`: ```sh g++ main.cpp -o main -g -Wall -lcpptrace ./main ``` > [!IMPORTANT] > If you aren't using cmake and are linking statically you must manually specify `-DCPPTRACE_STATIC_DEFINE`. If you get an error along the lines of ``` error while loading shared libraries: libcpptrace.so: cannot open shared object file: No such file or directory ``` You may have to run `sudo /sbin/ldconfig` to create any necessary links and update caches so the system can find libcpptrace.so (I had to do this on Ubuntu). Only when installing system-wide. Usually your package manager does this for you when installing new libraries. > [!NOTE] > Libdwarf requires a relatively new version of libdwarf. Sometimes a previously-installed system-wide libdwarf may > cause issues due to being too old. Libdwarf 8 and newer is known to work.
System-wide install on windows ```ps1 git clone https://github.com/jeremy-rifkin/cpptrace.git git checkout v1.0.4 mkdir cpptrace/build cd cpptrace/build cmake .. -DCMAKE_BUILD_TYPE=Release msbuild cpptrace.sln msbuild INSTALL.vcxproj ``` Note: You'll need to run as an administrator in a developer powershell, or use vcvarsall.bat distributed with visual studio to get the correct environment variables set.
## Local User Installation To install just for the local user (or any custom prefix): ```sh git clone https://github.com/jeremy-rifkin/cpptrace.git git checkout v1.0.4 mkdir cpptrace/build cd cpptrace/build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/wherever make -j make install ``` Using through cmake: ```cmake find_package(cpptrace REQUIRED PATHS $ENV{HOME}/wherever) target_link_libraries( cpptrace::cpptrace) ``` Using manually: ``` g++ main.cpp -o main -g -Wall -I$HOME/wherever/include -L$HOME/wherever/lib -lcpptrace ``` > [!IMPORTANT] > If you aren't using cmake and are linking statically you must manually specify `-DCPPTRACE_STATIC_DEFINE`. ## Use Without CMake To use the library without cmake first follow the installation instructions at [System-Wide Installation](#system-wide-installation), [Local User Installation](#local-user-installation), or [Package Managers](#package-managers). In addition to any include or library paths you'll need to specify to tell the compiler where cpptrace was installed. The typical dependencies for cpptrace are: | Compiler | Platform | Dependencies | | ----------------------- | ---------------- | ----------------------------------------- | | gcc, clang, intel, etc. | Linux/macos/unix | `-lcpptrace -ldwarf -lz -lzstd -ldl` | | gcc | Windows | `-lcpptrace -ldbghelp -ldwarf -lz -lzstd` | | msvc | Windows | `cpptrace.lib dbghelp.lib` | | clang | Windows | `-lcpptrace -ldbghelp` | Note: Newer libdwarf requires `-lzstd`, older libdwarf does not. > [!IMPORTANT] > If you are linking statically, you will additionally need to specify `-DCPPTRACE_STATIC_DEFINE`. Dependencies may differ if different back-ends are manually selected. ## Installation Without Package Managers or FetchContent Some users may prefer, or need to, to install cpptrace without package managers or fetchcontent (e.g. if their system does not have internet access). Below are instructions for how to install libdwarf and cpptrace.
Installation Without Package Managers or FetchContent Here is an example for how to build cpptrace and libdwarf. `~/scratch/cpptrace-test` is used as a working directory and the libraries are installed to `~/scratch/cpptrace-test/resources`. ```sh mkdir -p ~/scratch/cpptrace-test/resources cd ~/scratch/cpptrace-test git clone https://github.com/facebook/zstd.git cd zstd git checkout 63779c798237346c2b245c546c40b72a5a5913fe cd build/cmake mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=~/scratch/cpptrace-test/resources -DZSTD_BUILD_PROGRAMS=On -DZSTD_BUILD_CONTRIB=On -DZSTD_BUILD_TESTS=On -DZSTD_BUILD_STATIC=On -DZSTD_BUILD_SHARED=On -DZSTD_LEGACY_SUPPORT=On make -j make install cd ~/scratch/cpptrace-test git clone https://github.com/jeremy-rifkin/libdwarf-lite.git cd libdwarf-lite git checkout 5dfb2cd2aacf2bf473e5bfea79e41289f88b3a5f # 2.1.0 mkdir build cd build cmake .. -DPIC_ALWAYS=On -DBUILD_DWARFDUMP=Off -DCMAKE_PREFIX_PATH=~/scratch/cpptrace-test/resources -DCMAKE_INSTALL_PREFIX=~/scratch/cpptrace-test/resources make -j make install cd ~/scratch/cpptrace-test git clone https://github.com/jeremy-rifkin/cpptrace.git cd cpptrace git checkout v1.0.4 mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DCPPTRACE_USE_EXTERNAL_LIBDWARF=On -DCMAKE_PREFIX_PATH=~/scratch/cpptrace-test/resources -DCMAKE_INSTALL_PREFIX=~/scratch/cpptrace-test/resources make -j make install ``` The `~/scratch/cpptrace-test/resources` directory also serves as a bundle you can ship with all the installed files for cpptrace and its dependencies.
## Package Managers ### Conan Cpptrace is available through conan at https://conan.io/center/recipes/cpptrace. ``` [requires] cpptrace/1.0.4 [generators] CMakeDeps CMakeToolchain [layout] cmake_layout ``` ```cmake # ... find_package(cpptrace REQUIRED) # ... target_link_libraries(YOUR_TARGET cpptrace::cpptrace) ``` ### Vcpkg ``` vcpkg install cpptrace ``` ```cmake find_package(cpptrace CONFIG REQUIRED) target_link_libraries(main PRIVATE cpptrace::cpptrace) ``` ## C++20 Modules Cpptrace supports C++20 modules: `import cpptrace;`. You'll need a modern toolchain in order to use C++20 modules (i.e. relatively new compilers, cmake, etc). For features involving macros you will have to `#include` headers with the macro definitions: - ``: `CPPTRACE_WRAP` and `CPPTRACE_WRAP_BLOCK` - ``: `CPPTRACE_TRY`, `CPPTRACE_CATCH`, etc. # Platform Logistics Windows and macOS require a little extra work to get everything in the right place. ## Windows Copying the library `.dll` on Windows: ```cmake # Copy the cpptrace.dll on windows to the same directory as the executable for your_target. # Not required if static linking. if(WIN32) add_custom_command( TARGET your_target POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endif() ``` ## macOS On macOS, it is recommended to generate a `dSYM` file containing debug information for your program. This is not required as cpptrace makes a good effort at finding and reading the debug information without this, but having a `dSYM` file is the most robust method. When using Xcode with CMake, this can be done with: ```cmake set_target_properties(your_target PROPERTIES XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym") ``` Outside of Xcode, this can be done with `dsymutil yourbinary`: ```cmake # Create a .dSYM file on macOS if(APPLE) add_custom_command( TARGET your_target POST_BUILD COMMAND dsymutil $ ) endif() ``` # Library Back-Ends Cpptrace supports a number of back-ends to produce stack traces. Stack traces are produced in roughly three steps: Unwinding, symbol resolution, and demangling. The library's CMake automatically configures itself for what your system supports. The ideal configuration is as follows: | Platform | Unwinding | Symbols | Demangling | | -------- | ------------------------------------------------------- | ------------------ | -------------------- | | Linux | `_Unwind` | libdwarf | cxxabi.h | | MacOS | `_Unwind` for gcc, execinfo.h for clang and apple clang | libdwarf | cxxabi.h | | Windows | `StackWalk64` | dbghelp | No demangling needed | | MinGW | `StackWalk64` | libdwarf + dbghelp | cxxabi.h | Support for these back-ends is the main development focus and they should work well. If you want to use a different back-end such as addr2line, for example, you can configure the library to do so. **Unwinding** | Library | CMake config | Platforms | Info | | ------------- | -------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | libgcc unwind | `CPPTRACE_UNWIND_WITH_UNWIND` | linux, macos, mingw | Frames are captured with libgcc's `_Unwind_Backtrace`, which currently produces the most accurate stack traces on gcc/clang/mingw. Libgcc is often linked by default, and llvm has something equivalent. | | execinfo.h | `CPPTRACE_UNWIND_WITH_EXECINFO` | linux, macos | Frames are captured with `execinfo.h`'s `backtrace`, part of libc on linux/unix systems. | | winapi | `CPPTRACE_UNWIND_WITH_WINAPI` | windows, mingw | Frames are captured with `CaptureStackBackTrace`. | | dbghelp | `CPPTRACE_UNWIND_WITH_DBGHELP` | windows, mingw | Frames are captured with `StackWalk64`. | | libunwind | `CPPTRACE_UNWIND_WITH_LIBUNWIND` | linux, macos, windows, mingw | Frames are captured with [libunwind](https://github.com/libunwind/libunwind). **Note:** This is the only back-end that requires a library to be installed by the user, and a `CMAKE_PREFIX_PATH` may also be needed. | | N/A | `CPPTRACE_UNWIND_WITH_NOTHING` | all | Unwinding is not done, stack traces will be empty. | Some back-ends (execinfo and `CaptureStackBackTrace`) require a fixed buffer has to be created to read addresses into while unwinding. By default the buffer can hold addresses for 400 frames (beyond the `skip` frames). This is configurable with `CPPTRACE_HARD_MAX_FRAMES`. **Symbol resolution** | Library | CMake config | Platforms | Info | | ------------ | ---------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | libdwarf | `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF` | linux, macos, mingw | Libdwarf is the preferred method for symbol resolution for cpptrace. Cpptrace will get it via FetchContent or find_package depending on `CPPTRACE_USE_EXTERNAL_LIBDWARF`. | | dbghelp | `CPPTRACE_GET_SYMBOLS_WITH_DBGHELP` | windows | Dbghelp.h is the preferred method for symbol resolution on windows under msvc/clang and is supported on all windows machines. | | libbacktrace | `CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE` | linux, macos*, mingw* | Libbacktrace is already installed on most systems or available through the compiler directly. For clang you must specify the absolute path to `backtrace.h` using `CPPTRACE_BACKTRACE_PATH`. | | addr2line | `CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE` | linux, macos, mingw | Symbols are resolved by invoking `addr2line` (or `atos` on mac) via `fork()` (on linux/unix, and `popen` under mingw). | | libdl | `CPPTRACE_GET_SYMBOLS_WITH_LIBDL` | linux, macos | Libdl uses dynamic export information. Compiling with `-rdynamic` is needed for symbol information to be retrievable. Line numbers won't be retrievable. | | N/A | `CPPTRACE_GET_SYMBOLS_WITH_NOTHING` | all | No attempt is made to resolve symbols. | *: Requires installation One back-end should be used. For MinGW `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF` and `CPPTRACE_GET_SYMBOLS_WITH_DBGHELP` can be used in conjunction. Note for addr2line: By default cmake will resolve an absolute path to addr2line to bake into the library. This path can be configured with `CPPTRACE_ADDR2LINE_PATH`, or `CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH` can be used to have the library search the system path for `addr2line` at runtime. This is not the default to prevent against path injection attacks. **Demangling** Lastly, depending on other back-ends used a demangler back-end may be needed. | Library | CMake config | Platforms | Info | | --------- | -------------------------------- | ------------------- | ---------------------------------------------------------------------------------- | | cxxabi.h | `CPPTRACE_DEMANGLE_WITH_CXXABI` | Linux, macos, mingw | Should be available everywhere other than [msvc](https://godbolt.org/z/93ca9rcdz). | | dbghelp.h | `CPPTRACE_DEMANGLE_WITH_WINAPI` | Windows | Demangle with `UnDecorateSymbolName`. | | N/A | `CPPTRACE_DEMANGLE_WITH_NOTHING` | all | Don't attempt to do anything beyond what the symbol resolution back-end does. | **More?** There are plenty more libraries that can be used for unwinding, parsing debug information, and demangling. In the future more back-ends can be added. Ideally this library can "just work" on systems, without additional installation work. ## Summary of Library Configurations Summary of all library configuration options: Back-ends: - `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF=On/Off` - `CPPTRACE_GET_SYMBOLS_WITH_DBGHELP=On/Off` - `CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE=On/Off` - `CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On/Off` - `CPPTRACE_GET_SYMBOLS_WITH_LIBDL=On/Off` - `CPPTRACE_GET_SYMBOLS_WITH_NOTHING=On/Off` - `CPPTRACE_UNWIND_WITH_UNWIND=On/Off` - `CPPTRACE_UNWIND_WITH_LIBUNWIND=On/Off` - `CPPTRACE_UNWIND_WITH_EXECINFO=On/Off` - `CPPTRACE_UNWIND_WITH_WINAPI=On/Off` - `CPPTRACE_UNWIND_WITH_DBGHELP=On/Off` - `CPPTRACE_UNWIND_WITH_NOTHING=On/Off` - `CPPTRACE_DEMANGLE_WITH_CXXABI=On/Off` - `CPPTRACE_DEMANGLE_WITH_WINAPI=On/Off` - `CPPTRACE_DEMANGLE_WITH_NOTHING=On/Off` Back-end configuration: - `CPPTRACE_BACKTRACE_PATH=`: Path to libbacktrace backtrace.h, needed when compiling with clang/ - `CPPTRACE_HARD_MAX_FRAMES=`: Some back-ends write to a fixed-size buffer. This is the size of that buffer. Default is `400`. - `CPPTRACE_ADDR2LINE_PATH=`: Specify the absolute path to the addr2line binary for cpptrace to invoke. By default the config script will search for a binary and use that absolute path (this is to prevent against path injection). - `CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH=On/Off`: Specifies whether cpptrace should let the system search the PATH environment variable directories for the binary. Other useful configurations: - `CPPTRACE_BUILD_SHARED=On/Off`: Override for `BUILD_SHARED_LIBS`. - `CPPTRACE_INCLUDES_WITH_SYSTEM=On/Off`: Marks cpptrace headers as `SYSTEM` which will hide any warnings that aren't the fault of your project. Defaults to On. - `CPPTRACE_INSTALL_CMAKEDIR`: Override for the installation path for the cmake configs. - `CPPTRACE_USE_EXTERNAL_LIBDWARF=On/Off`: Get libdwarf from `find_package` rather than `FetchContent`. - `CPPTRACE_POSITION_INDEPENDENT_CODE=On/Off`: Compile the library as a position independent code (PIE). Defaults to On. - `CPPTRACE_STD_FORMAT=On/Off`: Control inclusion of `` and provision of `std::formatter` specializations by cpptrace.hpp. This can also be controlled with the macro `CPPTRACE_NO_STD_FORMAT`. Testing: - `CPPTRACE_BUILD_TESTING` Build small demo and test program - `CPPTRACE_BUILD_TEST_RDYNAMIC` Use `-rdynamic` when compiling the test program # Testing Methodology Cpptrace currently uses integration and functional testing, building and running under every combination of back-end options. The implementation is based on [github actions matrices][1] and driven by python scripts located in the [`ci/`](ci/) folder. Testing used to be done by github actions matrices directly, however, launching hundreds of two second jobs was extremely inefficient. Test outputs are compared against expected outputs located in [`test/expected/`](test/expected/). Stack trace addresses may point to the address after an instruction depending on the unwinding back-end, and the python script will check for an exact or near-match accordingly. [1]: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs # Notes About the Library For the most part I'm happy with the state of the library. But I'm sure that there is room for improvement and issues will exist. If you encounter any issue, please let me know! If you find any pain-points in the library, please let me know that too. A note about performance: For handling of DWARF symbols there is a lot of room to explore for performance optimizations and time-memory tradeoffs. If you find the current implementation is either slow or using too much memory, I'd be happy to explore some of these options. A couple things I'd like to improve in the future: - On Windows when collecting symbols with dbghelp (msvc/clang) parameter types are almost perfect but due to limitations in dbghelp the library cannot accurately show const and volatile qualifiers or rvalue references (these appear as pointers). # FAQ ## What about C++23 ``? Some day C++23's `` will be ubiquitous. And maybe one day the msvc implementation will be acceptable. The original motivation for cpptrace was to support projects using older C++ standards and as the library has grown its functionality has extended beyond the standard library's implementation. Cpptrace provides functionality beyond what the standard library provides and what implementations provide, such as: - Walking inlined function calls - Providing a lightweight interface for "raw traces" - Resolving function parameter types - Providing traced exception objects - Providing an API for signal-safe stacktrace generation - Providing a way to retrieve stack traces from arbitrary exceptions, not just special cpptrace traced exception objects. This is a feature coming to C++26, but cpptrace provides a solution for C++11. ## What does cpptrace have over other C++ stacktrace libraries? Other C++ stacktrace libraries, such as boost stacktrace and backward-cpp, fall short when it comes to portability and ease of use. In testing, I found neither to provide adequate coverage of various environments. Even when they can be made to work in an environment they require manual configuration from the end-user, possibly requiring manual installation of third-party dependencies. This is a highly undesirable burden to impose on users, especially when it is for a software package which just provides diagnostics as opposed to core functionality. Additionally, cpptrace provides support for resolving inlined calls by default for DWARF symbols (boost does not do this, backward-cpp can do this but only for some back-ends), better support for resolving full function signatures, and nicer API, among other features. ## I'm getting undefined standard library symbols like `std::__1::basic_string` on MacOS If you see a linker error along the lines of the following on MacOS then it's highly likely you are mixing standard library ABIs. ``` Undefined symbols for architecture arm64: "std::__1::basic_string, std::__1::allocator >::find(char, unsigned long) const", referenced from: cpptrace::detail::demangle(std::__1::basic_string, std::__1::allocator > const&, bool) in libcpptrace.a(demangle_with_cxxabi.cpp.o) cpptrace::detail::snippet_manager::build_line_table() in libcpptrace.a(snippet.cpp.o) ``` This can happen when using apple clang to compile cpptrace and gcc to compile your code, or vice versa. The reason is that apple clang defaults to libc++ and gcc defaults to libstdc++ and these two standard library implementations are not ABI-compatible. To resolve this, ensure you are compiling both cpptrace and your code with the same standard library by either using the same compiler for both or using `-stdlib=libc++`/`-stdlib=libstdc++` to control which standard library is used. # Contributing I'm grateful for the help I've received with this library and I welcome contributions! For information on contributing please refer to [CONTRIBUTING.md](./CONTRIBUTING.md). # License This library is under the MIT license. Cpptrace uses libdwarf on linux, macos, and mingw/cygwin unless configured to use something else. If this library is statically linked with libdwarf then the library's binary will itself be LGPL. [P2490R3]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2490r3.html cpptrace-1.0.4/SECURITY.md000066400000000000000000000007521504061443700151160ustar00rootroot00000000000000# Security Policy We take security seriously and I'm grateful for reports of security vulnerabilities. ## Reporting a Vulnerability If the vulnerability can be reported without revealing exploitable specifics, please open an issue. If the vulnerability can't be reported publicly without leaving an obvious exploit in the public eye please email me at jeremy@rifkin.dev or reach out to me on [discord](https://discord.gg/7kv5AuCndG). I will do my best to get back to you within a day. cpptrace-1.0.4/WORKSPACE000066400000000000000000000000351504061443700146000ustar00rootroot00000000000000workspace(name = "cpptrace") cpptrace-1.0.4/benchmarking/000077500000000000000000000000001504061443700157515ustar00rootroot00000000000000cpptrace-1.0.4/benchmarking/CMakeLists.txt000066400000000000000000000011701504061443700205100ustar00rootroot00000000000000include(CTest) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set( warning_options ${warning_options} $<$:-Wno-infinite-recursion> ) include(FetchContent) set(BENCHMARK_ENABLE_TESTING OFF) FetchContent_Declare( googlebench GIT_REPOSITORY "https://github.com/google/benchmark.git" GIT_TAG 12235e24652fc7f809373e7c11a5f73c5763fc4c # v1.9.0 ) FetchContent_MakeAvailable(googlebench) add_executable(benchmark_unwinding unwinding.cpp) target_compile_features(benchmark_unwinding PRIVATE cxx_std_20) target_link_libraries(benchmark_unwinding PRIVATE ${target_name} benchmark::benchmark) cpptrace-1.0.4/benchmarking/unwinding.cpp000066400000000000000000000023561504061443700204650ustar00rootroot00000000000000#include #include #include struct unwind_benchmark_info { benchmark::State& state; size_t& stack_depth; }; void unwind_loop(unwind_benchmark_info info) { auto& [state, depth] = info; depth = cpptrace::generate_raw_trace().frames.size(); for(auto _ : state) { benchmark::DoNotOptimize(cpptrace::generate_raw_trace()); } } void foo(unwind_benchmark_info info, int n) { if(n == 0) { unwind_loop(info); } else { foo(info, n - 1); } } template void foo(unwind_benchmark_info info, int, Args... args) { foo(info, args...); } void function_two(unwind_benchmark_info info, int, float) { foo(info, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } void function_one(unwind_benchmark_info info, int) { function_two(info, 0, 0); } static void unwinding(benchmark::State& state) { size_t stack_depth = 0; function_one({state, stack_depth}, 0); static bool did_print = false; if(!did_print) { did_print = true; std::cerr<<"[info] Unwinding benchmark stack depth: "< %temp%\vcvars.txt" cpptrace-1.0.4/ci/setup-prerequisites-mingw.ps1000066400000000000000000000013651504061443700215270ustar00rootroot00000000000000mkdir zstd cd zstd git init git remote add origin https://github.com/facebook/zstd.git git fetch --depth 1 origin 63779c798237346c2b245c546c40b72a5a5913fe # 1.5.5 git checkout FETCH_HEAD cd build/cmake mkdir build cd build cmake .. -DZSTD_BUILD_SHARED=On -DZSTD_BUILD_SHARED=Off -DZSTD_LEGACY_SUPPORT=Off -DZSTD_BUILD_PROGRAMS=Off -DZSTD_BUILD_CONTRIB=Off -DZSTD_BUILD_TESTS=Off -G"Unix Makefiles" make -j make install cd ../../../.. mkdir libdwarf cd libdwarf git init git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git git fetch --depth 1 origin 5dfb2cd2aacf2bf473e5bfea79e41289f88b3a5f # 2.1.0 git checkout FETCH_HEAD mkdir build cd build cmake .. -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE -G"Unix Makefiles" make -j make install cpptrace-1.0.4/ci/setup-prerequisites-unittest-macos.sh000077500000000000000000000025401504061443700232730ustar00rootroot00000000000000#!/bin/bash mkdir zstd cd zstd git init git remote add origin https://github.com/facebook/zstd.git git fetch --depth 1 origin 794ea1b0afca0f020f4e57b6732332231fb23c70 # 1.5.6 git checkout FETCH_HEAD make -j sudo make install cd .. mkdir libdwarf cd libdwarf git init git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git git fetch --depth 1 origin 5dfb2cd2aacf2bf473e5bfea79e41289f88b3a5f # 2.1.0 git checkout FETCH_HEAD mkdir build cd build cmake .. -GNinja -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE sudo ninja install cd ../.. mkdir googletest cd googletest git init git remote add origin https://github.com/google/googletest.git git fetch --depth 1 origin f8d7d77c06936315286eb55f8de22cd23c188571 git checkout FETCH_HEAD mkdir build cd build cmake .. -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install sudo ninja install rm -rf * # There's a false-positive container-overflow for apple clang/relwithdebinfo/sanitizers=on if gtest isn't built with # sanitizers. https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow#false-positives cmake .. -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=/tmp/gtest_asan_install -DCMAKE_CXX_FLAGS=-fsanitize=address sudo ninja install rm -rf * cmake .. -GNinja -DCMAKE_CXX_COMPILER=g++-12 -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install_gcc sudo ninja install cpptrace-1.0.4/ci/setup-prerequisites-unittest.sh000077500000000000000000000017561504061443700222030ustar00rootroot00000000000000#!/bin/bash mkdir zstd cd zstd git init git remote add origin https://github.com/facebook/zstd.git git fetch --depth 1 origin 794ea1b0afca0f020f4e57b6732332231fb23c70 # 1.5.6 git checkout FETCH_HEAD make -j sudo make install cd .. mkdir libdwarf cd libdwarf git init git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git git fetch --depth 1 origin 5dfb2cd2aacf2bf473e5bfea79e41289f88b3a5f # 2.1.0 git checkout FETCH_HEAD mkdir build cd build cmake .. -GNinja -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE sudo ninja install cd ../.. mkdir googletest cd googletest git init git remote add origin https://github.com/google/googletest.git git fetch --depth 1 origin f8d7d77c06936315286eb55f8de22cd23c188571 git checkout FETCH_HEAD mkdir build cd build cmake .. -GNinja -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install sudo ninja install rm -rf * cmake .. -GNinja -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install_libcxx sudo ninja install cpptrace-1.0.4/ci/setup-prerequisites.sh000077500000000000000000000010731504061443700203160ustar00rootroot00000000000000#!/bin/bash sudo apt install libgtest-dev mkdir zstd cd zstd git init git remote add origin https://github.com/facebook/zstd.git git fetch --depth 1 origin 794ea1b0afca0f020f4e57b6732332231fb23c70 # 1.5.6 git checkout FETCH_HEAD make -j sudo make install cd .. mkdir libdwarf cd libdwarf git init git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git git fetch --depth 1 origin 5dfb2cd2aacf2bf473e5bfea79e41289f88b3a5f # 2.1.0 git checkout FETCH_HEAD mkdir build cd build cmake .. -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE make -j sudo make install cpptrace-1.0.4/ci/speedtest.py000066400000000000000000000024211504061443700162650ustar00rootroot00000000000000import sys import re import platform def main(): output = sys.stdin.read() print(output) print("-" * 50) time = int(re.search(r"\d+ tests? from \d+ test suites? ran. \((\d+) ms total\)", output).group(1)) dwarf4 = any(["DWARF4" in arg for arg in sys.argv[1:]]) dwarf5 = any(["DWARF5" in arg for arg in sys.argv[1:]]) clang = any(["clang" in arg for arg in sys.argv[1:]]) # Somehow -gdwarf-4 clang is fast after a completely unrelated PR? Weird. Something to do with static linking...? # https://github.com/jeremy-rifkin/cpptrace/pull/22 expect_slow = False if platform.system() == "Windows": # For some reason SymInitialize is super slow on github's windows runner and it alone takes 250ms. Nothing we # can do about that. threshold = 350 # ms else: threshold = 100 # ms if expect_slow: if time > threshold: print(f"Success (expecting slow): Test program took {time} ms") else: print(f"Error (expecting slow): Test program took {time} ms") sys.exit(1) else: if time > threshold: print(f"Error: Test program took {time} ms") sys.exit(1) else: print(f"Success: Test program took {time} ms") main() cpptrace-1.0.4/ci/test-all-configs.py000066400000000000000000000426301504061443700174460ustar00rootroot00000000000000import argparse import os import platform import shutil import subprocess import sys from typing import Tuple from colorama import Fore, Back, Style from util import * sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../test/expected/") def get_c_compiler_counterpart(compiler: str) -> str: return compiler.replace("clang++", "clang").replace("g++", "gcc") MAX_LINE_DIFF = 2 def similarity(name: str, target: List[str]) -> int: parts = name.split(".txt")[0].split(".") c = 0 for part in parts: if part in target: c += 1 else: return -1 return c def output_matches(raw_output: str, params: Tuple[str]): target = [] if params[0].startswith("gcc") or params[0].startswith("g++"): target.append("gcc") elif params[0].startswith("clang"): target.append("clang") elif params[0].startswith("cl"): target.append("msvc") if platform.system() == "Windows": target.append("windows") elif platform.system() == "Darwin": target.append("macos") else: target.append("linux") other_configs = params[1:] for config in other_configs: assert "WITH_" in config target.append(config.split("WITH_")[1].lower()) print(f"Searching for expected file best matching {target}") files = [f for f in os.listdir(expected_dir) if os.path.isfile(os.path.join(expected_dir, f))] if len(files) == 0: print(f"Error: No expected files to use (searching {expected_dir})") sys.exit(1) files = list(map(lambda f: (f, similarity(f, target)), files)) m = max(files, key=lambda entry: entry[1])[1] if m <= 0: print(f"Error: Could not find match for {target} in {files}") sys.exit(1) files = [entry[0] for entry in files if entry[1] == m] if len(files) > 1: print(f"Error: Ambiguous expected file to use ({files})") sys.exit(1) file = files[0] print(f"Reading from {file}") with open(os.path.join(expected_dir, file), "r") as f: raw_expected = f.read() if raw_output.strip() == "": print(f"Error: No output from test") return False expected = [line.strip().split("||") for line in raw_expected.split("\n")] output = [line.strip().split("||") for line in raw_output.split("\n")] max_line_diff = 0 errored = False try: for i, ((output_file, output_line, output_symbol), (expected_file, expected_line, expected_symbol)) in enumerate(zip(output, expected)): if output_file != expected_file: print(f"Error: File name mismatch on line {i + 1}, found \"{output_file}\" expected \"{expected_file}\"") errored = True if abs(int(output_line) - int(expected_line)) > max_line_diff: print(f"Error: File line mismatch on line {i + 1}, found {output_line} expected {expected_line}") errored = True if output_symbol != expected_symbol: print(f"Error: File symbol mismatch on line {i + 1}, found \"{output_symbol}\" expected \"{expected_symbol}\"") errored = True if expected_symbol == "main" or expected_symbol == "main()": break except ValueError: print("ValueError during output checking") errored = True if errored: print("Output:") print(raw_output) print("Expected:") print(raw_expected) return not errored def run_test(runner: MatrixRunner, test_binary, params: Tuple[str]): def output_matcher(output: str): return output_matches(output, params) return runner.run_command(test_binary, output_matcher=output_matcher) def build(runner: MatrixRunner): matrix = runner.current_config() if platform.system() != "Windows": args = [ "cmake", "..", "-GNinja", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", "-DCPPTRACE_DISABLE_CXX_20_MODULES=ON", f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On", f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_WERROR_BUILD=On", f"-D{matrix['unwind']}=On", f"-D{matrix['symbols']}=On", f"-D{matrix['demangle']}=On", "-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h", "-DCPPTRACE_BUILD_TESTING=On", "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix['symbols'] == "CPPTRACE_GET_SYMBOLS_WITH_LIBDL": args.append("-DCPPTRACE_BUILD_TEST_RDYNAMIC=On") succeeded = runner.run_command(*args) if succeeded: return runner.run_command("ninja") else: args = [ "cmake", "..", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", "-DCPPTRACE_DISABLE_CXX_20_MODULES=ON", f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On", f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_WERROR_BUILD=On", f"-D{matrix['unwind']}=On", f"-D{matrix['symbols']}=On", f"-D{matrix['demangle']}=On", "-DCPPTRACE_BUILD_TESTING=On", "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix["compiler"] == "g++": args.append("-GNinja") succeeded = runner.run_command(*args) if succeeded: if matrix["compiler"] == "g++": return runner.run_command("ninja") else: return runner.run_command("msbuild", "cpptrace.sln") return False def build_full_or_auto(runner: MatrixRunner): matrix = runner.current_config() if platform.system() != "Windows": args = [ "cmake", "..", "-GNinja", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", "-DCPPTRACE_DISABLE_CXX_20_MODULES=ON", f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On", f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_WERROR_BUILD=On", f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h", "-DCPPTRACE_BUILD_TESTING=On", "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix["config"] != "": args.append(f"{matrix['config']}") succeeded = runner.run_command(*args) if succeeded: return runner.run_command("ninja") else: args = [ "cmake", "..", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", "-DCPPTRACE_DISABLE_CXX_20_MODULES=ON", f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On", f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_WERROR_BUILD=On", "-DCPPTRACE_BUILD_TESTING=On", "-DCPPTRACE_SKIP_UNIT=On", f"-DBUILD_SHARED_LIBS={matrix['shared']}" ] if matrix["config"] != "": args.append(f"{matrix['config']}") if matrix["compiler"] == "g++": args.append("-GNinja") succeeded = runner.run_command(*args) if succeeded: if matrix["compiler"] == "g++": return runner.run_command("ninja") else: return runner.run_command("msbuild", "cpptrace.sln") return False def test(runner: MatrixRunner): matrix = runner.current_config() if platform.system() != "Windows": return run_test( runner, "./integration", (matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"]) ) else: if matrix["compiler"] == "g++": return run_test( runner, f".\\integration.exe", (matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"]) ) else: return run_test( runner, f".\\{matrix['target']}\\integration.exe", (matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"]) ) def test_full_or_auto(runner: MatrixRunner): matrix = runner.current_config() if platform.system() != "Windows": return run_test( runner, "./integration", (matrix["compiler"],) ) else: if matrix["compiler"] == "g++": return run_test( runner, f".\\integration.exe", (matrix["compiler"],) ) else: return run_test( runner, f".\\{matrix['target']}\\integration.exe", (matrix["compiler"],) ) def build_and_test(runner: MatrixRunner): matrix = runner.current_config() if os.path.exists("build"): shutil.rmtree("build", ignore_errors=True) if not os.path.exists("build"): os.mkdir("build") os.chdir("build") good = False if build(runner): good = test(runner) os.chdir("..") print() return good def build_and_test_full_or_auto(runner: MatrixRunner): matrix = runner.current_config() if os.path.exists("build"): shutil.rmtree("build", ignore_errors=True) if not os.path.exists("build"): os.mkdir("build") os.chdir("build") good = False if build_full_or_auto(runner): good = test_full_or_auto(runner) os.chdir("..") print() return good def run_linux_matrix(compilers: list, shared: bool): MatrixRunner( matrix = { "compiler": compilers, "target": ["Debug"], "std": ["11", "20"], "unwind": [ "CPPTRACE_UNWIND_WITH_EXECINFO", "CPPTRACE_UNWIND_WITH_UNWIND", "CPPTRACE_UNWIND_WITH_LIBUNWIND", #"CPPTRACE_UNWIND_WITH_NOTHING", ], "symbols": [ # Disabled due to libbacktrace bug # "CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDL", "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", #"CPPTRACE_GET_SYMBOLS_WITH_NOTHING", ], "demangle": [ "CPPTRACE_DEMANGLE_WITH_CXXABI", #"CPPTRACE_DEMANGLE_WITH_NOTHING", ], "shared": ["On" if shared else "Off"] }, exclude = [] ).run(build_and_test) def run_linux_default(compilers: list, shared: bool): MatrixRunner( matrix = { "compiler": compilers, "target": ["Debug"], "std": ["11", "20"], "config": [""], "shared": ["On" if shared else "Off"] }, exclude = [] ).run(build_and_test_full_or_auto) def run_macos_matrix(compilers: list, shared: bool): MatrixRunner( matrix = { "compiler": compilers, "target": ["Debug"], "std": ["11", "20"], "unwind": [ "CPPTRACE_UNWIND_WITH_EXECINFO", "CPPTRACE_UNWIND_WITH_UNWIND", #"CPPTRACE_UNWIND_WITH_NOTHING", ], "symbols": [ #"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDL", "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", #"CPPTRACE_GET_SYMBOLS_WITH_NOTHING", ], "demangle": [ "CPPTRACE_DEMANGLE_WITH_CXXABI", #"CPPTRACE_DEMANGLE_WITH_NOTHING", ], "shared": ["On" if shared else "Off"] }, exclude = [] ).run(build_and_test) def run_macos_default(compilers: list, shared: bool): MatrixRunner( matrix = { "compiler": compilers, "target": ["Debug"], "std": ["11", "20"], "config": [""], "shared": ["On" if shared else "Off"] }, exclude = [] ).run(build_and_test_full_or_auto) def run_windows_matrix(compilers: list, shared: bool): MatrixRunner( matrix = { "compiler": compilers, "target": ["Debug"], "std": ["11", "20"], "unwind": [ "CPPTRACE_UNWIND_WITH_WINAPI", "CPPTRACE_UNWIND_WITH_DBGHELP", "CPPTRACE_UNWIND_WITH_UNWIND", # Broken on github actions for some reason #"CPPTRACE_UNWIND_WITH_NOTHING", ], "symbols": [ "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP", "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", #"CPPTRACE_GET_SYMBOLS_WITH_NOTHING", ], "demangle": [ "CPPTRACE_DEMANGLE_WITH_CXXABI", "CPPTRACE_DEMANGLE_WITH_NOTHING", ], "shared": ["On" if shared else "Off"] }, exclude = [ { "demangle": "CPPTRACE_DEMANGLE_WITH_CXXABI", "compiler": "cl" }, { "unwind": "CPPTRACE_UNWIND_WITH_UNWIND", "compiler": "cl" }, { "unwind": "CPPTRACE_UNWIND_WITH_UNWIND", "compiler": "clang++" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "compiler": "cl" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "compiler": "clang++" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", "compiler": "cl" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", "compiler": "clang++" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP", "compiler": "g++" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP", "demangle": "CPPTRACE_DEMANGLE_WITH_CXXABI" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", "demangle": "CPPTRACE_DEMANGLE_WITH_NOTHING" }, { "symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "demangle": "CPPTRACE_DEMANGLE_WITH_NOTHING" } ] ).run(build_and_test) def run_windows_default(compilers: list, shared: bool): MatrixRunner( matrix = { "compiler": compilers, "target": ["Debug"], "std": ["11", "20"], "config": [""], "shared": ["On" if shared else "Off"] }, exclude = [] ).run(build_and_test_full_or_auto) def main(): parser = argparse.ArgumentParser( prog="Build in all configs", description="Try building the library in all possible configurations for the current host" ) parser.add_argument( "--clang", action="store_true" ) parser.add_argument( "--gcc", action="store_true" ) parser.add_argument( "--msvc", action="store_true" ) parser.add_argument( "--all", action="store_true" ) parser.add_argument( "--shared", action="store_true" ) parser.add_argument( "--default-config", action="store_true" ) args = parser.parse_args() if platform.system() == "Linux": compilers = [] if args.clang or args.all: compilers.append("clang++-14") if args.gcc or args.all: compilers.append("g++-10") if args.default_config: run_linux_default(compilers, args.shared) else: run_linux_matrix(compilers, args.shared) if platform.system() == "Darwin": compilers = [] if args.clang or args.all: compilers.append("clang++") if args.gcc or args.all: compilers.append("g++-12") if args.default_config: run_macos_default(compilers, args.shared) else: run_macos_matrix(compilers, args.shared) if platform.system() == "Windows": compilers = [] if args.clang or args.all: compilers.append("clang++") if args.msvc or args.all: compilers.append("cl") if args.gcc or args.all: compilers.append("g++") if args.default_config: run_windows_default(compilers, args.shared) else: run_windows_matrix(compilers, args.shared) main() cpptrace-1.0.4/ci/unittest.py000066400000000000000000000141351504061443700161510ustar00rootroot00000000000000import argparse import os import platform import shutil import subprocess import sys from typing import Tuple from colorama import Fore, Back, Style from util import * sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner def get_c_compiler_counterpart(compiler: str) -> str: return compiler.replace("clang++", "clang").replace("g++", "gcc") def build(runner: MatrixRunner): if platform.system() == "Linux": matrix = runner.current_config() if "stdlib" in matrix and matrix["stdlib"] == "libc++": gtest_path = "/tmp/gtest_install_libcxx" else: gtest_path = "/tmp/gtest_install" args = [ "cmake", "..", "-GNinja", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_BUILD_TYPE={matrix['build_type']}", f"-DCPPTRACE_BUILD_SHARED={matrix['shared']}", "-DCPPTRACE_DISABLE_CXX_20_MODULES=ON", f"-DHAS_DL_FIND_OBJECT={matrix['has_dl_find_object']}", "-DCPPTRACE_WERROR_BUILD=On", "-DCPPTRACE_STD_FORMAT=Off", "-DCPPTRACE_BUILD_TESTING=On", f"-DCPPTRACE_SANITIZER_BUILD={matrix['sanitizers']}", f"-DCPPTRACE_BUILD_NO_SYMBOLS={matrix['no_symbols']}", f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['split_dwarf']}", f"-DCPPTRACE_BUILD_TESTING_DWARF_VERSION={matrix['dwarf_version']}", f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On", f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_USE_EXTERNAL_GTEST=On", f"-DCMAKE_PREFIX_PATH={gtest_path}", *(["-DCMAKE_CXX_FLAGS=-stdlib=libc++"] if "stdlib" in matrix and matrix["stdlib"] == "libc++" else []) ] return runner.run_command(*args) and runner.run_command("ninja") elif platform.system() == "Darwin": matrix = runner.current_config() if "clang++" in matrix["compiler"]: gtest_path = "/tmp/gtest_asan_install" if matrix['sanitizers'] == "ON" else "/tmp/gtest_install" else: gtest_path = "/tmp/gtest_install_gcc" args = [ "cmake", "..", "-GNinja", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_BUILD_TYPE={matrix['build_type']}", f"-DCPPTRACE_BUILD_SHARED={matrix['shared']}", "-DCPPTRACE_DISABLE_CXX_20_MODULES=ON", "-DCPPTRACE_WERROR_BUILD=On", "-DCPPTRACE_STD_FORMAT=Off", "-DCPPTRACE_BUILD_TESTING=On", f"-DCPPTRACE_SANITIZER_BUILD={matrix['sanitizers']}", f"-DCPPTRACE_BUILD_NO_SYMBOLS={matrix['no_symbols']}", # f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['split_dwarf']}", # f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['dwarf_version']}", f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On", f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On", f"-DCPPTRACE_USE_EXTERNAL_GTEST=On", f"-DCMAKE_PREFIX_PATH={gtest_path}", ] return runner.run_command(*args) and runner.run_command("ninja") else: raise ValueError() def test(runner: MatrixRunner): if platform.system() == "Linux": return runner.run_command("./unittest") and runner.run_command("bash", "-c", "exec -a u ./unittest") elif platform.system() == "Darwin": if runner.current_config()["dSYM"]: if not runner.run_command("dsymutil", "unittest"): return False good = runner.run_command("./unittest") and runner.run_command("bash", "-c", "exec -a u ./unittest") if runner.current_config()["dSYM"]: shutil.rmtree("unittest.dSYM") return good else: raise ValueError() def build_and_test(runner: MatrixRunner): # the build directory has to be purged on compiler or shared change last = runner.last_config() current = runner.current_config() if ( last is None or last["compiler"] != current["compiler"] or ("stdlib" in current and last["stdlib"] != current["stdlib"]) or (platform.system() == "Darwin" and last["sanitizers"] != current["sanitizers"]) ) and os.path.exists("build"): shutil.rmtree("build", ignore_errors=True) if not os.path.exists("build"): os.mkdir("build") os.chdir("build") good = False if build(runner): good = test(runner) os.chdir("..") print(flush=True) return good def run_linux_matrix(): MatrixRunner( matrix = { "compiler": ["g++-10", "clang++-18"], "stdlib": ["libstdc++", "libc++"], "sanitizers": ["OFF", "ON"], "build_type": ["Debug", "RelWithDebInfo"], "shared": ["OFF", "ON"], "has_dl_find_object": ["OFF", "ON"], "split_dwarf": ["OFF", "ON"], "dwarf_version": ["4", "5"], "no_symbols": ["On", "Off"], }, exclude = [ { "compiler": "g++-10", "stdlib": "libc++", }, { # need to workaround https://github.com/llvm/llvm-project/issues/59432 later "stdlib": "libc++", "sanitizers": "ON", }, ] ).run(build_and_test) def run_macos_matrix(): MatrixRunner( matrix = { "compiler": ["g++-12", "clang++"], "sanitizers": ["OFF", "ON"], "build_type": ["Debug", "RelWithDebInfo"], "shared": ["OFF", "ON"], "dSYM": [True, False], "no_symbols": ["On", "Off"], }, exclude = [ { "compiler": "g++-12", "sanitizers": "ON", }, ] ).run(build_and_test) def main(): if platform.system() == "Linux": run_linux_matrix() if platform.system() == "Darwin": run_macos_matrix() if platform.system() == "Windows": raise ValueError() # run_windows_matrix() main() cpptrace-1.0.4/ci/util.py000066400000000000000000000142271504061443700152510ustar00rootroot00000000000000import subprocess import sys import itertools from typing import List, Dict from colorama import Fore, Back, Style import re import time # https://stackoverflow.com/a/14693789/15675011 ansi_escape = re.compile(r''' \x1B # ESC (?: # 7-bit C1 Fe (except CSI) [@-Z\\-_] | # or [ for CSI, followed by a control sequence \[ [0-?]* # Parameter bytes [ -/]* # Intermediate bytes [@-~] # Final byte ) ''', re.VERBOSE) class MatrixRunner: def __init__(self, matrix, exclude): self.matrix = matrix self.exclude = exclude self.include = self.parse_includes() self.keys = [*matrix.keys()] self.values = [*matrix.values()] self.results = {} # insertion-ordered self.failed = False self.work = self.get_work() self.last_matrix_config = None self.current_matrix_config = None def parse_includes(self) -> Dict[str, List[str]]: includes: Dict[str, List[str]] = dict() for arg in sys.argv: if arg.startswith("--slice="): rest = arg[len("--slice="):] key, value = rest.split(":") if key not in includes: includes[key] = [] includes[key].append(value) return includes def run_command(self, *args: List[str], always_output=False, output_matcher=None) -> bool: self.log(f"{Fore.CYAN}{Style.BRIGHT}Running Command \"{' '.join(args)}\"{Style.RESET_ALL}") start_time = time.time() p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() runtime = time.time() - start_time self.log(Style.RESET_ALL, end="") # makefile in parallel sometimes messes up colors if p.returncode != 0: self.log(f"{Fore.RED}{Style.BRIGHT}Command failed{Style.RESET_ALL} {Fore.MAGENTA}(time: {runtime:.2f}s){Style.RESET_ALL}") self.log("stdout:") self.log(stdout.decode("utf-8"), end="") self.log("stderr:") self.log(stderr.decode("utf-8"), end="") self.failed = True return False else: self.log(f"{Fore.GREEN}{Style.BRIGHT}Command succeeded{Style.RESET_ALL} {Fore.MAGENTA}(time: {runtime:.2f}s){Style.RESET_ALL}") if always_output: self.log("stdout:") self.log(stdout.decode("utf-8"), end="") self.log("stderr:") self.log(stderr.decode("utf-8"), end="") elif len(stderr) != 0: self.log("stderr:") self.log(stderr.decode("utf-8"), end="") if output_matcher is not None: if not output_matcher(stdout.decode("utf-8")): self.failed = True return False return True def set_fail(self): self.failed = True def current_config(self): return self.current_matrix_config def last_config(self): return self.last_matrix_config def log(self, *args, **kwargs): print(*args, **kwargs, flush=True) def do_exclude(self, matrix_config, exclude): return all(map(lambda k: matrix_config[k] == exclude[k], exclude.keys())) def do_include(self, matrix_config, include): if len(include) == 0: return True return all(map(lambda k: matrix_config[k] in include[k], include.keys())) def assignment_to_matrix_config(self, assignment): matrix_config = {} for k, v in zip(self.matrix.keys(), assignment): matrix_config[k] = v return matrix_config def get_work(self): work = [] for assignment in itertools.product(*self.matrix.values()): config = self.assignment_to_matrix_config(assignment) if any(map(lambda ex: self.do_exclude(config, ex), self.exclude)): continue if not self.do_include(config, self.include): continue work.append(assignment) return work def run(self, fn): for i, assignment in enumerate(self.work): matrix_config = self.assignment_to_matrix_config(assignment) config_tuple = tuple(self.values[i].index(p) for i, p in enumerate(assignment)) config_str = ', '.join(map(lambda v: str(v), matrix_config.values())) if config_str == "": self.log(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} [{i + 1}/{len(self.work)}] Running with blank config {'=' * 10}{Style.RESET_ALL}") else: self.log(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} [{i + 1}/{len(self.work)}] Running with config {config_str} {'=' * 10}{Style.RESET_ALL}") self.last_matrix_config = self.current_matrix_config self.current_matrix_config = matrix_config self.results[config_tuple] = fn(self) self.print_results() if self.failed: self.log("🔴 Some checks failed") sys.exit(1) else: self.log("🟢 All checks passed") def adj_width(self, text): return len(text) - len(ansi_escape.sub("", text)) def print_table(self, table): columns = len(table[0]) column_widths = [1 for _ in range(columns)] for row in table: for i, cell in enumerate(row): column_widths[i] = max(column_widths[i], len(ansi_escape.sub("", cell))) for j, cell in enumerate(table[0]): self.log("| {cell:{width}} ".format(cell=cell, width=column_widths[j] + self.adj_width(cell)), end="") self.log("|") for i, row in enumerate(table[1:]): for j, cell in enumerate(row): self.log("| {cell:{width}} ".format(cell=cell, width=column_widths[j] + self.adj_width(cell)), end="") self.log("|") def print_results(self): self.log("Results:") table = [self.keys] for result in self.results: table.append([ f"{Fore.GREEN if self.results[result] else Fore.RED}{Style.BRIGHT}{self.values[i][v]}{Style.RESET_ALL}" for i, v in enumerate(result) ]) self.print_table(table) cpptrace-1.0.4/cmake/000077500000000000000000000000001504061443700144015ustar00rootroot00000000000000cpptrace-1.0.4/cmake/Autoconfig.cmake000066400000000000000000000137441504061443700175120ustar00rootroot00000000000000# ================================================== Platform Support ================================================== function(check_support var source includes libraries definitions) set(CMAKE_REQUIRED_INCLUDES "${includes}") list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(CMAKE_REQUIRED_LIBRARIES "${libraries}") set(CMAKE_REQUIRED_DEFINITIONS "${definitions}") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) string(CONCAT full_source "#include \"${source}\"" ${nonce}) check_cxx_source_compiles(${full_source} ${var}) set(${var} ${${var}} PARENT_SCOPE) endfunction() if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") check_support(HAS_CXXABI has_cxxabi.cpp "" "" "") endif() if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") check_support(HAS_ATTRIBUTE_PACKED has_attribute_packed.cpp "" "" "") endif() if(NOT WIN32) check_support(HAS_UNWIND has_unwind.cpp "" "" "") check_support(HAS_EXECINFO has_execinfo.cpp "" "" "") else() check_support(HAS_STACKWALK has_stackwalk.cpp "" "dbghelp" "") endif() set(HAS_CXX20_MODULES FALSE) if(NOT CPPTRACE_DISABLE_CXX_20_MODULES) if(CMAKE_CXX_STANDARD GREATER_EQUAL 20) # https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html#compiler-support # msvc 14.34+/19.34+ # clang 16+ # gcc 15 and newer if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.34) set(HAS_CXX20_MODULES TRUE) endif() elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) set(HAS_CXX20_MODULES TRUE) endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0) set(HAS_CXX20_MODULES TRUE) endif() endif() endif() if(HAS_CXX20_MODULES) message(STATUS "Cpptrace HAS_CXX20_MODULES: Yes") else() message(STATUS "Cpptrace HAS_CXX20_MODULES: No") endif() endif() if(NOT WIN32 OR MINGW) check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${CPPTRACE_BACKTRACE_PATH_DEFINITION}") set(STACKTRACE_LINK_LIB "stdc++_libbacktrace") check_support(HAS_CXX_EXCEPTION_TYPE has_cxx_exception_type.cpp "" "" "") endif() if(UNIX AND NOT APPLE) check_support(HAS_DL_FIND_OBJECT has_dl_find_object.cpp "" "dl" "") if(NOT HAS_DL_FIND_OBJECT) check_support(HAS_DLADDR1 has_dladdr1.cpp "" "dl" "") endif() endif() if(APPLE) check_support(HAS_MACH_VM has_mach_vm.cpp "" "" "") endif() # ================================================ Autoconfig unwinding ================================================ # Unwind back-ends if( NOT ( CPPTRACE_UNWIND_WITH_UNWIND OR CPPTRACE_UNWIND_WITH_LIBUNWIND OR CPPTRACE_UNWIND_WITH_EXECINFO OR CPPTRACE_UNWIND_WITH_WINAPI OR CPPTRACE_UNWIND_WITH_DBGHELP OR CPPTRACE_UNWIND_WITH_NOTHING ) ) # Attempt to auto-config if(APPLE AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")) if(HAS_EXECINFO) set(CPPTRACE_UNWIND_WITH_EXECINFO On) message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding") else() set(CPPTRACE_UNWIND_WITH_NOTHING On) message(FATAL_ERROR "Cpptrace auto config: No unwinding back-end seems to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.") endif() elseif(UNIX) if(HAS_UNWIND) set(CPPTRACE_UNWIND_WITH_UNWIND On) message(STATUS "Cpptrace auto config: Using libgcc unwind for unwinding") elseif(HAS_EXECINFO) set(CPPTRACE_UNWIND_WITH_EXECINFO On) message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding") else() set(CPPTRACE_UNWIND_WITH_NOTHING On) message(FATAL_ERROR "Cpptrace auto config: No unwinding back-end seems to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.") endif() elseif(MINGW OR WIN32) if(HAS_STACKWALK) set(CPPTRACE_UNWIND_WITH_DBGHELP On) message(STATUS "Cpptrace auto config: Using dbghelp for unwinding") else() set(CPPTRACE_UNWIND_WITH_WINAPI On) message(STATUS "Cpptrace auto config: Using winapi for unwinding") endif() endif() else() #message(STATUS "MANUAL CONFIG SPECIFIED") endif() # ================================================= Autoconfig symbols ================================================= if( NOT ( CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF OR CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR CPPTRACE_GET_SYMBOLS_WITH_NOTHING ) ) if(UNIX) message(STATUS "Cpptrace auto config: Using libdwarf for symbols") set(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF On) elseif(MINGW) message(STATUS "Cpptrace auto config: Using libdwarf + dbghelp for symbols") # Use both dbghelp and libdwarf under mingw: Some files may use pdb symbols, e.g. system dlls like KERNEL32.dll and # ntdll.dll at the very least, but also other libraries linked with may have pdb symbols. set(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF On) set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On) else() message(STATUS "Cpptrace auto config: Using dbghelp for symbols") set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On) endif() endif() # =============================================== Autoconfig demangling ================================================ # Handle demangle configuration if( NOT ( CPPTRACE_DEMANGLE_WITH_CXXABI OR CPPTRACE_DEMANGLE_WITH_WINAPI OR CPPTRACE_DEMANGLE_WITH_NOTHING ) ) if(HAS_CXXABI) message(STATUS "Cpptrace auto config: Using cxxabi for demangling") set(CPPTRACE_DEMANGLE_WITH_CXXABI On) elseif(WIN32 AND NOT MINGW) message(STATUS "Cpptrace auto config: Using dbghelp for demangling") set(CPPTRACE_DEMANGLE_WITH_WINAPI On) else() set(CPPTRACE_DEMANGLE_WITH_NOTHING On) endif() else() #message(STATUS "Manual demangling back-end specified") endif() cpptrace-1.0.4/cmake/Findzstd.cmake000066400000000000000000000030551504061443700171730ustar00rootroot00000000000000# Libdwarf needs zstd, cpptrace doesn't, and libdwarf has its own Findzstd but it doesn't define zstd::libzstd_static / # zstd::libzstd_shared targets which leads to issues, necessitating a find_dependency(zstd) in cpptrace's cmake config # and in order to support non-cmake-module installs we need to provide a Findzstd script. # https://github.com/jeremy-rifkin/cpptrace/issues/112 # This will define # zstd_FOUND # zstd_INCLUDE_DIR # zstd_LIBRARY find_path(zstd_INCLUDE_DIR NAMES zstd.h) find_library(zstd_LIBRARY_DEBUG NAMES zstdd zstd_staticd) find_library(zstd_LIBRARY_RELEASE NAMES zstd zstd_static) include(SelectLibraryConfigurations) SELECT_LIBRARY_CONFIGURATIONS(zstd) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS( zstd DEFAULT_MSG zstd_LIBRARY zstd_INCLUDE_DIR ) if(zstd_FOUND) message(STATUS "Found Zstd: ${zstd_LIBRARY}") endif() mark_as_advanced(zstd_INCLUDE_DIR zstd_LIBRARY) if(zstd_FOUND) # just defining them the same... cmake will figure it out if(NOT TARGET zstd::libzstd_static) add_library(zstd::libzstd_static UNKNOWN IMPORTED) set_target_properties( zstd::libzstd_static PROPERTIES IMPORTED_LOCATION "${zstd_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${zstd_INCLUDE_DIR}" ) endif() if(NOT TARGET zstd::libzstd_shared) add_library(zstd::libzstd_shared UNKNOWN IMPORTED) set_target_properties( zstd::libzstd_shared PROPERTIES IMPORTED_LOCATION "${zstd_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${zstd_INCLUDE_DIR}" ) endif() endif() cpptrace-1.0.4/cmake/InstallRules.cmake000066400000000000000000000052271504061443700200320ustar00rootroot00000000000000include(CMakePackageConfigHelpers) # copy header files to CMAKE_INSTALL_INCLUDEDIR # don't include third party header files install( DIRECTORY "${PROJECT_SOURCE_DIR}/include/" # our header files "${PROJECT_BINARY_DIR}/include/" # generated header files DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT ${package_name}_development # PATTERN "**/third_party" EXCLUDE # skip third party directory # PATTERN "**/third_party/**" EXCLUDE # skip third party files ) # copy target build output artifacts to OS dependent locations # (Except includes, that just sets a compiler flag with the path) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23") set( FILE_SET_ARGS FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/cpptrace" ) endif() install( TARGETS ${target_name} EXPORT ${package_name}-targets RUNTIME # COMPONENT ${package_name}_runtime LIBRARY # COMPONENT ${package_name}_runtime NAMELINK_COMPONENT ${package_name}_development ARCHIVE # COMPONENT ${package_name}_development INCLUDES # DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ${FILE_SET_ARGS} ) # create config file that points to targets file configure_file( "${PROJECT_SOURCE_DIR}/cmake/in/cpptrace-config-cmake.in" "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake" @ONLY ) # copy config file for find_package to find install( FILES "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake" DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}" COMPONENT ${package_name}_development ) # create version file for consumer to check version in CMake write_basic_package_version_file( "${package_name}-config-version.cmake" COMPATIBILITY SameMajorVersion # a.k.a SemVer ) # copy version file for find_package to find for version check install( FILES "${PROJECT_BINARY_DIR}/${package_name}-config-version.cmake" DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}" COMPONENT ${package_name}_development ) # create targets file included by config file with targets for consumers install( EXPORT ${package_name}-targets NAMESPACE cpptrace:: DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}" COMPONENT ${package_name}_development ) if(CPPTRACE_PROVIDE_EXPORT_SET) export( TARGETS ${target_name} NAMESPACE cpptrace:: FILE "${PROJECT_BINARY_DIR}/${package_name}-targets.cmake" ) endif() # Findzstd.cmake # vcpkg doesn't like anything being put in share/, which is where this goes apparently on their setup if(NOT CPPTRACE_VCPKG) install( FILES "${PROJECT_SOURCE_DIR}/cmake/Findzstd.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}" ) endif() # support packaging library if(PROJECT_IS_TOP_LEVEL) include(CPack) endif() cpptrace-1.0.4/cmake/OptionVariables.cmake000066400000000000000000000204461504061443700205120ustar00rootroot00000000000000# Included further down to avoid interfering with our cache variables # include(GNUInstallDirs) # ---- Options Summary ---- # --------------------------------------------------------------------------------------------------- # | Option | Availability | Default | # |=================================|===============|===============================================| # | BUILD_SHARED_LIBS | Top-Level | OFF | # | BUILD_TESTING | Top-Level | OFF | # |---------------------------------|---------------|-----------------------------------------------| # | CPPTRACE_BUILD_SHARED | Always | ${BUILD_SHARED_LIBS} | # | CPPTRACE_BUILD_TESTING | Always | ${BUILD_TESTING} AND ${PROJECT_IS_TOP_LEVEL} | # | CPPTRACE_INCLUDES_WITH_SYSTEM | Not Top-Level | ON | # | CPPTRACE_INSTALL_CMAKEDIR | Always | ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name} | # | CPPTRACE_USE_EXTERNAL_LIBDWARF | Always | OFF | # | CPPTRACE_USE_EXTERNAL_ZSTD | Always | OFF | # | ... | | | # --------------------------------------------------------------------------------------------------- # ---- Build Shared ---- # Sometimes it's useful to be able to single out a dependency to be built as # static or shared, even if obtained from source if(PROJECT_IS_TOP_LEVEL) option(BUILD_SHARED_LIBS "Build shared libs" OFF) endif() option( CPPTRACE_BUILD_SHARED "Override BUILD_SHARED_LIBS for ${package_name} library" ${BUILD_SHARED_LIBS} ) mark_as_advanced(CPPTRACE_BUILD_SHARED) set(build_type STATIC) if(CPPTRACE_BUILD_SHARED) set(build_type SHARED) endif() # ---- Warning Guard ---- # target_include_directories with SYSTEM modifier will request the compiler to # omit warnings from the provided paths, if the compiler supports that. # This is to provide a user experience similar to find_package when # add_subdirectory or FetchContent is used to consume this project. set(warning_guard ) if(NOT PROJECT_IS_TOP_LEVEL) option( CPPTRACE_INCLUDES_WITH_SYSTEM "Use SYSTEM modifier for ${package_name}'s includes, disabling warnings" ON ) mark_as_advanced(CPPTRACE_INCLUDES_WITH_SYSTEM) if(CPPTRACE_INCLUDES_WITH_SYSTEM) set(warning_guard SYSTEM) endif() endif() # ---- Enable Testing ---- # By default tests aren't enabled even with BUILD_TESTING=ON unless the library # is built as a top level project. # This is in order to cut down on unnecessary compile times, since it's unlikely # for users to want to run the tests of their dependencies. if(PROJECT_IS_TOP_LEVEL) option(BUILD_TESTING "Build tests" OFF) endif() if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING) set(build_testing ON) endif() option( CPPTRACE_BUILD_TESTING "Override BUILD_TESTING for ${package_name} library" ${build_testing} ) set(build_testing ) mark_as_advanced(CPPTRACE_BUILD_TESTING) # ---- Install Include Directory ---- # Adds an extra directory to the include path by default, so that when you link # against the target, you get `/include/` added to your # include paths rather than `/include`. # This doesn't affect include paths used by consumers of this project, but helps # prevent consumers having access to other projects in the same include # directory (e.g. usr/include). # The variable type is STRING rather than PATH, because otherwise passing # -DCMAKE_INSTALL_INCLUDEDIR=include on the command line would expand to an # absolute path with the base being the current CMake directory, leading to # unexpected errors. # if(PROJECT_IS_TOP_LEVEL) # set( # CMAKE_INSTALL_INCLUDEDIR "include/${package_name}-${PROJECT_VERSION}" # CACHE STRING "" # ) # # marked as advanced in GNUInstallDirs version, so we follow their lead # mark_as_advanced(CMAKE_INSTALL_INCLUDEDIR) # endif() # do not include earlier or we can't set CMAKE_INSTALL_INCLUDEDIR above # include required for CMAKE_INSTALL_LIBDIR below include(GNUInstallDirs) # ---- Install CMake Directory ---- # This allows package maintainers to freely override the installation path for # the CMake configs. # This doesn't affects include paths used by consumers of this project. # The variable type is STRING rather than PATH, because otherwise passing # -DCPPTRACE_INSTALL_CMAKEDIR=lib/cmake on the command line would expand to an # absolute path with the base being the current CMake directory, leading to # unexpected errors. set( CPPTRACE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}" CACHE STRING "CMake package config location relative to the install prefix" ) # depends on CMAKE_INSTALL_LIBDIR which is marked as advanced in GNUInstallDirs mark_as_advanced(CPPTRACE_INSTALL_CMAKEDIR) # ---- Symbol Options ---- option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF) option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF) # ---- Unwinding Options ---- option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF) option(CPPTRACE_UNWIND_WITH_LIBUNWIND "" OFF) option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF) option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF) option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF) option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF) # ---- Demangling Options ---- option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF) option(CPPTRACE_DEMANGLE_WITH_WINAPI "" OFF) option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF) # ---- Back-end configurations ---- set(CPPTRACE_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.") set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 400.") set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.") option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF) # ---- Other configurations ---- if(PROJECT_IS_TOP_LEVEL) option(CPPTRACE_BUILD_TESTING "" OFF) option(CPPTRACE_BUILD_TOOLS "" OFF) option(CPPTRACE_BUILD_BENCHMARKING "" OFF) option(CPPTRACE_BUILD_NO_SYMBOLS "" OFF) option(CPPTRACE_BUILD_TESTING_SPLIT_DWARF "" OFF) set(CPPTRACE_BUILD_TESTING_DWARF_VERSION "0" CACHE STRING "") option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF) mark_as_advanced( CPPTRACE_BUILD_TESTING CPPTRACE_BUILD_TOOLS CPPTRACE_BUILD_BENCHMARKING CPPTRACE_BUILD_NO_SYMBOLS CPPTRACE_BUILD_TESTING_SPLIT_DWARF CPPTRACE_BUILD_TESTING_DWARF_VERSION CPPTRACE_BUILD_TEST_RDYNAMIC ) endif() option(CPPTRACE_USE_EXTERNAL_LIBDWARF "" OFF) option(CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG "" OFF) option(CPPTRACE_USE_EXTERNAL_ZSTD "" OFF) option(CPPTRACE_CONAN "" OFF) option(CPPTRACE_VCPKG "" OFF) option(CPPTRACE_SANITIZER_BUILD "" OFF) option(CPPTRACE_WERROR_BUILD "" OFF) option(CPPTRACE_POSITION_INDEPENDENT_CODE "" ON) option(CPPTRACE_SKIP_UNIT "" OFF) option(CPPTRACE_STD_FORMAT "" ON) option(CPPTRACE_UNPREFIXED_TRY_CATCH "" OFF) option(CPPTRACE_USE_EXTERNAL_GTEST "" OFF) set(CPPTRACE_ZSTD_URL "https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz" CACHE STRING "") set(CPPTRACE_LIBDWARF_REPO "https://github.com/jeremy-rifkin/libdwarf-lite.git" CACHE STRING "") set(CPPTRACE_LIBDWARF_TAG "5dfb2cd2aacf2bf473e5bfea79e41289f88b3a5f" CACHE STRING "") # v2.1.0 set(CPPTRACE_LIBDWARF_SHALLOW "1" CACHE STRING "") option(CPPTRACE_PROVIDE_EXPORT_SET "" ON) option(CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF "" OFF) option(CPPTRACE_DISABLE_CXX_20_MODULES "" OFF) mark_as_advanced( CPPTRACE_BACKTRACE_PATH CPPTRACE_ADDR2LINE_PATH CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH CPPTRACE_SANITIZER_BUILD CPPTRACE_WERROR_BUILD CPPTRACE_CONAN CPPTRACE_VCPKG CPPTRACE_SKIP_UNIT CPPTRACE_USE_EXTERNAL_GTEST CPPTRACE_ZSTD_REPO CPPTRACE_ZSTD_TAG CPPTRACE_ZSTD_SHALLOW CPPTRACE_LIBDWARF_REPO CPPTRACE_LIBDWARF_TAG CPPTRACE_LIBDWARF_SHALLOW CPPTRACE_PROVIDE_EXPORT_SET CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF CPPTRACE_DISABLE_CXX_20_MODULES ) cpptrace-1.0.4/cmake/PreventInSourceBuilds.cmake000066400000000000000000000003711504061443700216420ustar00rootroot00000000000000# In-source build guard if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message( FATAL_ERROR "In-source builds are not supported. " "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' before rebuilding this project." ) endif() cpptrace-1.0.4/cmake/ProjectIsTopLevel.cmake000066400000000000000000000002241504061443700207560ustar00rootroot00000000000000# This variable is set by project() in CMake 3.21+ string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL ) cpptrace-1.0.4/cmake/has_attribute_packed.cpp000066400000000000000000000001201504061443700212430ustar00rootroot00000000000000struct __attribute__((packed)) foo { int i; double d; }; int main() {} cpptrace-1.0.4/cmake/has_backtrace.cpp000066400000000000000000000003121504061443700176530ustar00rootroot00000000000000#ifdef CPPTRACE_BACKTRACE_PATH #include CPPTRACE_BACKTRACE_PATH #else #include #endif int main() { backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr); } cpptrace-1.0.4/cmake/has_cxx_exception_type.cpp000066400000000000000000000001601504061443700216560ustar00rootroot00000000000000#include int main() { std::type_info* t = abi::__cxa_current_exception_type(); (void*) t; } cpptrace-1.0.4/cmake/has_cxxabi.cpp000066400000000000000000000001631504061443700172160ustar00rootroot00000000000000#include int main() { int status; abi::__cxa_demangle("_Z3foov", nullptr, nullptr, &status); } cpptrace-1.0.4/cmake/has_dl.cpp000066400000000000000000000001211504061443700163310ustar00rootroot00000000000000#include int main() { Dl_info info; dladdr(nullptr, &info); } cpptrace-1.0.4/cmake/has_dl_find_object.cpp000066400000000000000000000001731504061443700206660ustar00rootroot00000000000000#include int main() { dl_find_object result; _dl_find_object(reinterpret_cast(main), &result); } cpptrace-1.0.4/cmake/has_dladdr1.cpp000066400000000000000000000003231504061443700172510ustar00rootroot00000000000000#include #include int main() { Dl_info info; link_map* link_map_info; dladdr1(reinterpret_cast(&main), &info, reinterpret_cast(&link_map_info), RTLD_DL_LINKMAP); } cpptrace-1.0.4/cmake/has_execinfo.cpp000066400000000000000000000001271504061443700175400ustar00rootroot00000000000000#include int main() { void* frames[10]; backtrace(frames, 10); } cpptrace-1.0.4/cmake/has_mach_vm.cpp000066400000000000000000000012331504061443700173510ustar00rootroot00000000000000#include #include #include int main() { mach_vm_size_t vmsize; uintptr_t addr = reinterpret_cast(&vmsize); uintptr_t page_addr = addr & ~(4096 - 1); mach_vm_address_t address = (mach_vm_address_t)page_addr; vm_region_basic_info_data_t info; mach_msg_type_number_t info_count = sizeof(size_t) == 8 ? VM_REGION_BASIC_INFO_COUNT_64 : VM_REGION_BASIC_INFO_COUNT; memory_object_name_t object; mach_vm_region( mach_task_self(), &address, &vmsize, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &info_count, &object ); } cpptrace-1.0.4/cmake/has_stackwalk.cpp000066400000000000000000000060571504061443700177340ustar00rootroot00000000000000#define WIN32_LEAN_AND_MEAN #include #include #define IS_CLANG 0 #define IS_GCC 0 #define IS_MSVC 0 #if defined(__clang__) #undef IS_CLANG #define IS_CLANG 1 #elif defined(__GNUC__) || defined(__GNUG__) #undef IS_GCC #define IS_GCC 1 #elif defined(_MSC_VER) #undef IS_MSVC #define IS_MSVC 1 #else #error "Unsupported compiler" #endif int main() { HANDLE proc = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); // https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/ // Get current thread context // GetThreadContext cannot be used on the current thread. // RtlCaptureContext doesn't work on i386 CONTEXT context; #if defined(_M_IX86) || defined(__i386__) ZeroMemory(&context, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_CONTROL; #if IS_MSVC __asm { label: mov [context.Ebp], ebp; mov [context.Esp], esp; mov eax, [label]; mov [context.Eip], eax; } #else asm( "label:\n\t" "mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t" "mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t" "mov{l $label, %%eax | eax, OFFSET label};\n\t" "mov{l %%eax, %[cEip] | %[cEip], eax};\n\t" : [cEbp] "=r" (context.Ebp), [cEsp] "=r" (context.Esp), [cEip] "=r" (context.Eip) ); #endif #else RtlCaptureContext(&context); #endif // Setup current frame STACKFRAME64 frame; ZeroMemory(&frame, sizeof(STACKFRAME64)); DWORD machine_type; #if defined(_M_IX86) || defined(__i386__) machine_type = IMAGE_FILE_MACHINE_I386; frame.AddrPC.Offset = context.Eip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Ebp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Esp; frame.AddrStack.Mode = AddrModeFlat; #elif defined(_M_X64) || defined(__x86_64__) machine_type = IMAGE_FILE_MACHINE_AMD64; frame.AddrPC.Offset = context.Rip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Rsp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Rsp; frame.AddrStack.Mode = AddrModeFlat; #elif defined(_M_IA64) || defined(__aarch64__) machine_type = IMAGE_FILE_MACHINE_IA64; frame.AddrPC.Offset = context.StIIP; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.IntSp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrBStore.Offset= context.RsBSP; frame.AddrBStore.Mode = AddrModeFlat; frame.AddrStack.Offset = context.IntSp; frame.AddrStack.Mode = AddrModeFlat; #else #error "Cpptrace: StackWalk64 not supported for this platform yet" #endif ZeroMemory(&context, sizeof(CONTEXT)); StackWalk64( machine_type, proc, thread, &frame, machine_type == IMAGE_FILE_MACHINE_I386 ? NULL : &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ); } cpptrace-1.0.4/cmake/has_unwind.cpp000066400000000000000000000005331504061443700172450ustar00rootroot00000000000000#include #include _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) { _Unwind_GetIP(context); int is_before_instruction = 0; uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction); return _URC_END_OF_STACK; } int main() { _Unwind_Backtrace(unwind_callback, nullptr); } cpptrace-1.0.4/cmake/in/000077500000000000000000000000001504061443700150075ustar00rootroot00000000000000cpptrace-1.0.4/cmake/in/cpptrace-config-cmake.in000066400000000000000000000016471504061443700214710ustar00rootroot00000000000000# Init @ variables before doing anything else @PACKAGE_INIT@ # Dependencies if(@CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF@) include(CMakeFindDependencyMacro) # we don't go the Findzstd.cmake route on vcpkg if(@CPPTRACE_VCPKG@) find_dependency(zstd CONFIG REQUIRED) else() set(CMAKE_MODULE_PATH_OLD "${CMAKE_MODULE_PATH}") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_LIST_DIR}") find_dependency(zstd) set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH_OLD}") unset(CMAKE_MODULE_PATH_OLD) endif() if(NOT @CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG@) find_dependency(libdwarf REQUIRED) endif() endif() # We cannot modify an existing IMPORT target if(NOT TARGET cpptrace::cpptrace) # import targets include("${CMAKE_CURRENT_LIST_DIR}/@package_name@-targets.cmake") endif() if(@CPPTRACE_STATIC_DEFINE@) target_compile_definitions(cpptrace::cpptrace INTERFACE CPPTRACE_STATIC_DEFINE) endif() cpptrace-1.0.4/cmake/in/version-hpp.in000066400000000000000000000006761504061443700176220ustar00rootroot00000000000000#ifndef CPPTRACE_VERSION_HPP #define CPPTRACE_VERSION_HPP #define CPPTRACE_VERSION_MAJOR @CPPTRACE_VERSION_MAJOR@ #define CPPTRACE_VERSION_MINOR @CPPTRACE_VERSION_MINOR@ #define CPPTRACE_VERSION_PATCH @CPPTRACE_VERSION_PATCH@ #define CPPTRACE_TO_VERSION(MAJOR, MINOR, PATCH) ((MAJOR) * 10000 + (MINOR) * 100 + (PATCH)) #define CPPTRACE_VERSION CPPTRACE_TO_VERSION(CPPTRACE_VERSION_MAJOR, CPPTRACE_VERSION_MINOR, CPPTRACE_VERSION_PATCH) #endif cpptrace-1.0.4/docs/000077500000000000000000000000001504061443700142515ustar00rootroot00000000000000cpptrace-1.0.4/docs/c-api.md000066400000000000000000000133411504061443700155660ustar00rootroot00000000000000# ctrace Cpptrace provides a C API under the name ctrace, documented below. ## Table of Contents - [Documentation](#documentation) - [Stack Traces](#stack-traces) - [Object Traces](#object-traces) - [Raw Traces](#raw-traces) - [Utilities](#utilities) - [Utility types](#utility-types) - [Configuration](#configuration) - [Signal-Safe Tracing](#signal-safe-tracing) ## Documentation All ctrace declarations are in the `ctrace.h` header: ```c #include ``` ### Stack Traces Generate stack traces with `ctrace_generate_trace()`. Often `skip = 0` and `max_depth = SIZE_MAX` is what you want for the parameters. `ctrace_stacktrace_to_string` and `ctrace_print_stacktrace` can then be used for output. `ctrace_free_stacktrace` must be called when you are done with the trace. ```c typedef struct ctrace_stacktrace ctrace_stacktrace; struct ctrace_stacktrace_frame { ctrace_frame_ptr raw_address; ctrace_frame_ptr object_address; uint32_t line; uint32_t column; const char* filename; const char* symbol; ctrace_bool is_inline; }; struct ctrace_stacktrace { ctrace_stacktrace_frame* frames; size_t count; }; ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth); ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color); void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color); void ctrace_free_stacktrace(ctrace_stacktrace* trace); // object_address is stored but if the object_path is needed this can be used ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame); ``` ### Object Traces Object traces contain the most basic information needed to construct a stack trace outside the currently running executable. It contains the raw address, the address in the binary (ASLR and the object file's memory space and whatnot is resolved), and the path to the object the instruction pointer is located in. `ctrace_free_object_trace` must be called when you are done with the trace. ```c typedef struct ctrace_object_trace ctrace_object_trace; struct ctrace_object_frame { ctrace_frame_ptr raw_address; ctrace_frame_ptr obj_address; const char* obj_path; }; struct ctrace_object_trace { ctrace_object_frame* frames; size_t count; }; ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth); ctrace_stacktrace ctrace_resolve_object_trace(const ctrace_object_trace* trace); void ctrace_free_object_trace(ctrace_object_trace* trace); ``` ### Raw Traces Raw traces are arrays of program counters. These are ideal for fast and cheap traces you want to resolve later. Note it is important executables and shared libraries in memory aren't somehow unmapped otherwise libdl calls (and `GetModuleFileName` in windows) will fail to figure out where the program counter corresponds to. `ctrace_free_raw_trace` must be called when you are done with the trace. ```c typedef struct ctrace_raw_trace ctrace_raw_trace; ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth); ctrace_stacktrace ctrace_resolve_raw_trace(const ctrace_raw_trace* trace); void ctrace_free_raw_trace(ctrace_raw_trace* trace); ``` ### Utilities `ctrace_demangle`: Helper function for name demangling `ctrace_stdin_fileno`, `ctrace_stderr_fileno`, `ctrace_stdout_fileno`: Returns the appropriate file descriptor for the respective standard stream. `ctrace_isatty`: Checks if a file descriptor corresponds to a tty device. ```c ctrace_owning_string ctrace_demangle(const char* mangled); int ctrace_stdin_fileno(void); int ctrace_stderr_fileno(void); int ctrace_stdout_fileno(void); ctrace_bool ctrace_isatty(int fd); ``` ### Utility types For ABI reasons `ctrace_bool`s are used for bools. `ctrace_owning_string` is a wrapper type which indicates that a string is owned and must be freed. ```c typedef int8_t ctrace_bool; typedef struct { const char* data; } ctrace_owning_string; ctrace_owning_string ctrace_generate_owning_string(const char* raw_string); void ctrace_free_owning_string(ctrace_owning_string* string); ``` ### Configuration `experimental::set_cache_mode`: Control time-memory tradeoffs within the library. By default speed is prioritized. If using this function, set the cache mode at the very start of your program before any traces are performed. Note: This API is not set in stone yet and could change in the future. `ctrace_enable_inlined_call_resolution`: Configure whether the library will attempt to resolve inlined call information for release builds. Default is true. ```c typedef enum { /* Only minimal lookup tables */ ctrace_prioritize_memory = 0, /* Build lookup tables but don't keep them around between trace calls */ ctrace_hybrid = 1, /* Build lookup tables as needed */ ctrace_prioritize_speed = 2 } ctrace_cache_mode; void ctrace_set_cache_mode(ctrace_cache_mode mode); void ctrace_enable_inlined_call_resolution(ctrace_bool enable); ``` ### Signal-Safe Tracing For more details on the signal-safe tracing interface please refer to the README and the [signal-safe-tracing.md](signal-safe-tracing.md) guide. ```c typedef struct ctrace_safe_object_frame ctrace_safe_object_frame; struct ctrace_safe_object_frame { ctrace_frame_ptr raw_address; ctrace_frame_ptr relative_obj_address; char object_path[CTRACE_PATH_MAX + 1]; }; size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth); void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out); ctrace_bool ctrace_can_signal_safe_unwind(); ctrace_bool ctrace_can_get_safe_object_frame(); ``` cpptrace-1.0.4/docs/signal-safe-tracing.md000066400000000000000000000232201504061443700204100ustar00rootroot00000000000000# Signal-Safe Stack Tracing - [Overview](#overview) - [Big-Picture](#big-picture) - [API](#api) - [Strategy](#strategy) - [Technical Requirements](#technical-requirements) - [Signal-Safe Tracing With `fork()` + `exec()`](#signal-safe-tracing-with-fork--exec) - [In the main program](#in-the-main-program) - [In the tracer program](#in-the-tracer-program) # Overview Stack traces from signal handlers can provide very helpful information for debugging application crashes, e.g. from SIGSEGV or SIGTRAP handlers. Signal handlers are really restrictive environments as your application could be interrupted by a signal at any point, including in the middle of malloc or buffered IO or while holding a lock. Doing a stack trace in a signal handler is possible but it requires a lot of care. This is difficult to do correctly and most examples online do this incorrectly. It is not possible to resolve debug symbols safely in the process from a signal handler without heroic effort. In order to produce a full trace there are three options: 1. Carefully save the object trace information to be resolved at a later time outside the signal handler 2. Write the object trace information to a file to be resolved later 3. Spawn a new process, communicate object trace information to that process, and have that process do the trace resolution For traces on segfaults, e.g., only options 2 and 3 are viable. The this guide will go over approach 3 and the cpptrace safe API. # Big-Picture In order to do this full process safely the way to go is collecting basic information in the signal handler and then either resolving later or handing that information to another process to resolve. It's not as simple as calling `cpptrace::generate_trace().print()` but this is what is needed to really do this safely as far as I can tell. FAQ: What's the worst that could happen if you call `cpptrace::generate_trace().print()` from a signal handler? In many cases you might be able to get away with it but you risk deadlocking or memory corruption. > [!IMPORTANT] > Currently signal-safe stack unwinding is only possible with `libunwind`, more details later. > [!IMPORTANT] > `_dl_find_object` is required for signal-safe stack tracing. This is a relatively recent addition to glibc, added in > glibc 2.35. > [!CAUTION] > Calls to shared objects can be lazy-loaded where the first call to the shared object invokes non-signal-safe functions > such as `malloc()`. Because of this, the signal safe api must be "warmed up" ahead of a signal handler. # API Cpptrace provides APIs for generating raw trace information safely and then also safely resolving those raw pointers to the most minimal object information needed to resolve later. ```cpp namespace cpptrace { // signal-safe std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip = 0); // signal-safe std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth); struct safe_object_frame { frame_ptr raw_address; frame_ptr address_relative_to_object_start; char object_path[CPPTRACE_PATH_MAX + 1]; object_frame resolve() const; // To be called outside a signal handler. Not signal safe. }; // signal-safe void get_safe_object_frame(frame_ptr address, safe_object_frame* out); // signal-safe bool can_signal_safe_unwind(); bool can_get_safe_object_frame(); } ``` `safe_generate_raw_trace` produces instruction pointers found while unwinding the stack. These addresses are sufficient information to resolve a stack trace in the currently running process after a signal handler exits, but they aren't sufficient for resolving outside of the currently running process, unless there is no position-independent code or shared-library code. To resolve outside the current process `safe_object_frame` information is needed. This contains the path to the object where the address is located as well as the address before address randomization. # Strategy Signal-safe tracing can be done three ways: - In a signal handler, call `safe_generate_raw_trace` and then outside a signal handler construct a `cpptrace:raw_trace` and resolve. - In a signal handler, call `safe_generate_raw_trace`, then write `cpptrace::safe_object_frame` information to a file to be resolved later. - In a signal handler, call `safe_generate_raw_trace`, `fork()` and `exec()` a process to handle the resolution, pass `cpptrace::safe_object_frame` information to that child through a pipe, and wait for the child to exit. It's not as simple as calling `cpptrace::generate_trace().print()`, I know, but these are truly the only ways to do this safely as far as I can tell. # Technical Requirements **Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported `safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe way then `get_safe_object_frame` will not populate fields beyond the `raw_address`. `cpptrace::can_signal_safe_unwind` and `cpptrace::can_get_safe_object_frame` can be used to check for safe tracing support. Currently the only back-end that can unwind safely is libunwind. Currently, the only way I know to get `dladdr`'s information in a signal-safe manner is `_dl_find_object`, which doesn't exist on macos (or windows of course). If anyone knows ways to do these safely on other platforms, I'd be much appreciative. # Signal-Safe Tracing With `fork()` + `exec()` Of the three strategies, `fork()` + `exec()`, is the most technically involved and the only way to resolve while the signal handler is running. I think it's worthwhile to do a deep-dive into how to do this. In the source code, [`signal_demo.cpp`](../test/signal_demo.cpp) and [`signal_tracer.cpp`](../test/signal_tracer.cpp) provide a working example for what is described here. ## In the main program The main program handles most of the complexity for tracing from signal handlers: - Collecting a raw trace - Spawning a child process - Resolving raw frame pointers to minimal object frames - Sending that info to the other process Also note: A warmup for the library is done in main. A basic implementation is as follows: ```cpp #include #include #include #include #include // This is just a utility I like, it makes the pipe API more expressive. struct pipe_t { union { struct { int read_end; int write_end; }; int data[2]; }; }; void do_signal_safe_trace(cpptrace::frame_ptr* buffer, std::size_t count) { // Setup pipe and spawn child pipe_t input_pipe; pipe(input_pipe.data); const pid_t pid = fork(); if(pid == -1) { const char* fork_failure_message = "fork() failed\n"; write(STDERR_FILENO, fork_failure_message, strlen(fork_failure_message)); return; } if(pid == 0) { // child dup2(input_pipe.read_end, STDIN_FILENO); close(input_pipe.read_end); close(input_pipe.write_end); execl("signal_tracer", "signal_tracer", nullptr); const char* exec_failure_message = "exec(signal_tracer) failed: Make sure the signal_tracer executable is in " "the current working directory and the binary's permissions are correct.\n"; write(STDERR_FILENO, exec_failure_message, strlen(exec_failure_message)); _exit(1); } // Resolve to safe_object_frames and write those to the pipe for(std::size_t i = 0; i < count; i++) { cpptrace::safe_object_frame frame; cpptrace::get_safe_object_frame(buffer[i], &frame); write(input_pipe.write_end, &frame, sizeof(frame)); } close(input_pipe.read_end); close(input_pipe.write_end); // Wait for child waitpid(pid, nullptr, 0); } void handler(int signo, siginfo_t* info, void* context) { // Print basic message const char* message = "SIGSEGV occurred:\n"; write(STDERR_FILENO, message, strlen(message)); // Generate trace constexpr std::size_t N = 100; cpptrace::frame_ptr buffer[N]; std::size_t count = cpptrace::safe_generate_raw_trace(buffer, N); do_signal_safe_trace(buffer, count); // Up to you if you want to exit or continue or whatever _exit(1); } void warmup_cpptrace() { // This is done for any dynamic-loading shenanigans cpptrace::frame_ptr buffer[10]; std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10); cpptrace::safe_object_frame frame; cpptrace::get_safe_object_frame(buffer[0], &frame); } int main() { warmup_cpptrace(); // Setup signal handler struct sigaction action = { 0 }; action.sa_flags = 0; action.sa_sigaction = &handler; if (sigaction(SIGSEGV, &action, NULL) == -1) { perror("sigaction"); } /// ... } ``` ## In the tracer program The tracer program is quite simple. It just has to read `cpptrace::safe_object_frame`s from the pipe, resolve to `cpptrace::object_frame`s, and resolve an `object_trace`. ```cpp #include #include #include #include int main() { cpptrace::object_trace trace; while(true) { cpptrace::safe_object_frame frame; // fread used over read because a read() from a pipe might not read the full frame std::size_t res = fread(&frame, sizeof(frame), 1, stdin); if(res == 0) { break; } else if(res != 1) { std::cerr<<"Something went wrong while reading from the pipe"< #include #include #include #include #ifdef _WIN32 #define CPPTRACE_EXPORT_ATTR __declspec(dllexport) #define CPPTRACE_IMPORT_ATTR __declspec(dllimport) #else #define CPPTRACE_EXPORT_ATTR __attribute__((visibility("default"))) #define CPPTRACE_IMPORT_ATTR __attribute__((visibility("default"))) #endif #ifdef CPPTRACE_STATIC_DEFINE # define CPPTRACE_EXPORT # define CPPTRACE_NO_EXPORT #else # ifndef CPPTRACE_EXPORT # ifdef cpptrace_lib_EXPORTS /* We are building this library */ # define CPPTRACE_EXPORT CPPTRACE_EXPORT_ATTR # else /* We are using this library */ # define CPPTRACE_EXPORT CPPTRACE_IMPORT_ATTR # endif # endif #endif #if __cplusplus >= 201703L #define CONSTEXPR_SINCE_CPP17 constexpr #else #define CONSTEXPR_SINCE_CPP17 #endif #ifdef _MSC_VER #define CPPTRACE_FORCE_NO_INLINE __declspec(noinline) #else #define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline)) #endif #ifdef _MSC_VER #pragma warning(push) // warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector and others for some // reason // 4275 is the same thing but for base classes #pragma warning(disable: 4251; disable: 4275) #endif CPPTRACE_BEGIN_NAMESPACE struct CPPTRACE_EXPORT raw_trace { std::vector frames; static raw_trace current(std::size_t skip = 0); static raw_trace current(std::size_t skip, std::size_t max_depth); object_trace resolve_object_trace() const; stacktrace resolve() const; void clear(); bool empty() const noexcept; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; inline iterator begin() noexcept { return frames.begin(); } inline iterator end() noexcept { return frames.end(); } inline const_iterator begin() const noexcept { return frames.begin(); } inline const_iterator end() const noexcept { return frames.end(); } inline const_iterator cbegin() const noexcept { return frames.cbegin(); } inline const_iterator cend() const noexcept { return frames.cend(); } }; struct CPPTRACE_EXPORT object_frame { frame_ptr raw_address; frame_ptr object_address; std::string object_path; }; struct CPPTRACE_EXPORT object_trace { std::vector frames; static object_trace current(std::size_t skip = 0); static object_trace current(std::size_t skip, std::size_t max_depth); stacktrace resolve() const; void clear(); bool empty() const noexcept; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; inline iterator begin() noexcept { return frames.begin(); } inline iterator end() noexcept { return frames.end(); } inline const_iterator begin() const noexcept { return frames.begin(); } inline const_iterator end() const noexcept { return frames.end(); } inline const_iterator cbegin() const noexcept { return frames.cbegin(); } inline const_iterator cend() const noexcept { return frames.cend(); } }; // This represents a nullable integer type // The max value of the type is used as a sentinel // This is used over std::optional because the library is C++11 and also std::optional is a bit heavy-duty for this // use. template::value, int>::type = 0> struct nullable { T raw_value = null_value(); constexpr nullable() noexcept = default; constexpr nullable(T value) noexcept : raw_value(value) {} CONSTEXPR_SINCE_CPP17 nullable& operator=(T value) noexcept { raw_value = value; return *this; } constexpr bool has_value() const noexcept { return raw_value != null_value(); } CONSTEXPR_SINCE_CPP17 T& value() noexcept { return raw_value; } constexpr const T& value() const noexcept { return raw_value; } constexpr T value_or(T alternative) const noexcept { return has_value() ? raw_value : alternative; } CONSTEXPR_SINCE_CPP17 void swap(nullable& other) noexcept { std::swap(raw_value, other.raw_value); } CONSTEXPR_SINCE_CPP17 void reset() noexcept { raw_value = (std::numeric_limits::max)(); } constexpr bool operator==(const nullable& other) const noexcept { return raw_value == other.raw_value; } constexpr bool operator!=(const nullable& other) const noexcept { return raw_value != other.raw_value; } constexpr static T null_value() noexcept { return (std::numeric_limits::max)(); } constexpr static nullable null() noexcept { return { null_value() }; } }; struct CPPTRACE_EXPORT stacktrace_frame { frame_ptr raw_address; frame_ptr object_address; nullable line; nullable column; std::string filename; std::string symbol; bool is_inline; bool operator==(const stacktrace_frame& other) const { return raw_address == other.raw_address && object_address == other.object_address && line == other.line && column == other.column && filename == other.filename && symbol == other.symbol; } bool operator!=(const stacktrace_frame& other) const { return !operator==(other); } object_frame get_object_info() const; std::string to_string() const; std::string to_string(bool color) const; friend CPPTRACE_EXPORT std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame); }; struct CPPTRACE_EXPORT stacktrace { std::vector frames; static stacktrace current(std::size_t skip = 0); static stacktrace current(std::size_t skip, std::size_t max_depth); void print() const; void print(std::ostream& stream) const; void print(std::ostream& stream, bool color) const; void print_with_snippets() const; void print_with_snippets(std::ostream& stream) const; void print_with_snippets(std::ostream& stream, bool color) const; void clear(); bool empty() const noexcept; std::string to_string(bool color = false) const; friend CPPTRACE_EXPORT std::ostream& operator<<(std::ostream& stream, const stacktrace& trace); using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; inline iterator begin() noexcept { return frames.begin(); } inline iterator end() noexcept { return frames.end(); } inline const_iterator begin() const noexcept { return frames.begin(); } inline const_iterator end() const noexcept { return frames.end(); } inline const_iterator cbegin() const noexcept { return frames.cbegin(); } inline const_iterator cend() const noexcept { return frames.cend(); } private: friend void print_terminate_trace(); }; CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip = 0); CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth); CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip = 0); CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip, std::size_t max_depth); CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip = 0); CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip, std::size_t max_depth); // Path max isn't so simple, so I'm choosing 4096 which seems to encompass what all major OS's expect and should be // fine in all reasonable cases. // https://eklitzke.org/path-max-is-tricky // https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html #define CPPTRACE_PATH_MAX 4096 // safe tracing interface // signal-safe CPPTRACE_EXPORT std::size_t safe_generate_raw_trace( frame_ptr* buffer, std::size_t size, std::size_t skip = 0 ); // signal-safe CPPTRACE_EXPORT std::size_t safe_generate_raw_trace( frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth ); struct CPPTRACE_EXPORT safe_object_frame { frame_ptr raw_address; // This ends up being the real object address. It was named at a time when I thought the object base address // still needed to be added in frame_ptr address_relative_to_object_start; char object_path[CPPTRACE_PATH_MAX + 1]; // To be called outside a signal handler. Not signal safe. object_frame resolve() const; }; // signal-safe CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out); CPPTRACE_EXPORT bool can_signal_safe_unwind(); CPPTRACE_EXPORT bool can_get_safe_object_frame(); // JIT API CPPTRACE_EXPORT void register_jit_object(const char*, std::size_t); CPPTRACE_EXPORT void unregister_jit_object(const char*); CPPTRACE_EXPORT void clear_all_jit_objects(); CPPTRACE_END_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) #endif #endif cpptrace-1.0.4/include/cpptrace/cpptrace.hpp000066400000000000000000000002551504061443700210610ustar00rootroot00000000000000#ifndef CPPTRACE_HPP #define CPPTRACE_HPP #include #include #include #include #endif cpptrace-1.0.4/include/cpptrace/exceptions.hpp000066400000000000000000000164541504061443700214510ustar00rootroot00000000000000#ifndef CPPTRACE_EXCEPTIONS_HPP #define CPPTRACE_EXCEPTIONS_HPP #include #include #include #include #ifdef _MSC_VER #pragma warning(push) // warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector and others for some // reason // 4275 is the same thing but for base classes #pragma warning(disable: 4251; disable: 4275) #endif CPPTRACE_BEGIN_NAMESPACE // tracing exceptions: namespace detail { // This is a helper utility, if the library weren't C++11 an std::variant would be used class CPPTRACE_EXPORT lazy_trace_holder { bool resolved; union { raw_trace trace; stacktrace resolved_trace; }; public: // constructors lazy_trace_holder() : resolved(false), trace() {} explicit lazy_trace_holder(raw_trace&& _trace) : resolved(false), trace(std::move(_trace)) {} explicit lazy_trace_holder(stacktrace&& _resolved_trace) : resolved(true), resolved_trace(std::move(_resolved_trace)) {} // logistics lazy_trace_holder(const lazy_trace_holder& other); lazy_trace_holder(lazy_trace_holder&& other) noexcept; lazy_trace_holder& operator=(const lazy_trace_holder& other); lazy_trace_holder& operator=(lazy_trace_holder&& other) noexcept; ~lazy_trace_holder(); // access const raw_trace& get_raw_trace() const; stacktrace& get_resolved_trace(); const stacktrace& get_resolved_trace() const; bool is_resolved() const; private: void clear(); }; CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth); CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip = 0); } // Interface for a traced exception object class CPPTRACE_EXPORT exception : public std::exception { public: const char* what() const noexcept override = 0; virtual const char* message() const noexcept = 0; virtual const stacktrace& trace() const noexcept = 0; }; // Cpptrace traced exception object // I hate to have to expose anything about implementation detail but the idea here is that class CPPTRACE_EXPORT lazy_exception : public exception { mutable detail::lazy_trace_holder trace_holder; mutable std::string what_string; public: explicit lazy_exception( raw_trace&& trace = detail::get_raw_trace_and_absorb() ) : trace_holder(std::move(trace)) {} // std::exception const char* what() const noexcept override; // cpptrace::exception const char* message() const noexcept override; const stacktrace& trace() const noexcept override; }; class CPPTRACE_EXPORT exception_with_message : public lazy_exception { mutable std::string user_message; public: explicit exception_with_message( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {} const char* message() const noexcept override; }; class CPPTRACE_EXPORT logic_error : public exception_with_message { public: explicit logic_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT domain_error : public exception_with_message { public: explicit domain_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT invalid_argument : public exception_with_message { public: explicit invalid_argument( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT length_error : public exception_with_message { public: explicit length_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT out_of_range : public exception_with_message { public: explicit out_of_range( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT runtime_error : public exception_with_message { public: explicit runtime_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT range_error : public exception_with_message { public: explicit range_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT overflow_error : public exception_with_message { public: explicit overflow_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT underflow_error : public exception_with_message { public: explicit underflow_error( std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : exception_with_message(std::move(message_arg), std::move(trace)) {} }; class CPPTRACE_EXPORT nested_exception : public lazy_exception { std::exception_ptr ptr; mutable std::string message_value; public: explicit nested_exception( const std::exception_ptr& exception_ptr, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept : lazy_exception(std::move(trace)), ptr(exception_ptr) {} const char* message() const noexcept override; std::exception_ptr nested_ptr() const noexcept; }; class CPPTRACE_EXPORT system_error : public runtime_error { std::error_code ec; public: explicit system_error( int error_code, std::string&& message_arg, raw_trace&& trace = detail::get_raw_trace_and_absorb() ) noexcept; const std::error_code& code() const noexcept; }; // [[noreturn]] must come first due to old clang [[noreturn]] CPPTRACE_EXPORT void rethrow_and_wrap_if_needed(std::size_t skip = 0); CPPTRACE_END_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) #endif #endif cpptrace-1.0.4/include/cpptrace/exceptions_macros.hpp000066400000000000000000000010741504061443700230050ustar00rootroot00000000000000#ifndef CPPTRACE_EXCEPTIONS_MACROS_HPP #define CPPTRACE_EXCEPTIONS_MACROS_HPP // Exception wrapper utilities #define CPPTRACE_WRAP_BLOCK(statements) do { \ try { \ statements \ } catch(...) { \ ::cpptrace::rethrow_and_wrap_if_needed(); \ } \ } while(0) #define CPPTRACE_WRAP(expression) [&] () -> decltype((expression)) { \ try { \ return expression; \ } catch(...) { \ ::cpptrace::rethrow_and_wrap_if_needed(1); \ } \ } () #endif // CPPTRACE_EXCEPTIONS_MACROS_HPP cpptrace-1.0.4/include/cpptrace/formatting.hpp000066400000000000000000000070321504061443700214320ustar00rootroot00000000000000#ifndef CPPTRACE_FORMATTING_HPP #define CPPTRACE_FORMATTING_HPP #include #include #include CPPTRACE_BEGIN_NAMESPACE class CPPTRACE_EXPORT formatter { class impl; // can't be a std::unique_ptr due to msvc awfulness with dllimport/dllexport and https://stackoverflow.com/q/4145605/15675011 impl* pimpl; public: formatter(); ~formatter(); formatter(formatter&&); formatter(const formatter&); formatter& operator=(formatter&&); formatter& operator=(const formatter&); formatter& header(std::string); enum class color_mode { always, none, automatic, }; formatter& colors(color_mode); enum class address_mode { raw, object, none, }; formatter& addresses(address_mode); enum class path_mode { // full path is used full, // only the file name is used basename, }; formatter& paths(path_mode); formatter& snippets(bool); formatter& snippet_context(int); formatter& columns(bool); enum class symbol_mode { // full demangled symbol full, // symbols are prettified to clean up some especially long template argument lists pretty, // template arguments and function parameters are pruned pruned }; formatter& symbols(symbol_mode); formatter& filtered_frame_placeholders(bool); formatter& filter(std::function); formatter& transform(std::function); formatter& break_before_filename(bool do_break = true); formatter& hide_exception_machinery(bool do_hide = true); std::string format(const stacktrace_frame&) const; std::string format(const stacktrace_frame&, bool color) const; // The last argument is the indent to use for the filename, if break_before_filename is set std::string format(const stacktrace_frame&, bool color, size_t filename_indent) const; std::string format(const stacktrace&) const; std::string format(const stacktrace&, bool color) const; void print(const stacktrace_frame&) const; void print(const stacktrace_frame&, bool color) const; void print(std::ostream&, const stacktrace_frame&) const; void print(std::ostream&, const stacktrace_frame&, bool color) const; // The last argument is the indent to use for the filename, if break_before_filename is set void print(std::ostream&, const stacktrace_frame&, bool color, size_t filename_indent) const; void print(std::FILE*, const stacktrace_frame&) const; void print(std::FILE*, const stacktrace_frame&, bool color) const; // The last argument is the indent to use for the filename, if break_before_filename is set void print(std::FILE*, const stacktrace_frame&, bool color, size_t filename_indent) const; void print(const stacktrace&) const; void print(const stacktrace&, bool color) const; void print(std::ostream&, const stacktrace&) const; void print(std::ostream&, const stacktrace&, bool color) const; void print(std::FILE*, const stacktrace&) const; void print(std::FILE*, const stacktrace&, bool color) const; }; CPPTRACE_EXPORT const formatter& get_default_formatter(); CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/include/cpptrace/forward.hpp000066400000000000000000000010651504061443700207240ustar00rootroot00000000000000#ifndef CPPTRACE_FORWARD_HPP #define CPPTRACE_FORWARD_HPP #include #define CPPTRACE_BEGIN_NAMESPACE \ namespace cpptrace { \ inline namespace v1 { #define CPPTRACE_END_NAMESPACE \ } \ } CPPTRACE_BEGIN_NAMESPACE // Some type sufficient for an instruction pointer, currently always an alias to std::uintptr_t using frame_ptr = std::uintptr_t; struct raw_trace; struct object_trace; struct stacktrace; struct object_frame; struct stacktrace_frame; struct safe_object_frame; CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/include/cpptrace/from_current.hpp000066400000000000000000000134731504061443700217730ustar00rootroot00000000000000#ifndef CPPTRACE_FROM_CURRENT_HPP #define CPPTRACE_FROM_CURRENT_HPP #include #include #include #ifdef _MSC_VER #include #endif #include #include CPPTRACE_BEGIN_NAMESPACE CPPTRACE_EXPORT const raw_trace& raw_trace_from_current_exception(); CPPTRACE_EXPORT const stacktrace& from_current_exception(); CPPTRACE_EXPORT const raw_trace& raw_trace_from_current_exception_rethrow(); CPPTRACE_EXPORT const stacktrace& from_current_exception_rethrow(); CPPTRACE_EXPORT bool current_exception_was_rethrown(); CPPTRACE_NORETURN CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void rethrow(); CPPTRACE_NORETURN CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void rethrow(std::exception_ptr exception); CPPTRACE_EXPORT void clear_current_exception_traces(); namespace detail { template struct argument; template struct argument { using type = Arg; }; template struct argument { using type = void; }; #ifdef _MSC_VER CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE int maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, int filter_result); CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info); template CPPTRACE_FORCE_NO_INLINE inline int exception_filter(EXCEPTION_POINTERS* exception_ptrs) { maybe_collect_trace(exception_ptrs, typeid(E)); return EXCEPTION_CONTINUE_SEARCH; } class dont_return_from_try_catch_macros { public: explicit dont_return_from_try_catch_macros() = default; }; #else CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void maybe_collect_trace(const std::type_info*, const std::type_info*, void**, unsigned); template class unwind_interceptor { public: static int init; CPPTRACE_FORCE_NO_INLINE static bool can_catch( const std::type_info* /* this */, const std::type_info* throw_type, void** throw_obj, unsigned outer ) { maybe_collect_trace(&typeid(T), throw_type, throw_obj, outer); return false; } }; CPPTRACE_EXPORT void do_prepare_unwind_interceptor( const std::type_info&, bool(*)(const std::type_info*, const std::type_info*, void**, unsigned) ); template inline int prepare_unwind_interceptor() { do_prepare_unwind_interceptor(typeid(unwind_interceptor), unwind_interceptor::can_catch); return 1; } template int unwind_interceptor::init = prepare_unwind_interceptor(); template using unwind_interceptor_for = unwind_interceptor::type>; inline void nop(int) {} #endif } namespace detail { template Arg get_callable_argument_helper(R(*) (Arg)); template Arg get_callable_argument_helper(R(F::*) (Arg)); template Arg get_callable_argument_helper(R(F::*) (Arg) const); template void get_callable_argument_helper(R(*) ()); template void get_callable_argument_helper(R(F::*) ()); template void get_callable_argument_helper(R(F::*) () const); template decltype(get_callable_argument_helper(&F::operator())) get_callable_argument_wrapper(F); template using get_callable_argument = decltype(get_callable_argument_wrapper(std::declval())); template::value, int>::type = 0> void do_try_catch(F&& f, Catch&& catcher) { CPPTRACE_TRY { std::forward(f)(); } CPPTRACE_CATCH(E e) { std::forward(catcher)(std::forward(e)); } } template::value, int>::type = 0> void do_try_catch(F&& f, Catch&& catcher) { CPPTRACE_TRY { std::forward(f)(); } CPPTRACE_CATCH(...) { std::forward(catcher)(); } } template void try_catch_impl(F&& f) { std::forward(f)(); } // TODO: This could be made more efficient to reduce the number of interceptor levels that do typeid checks // and possible traces template void try_catch_impl(F&& f, Catch&& catcher, Catches&&... catches) { // match the first catch at the inner-most level... no real way to reverse a pack or extract from the end so // we have to wrap with a lambda auto wrapped = [&] () { using E = get_callable_argument; do_try_catch(std::forward(f), std::forward(catcher)); }; try_catch_impl(std::move(wrapped), std::forward(catches)...); } } template void try_catch(F&& f, Catches&&... catches) { return detail::try_catch_impl(std::forward(f), std::forward(catches)...); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/include/cpptrace/from_current_macros.hpp000066400000000000000000000054501504061443700233330ustar00rootroot00000000000000#ifndef CPPTRACE_FROM_CURRENT_MACROS_HPP #define CPPTRACE_FROM_CURRENT_MACROS_HPP // https://godbolt.org/z/4MsT6KqP1 #ifdef _MSC_VER #define CPPTRACE_UNREACHABLE() __assume(false) #else #define CPPTRACE_UNREACHABLE() __builtin_unreachable() #endif // https://godbolt.org/z/7neGPEche // gcc added support in 4.8 but I'm too lazy to check the minor version #if defined(__GNUC__) && (__GNUC__ < 5) #define CPPTRACE_NORETURN __attribute__((noreturn)) #else #define CPPTRACE_NORETURN [[noreturn]] #endif #ifdef _MSC_VER #if defined(__clang__) #define CPPTRACE_PUSH_EXTENSION_WARNINGS \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") #define CPPTRACE_POP_EXTENSION_WARNINGS \ _Pragma("clang diagnostic pop") #else #define CPPTRACE_PUSH_EXTENSION_WARNINGS #define CPPTRACE_POP_EXTENSION_WARNINGS #endif #define CPPTRACE_TYPE_FOR(param) \ typename ::cpptrace::detail::argument::type // this awful double-IILE is due to C2713 "You can't use structured exception handling (__try/__except) and C++ // exception handling (try/catch) in the same function." #define CPPTRACE_TRY \ try { \ [&]() -> ::cpptrace::detail::dont_return_from_try_catch_macros { \ CPPTRACE_PUSH_EXTENSION_WARNINGS \ __try { \ CPPTRACE_POP_EXTENSION_WARNINGS \ return [&]() -> ::cpptrace::detail::dont_return_from_try_catch_macros { #define CPPTRACE_CATCH(param) \ return ::cpptrace::detail::dont_return_from_try_catch_macros(); \ }(); \ CPPTRACE_PUSH_EXTENSION_WARNINGS \ } __except(::cpptrace::detail::exception_filter(GetExceptionInformation())) { \ CPPTRACE_POP_EXTENSION_WARNINGS \ CPPTRACE_UNREACHABLE(); \ } \ }(); \ } catch(param) #define CPPTRACE_SEH_TRY __try #define CPPTRACE_SEH_EXCEPT(filter) \ __except(::cpptrace::detail::maybe_collect_trace(GetExceptionInformation(), (filter))) #else #define CPPTRACE_UNWIND_INTERCEPTOR_FOR(param) \ ::cpptrace::detail::unwind_interceptor_for #define CPPTRACE_TRY \ try { \ try { #define CPPTRACE_CATCH(param) \ } catch(const CPPTRACE_UNWIND_INTERCEPTOR_FOR(param)&) { \ CPPTRACE_UNREACHABLE(); \ /* force instantiation of the init-er */ \ ::cpptrace::detail::nop(CPPTRACE_UNWIND_INTERCEPTOR_FOR(param)::init); \ } \ } catch(param) #endif #ifdef CPPTRACE_UNPREFIXED_TRY_CATCH #define TRY CPPTRACE_TRY #define CATCH(param) CPPTRACE_CATCH(param) #ifdef _MSC_VER #define SEH_TRY CPPTRACE_SEH_TRY #define SEH_EXCEPT(filter) CPPTRACE_SEH_EXCEPT(filter) #endif #endif #endif // CPPTRACE_FROM_CURRENT_MACROS_HPP cpptrace-1.0.4/include/cpptrace/gdb_jit.hpp000066400000000000000000000026131504061443700206620ustar00rootroot00000000000000#ifndef CPPTRACE_GDB_JIT_HPP #define CPPTRACE_GDB_JIT_HPP #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { // https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html extern "C" { typedef enum { JIT_NOACTION = 0, JIT_REGISTER_FN, JIT_UNREGISTER_FN } jit_actions_t; struct jit_code_entry { struct jit_code_entry *next_entry; struct jit_code_entry *prev_entry; const char *symfile_addr; uint64_t symfile_size; }; struct jit_descriptor { uint32_t version; /* This type should be jit_actions_t, but we use uint32_t to be explicit about the bitwidth. */ uint32_t action_flag; struct jit_code_entry *relevant_entry; struct jit_code_entry *first_entry; }; extern struct jit_descriptor __jit_debug_descriptor; } } namespace experimental { inline void register_jit_objects_from_gdb_jit_interface() { clear_all_jit_objects(); detail::jit_code_entry* entry = detail::__jit_debug_descriptor.first_entry; while(entry) { register_jit_object(entry->symfile_addr, entry->symfile_size); entry = entry->next_entry; } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/include/cpptrace/io.hpp000066400000000000000000000026271504061443700176740ustar00rootroot00000000000000#ifndef CPPTRACE_IO_HPP #define CPPTRACE_IO_HPP #include #include #ifndef CPPTRACE_NO_STD_FORMAT #if __cplusplus >= 202002L #ifdef __has_include #if __has_include() #define CPPTRACE_STD_FORMAT #include #endif #endif #endif #endif #ifdef _MSC_VER #pragma warning(push) // warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector and others for some // reason // 4275 is the same thing but for base classes #pragma warning(disable: 4251; disable: 4275) #endif CPPTRACE_BEGIN_NAMESPACE CPPTRACE_EXPORT std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame); CPPTRACE_EXPORT std::ostream& operator<<(std::ostream& stream, const stacktrace& trace); CPPTRACE_END_NAMESPACE #if defined(CPPTRACE_STD_FORMAT) && defined(__cpp_lib_format) template <> struct std::formatter : std::formatter { auto format(cpptrace::stacktrace_frame frame, format_context& ctx) const { return formatter::format(frame.to_string(), ctx); } }; template <> struct std::formatter : std::formatter { auto format(cpptrace::stacktrace trace, format_context& ctx) const { return formatter::format(trace.to_string(), ctx); } }; #endif #ifdef _MSC_VER #pragma warning(pop) #endif #endif cpptrace-1.0.4/include/cpptrace/utils.hpp000066400000000000000000000047741504061443700204320ustar00rootroot00000000000000#ifndef CPPTRACE_UTILS_HPP #define CPPTRACE_UTILS_HPP #include #include #ifdef _MSC_VER #pragma warning(push) // warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector and others for some // reason // 4275 is the same thing but for base classes #pragma warning(disable: 4251; disable: 4275) #endif CPPTRACE_BEGIN_NAMESPACE CPPTRACE_EXPORT std::string demangle(const std::string& name); CPPTRACE_EXPORT std::string basename(const std::string& path); CPPTRACE_EXPORT std::string prettify_symbol(std::string symbol); CPPTRACE_EXPORT std::string prune_symbol(const std::string& symbol); CPPTRACE_EXPORT std::string get_snippet( const std::string& path, std::size_t line, std::size_t context_size, bool color = false ); CPPTRACE_EXPORT std::string get_snippet( const std::string& path, std::size_t line, nullable column, std::size_t context_size, bool color = false ); CPPTRACE_EXPORT bool isatty(int fd); CPPTRACE_EXPORT extern const int stdin_fileno; CPPTRACE_EXPORT extern const int stderr_fileno; CPPTRACE_EXPORT extern const int stdout_fileno; CPPTRACE_EXPORT void register_terminate_handler(); // options: CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb); CPPTRACE_EXPORT void enable_inlined_call_resolution(bool enable); enum class log_level { debug, info, warning, error }; CPPTRACE_EXPORT void set_log_level(log_level level); CPPTRACE_EXPORT void set_log_callback(std::function); CPPTRACE_EXPORT void use_default_stderr_logger(); CPPTRACE_EXPORT const char* to_string(log_level level); enum class cache_mode { // Only minimal lookup tables prioritize_memory = 0, // Build lookup tables but don't keep them around between trace calls hybrid = 1, // Build lookup tables as needed prioritize_speed = 2 }; namespace experimental { CPPTRACE_EXPORT void set_cache_mode(cache_mode mode); } // dwarf options namespace experimental { CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable max_entries); CPPTRACE_EXPORT void set_dwarf_resolver_disable_aranges(bool disable); } // dbghelp #ifdef _WIN32 CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); #endif CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/include/ctrace/000077500000000000000000000000001504061443700162055ustar00rootroot00000000000000cpptrace-1.0.4/include/ctrace/ctrace.h000066400000000000000000000131271504061443700176230ustar00rootroot00000000000000#ifndef CTRACE_H #define CTRACE_H #include #include #include #ifdef _WIN32 #define CPPTRACE_EXPORT_ATTR __declspec(dllexport) #define CPPTRACE_IMPORT_ATTR __declspec(dllimport) #else #define CPPTRACE_EXPORT_ATTR __attribute__((visibility("default"))) #define CPPTRACE_IMPORT_ATTR __attribute__((visibility("default"))) #endif #ifdef CPPTRACE_STATIC_DEFINE # define CPPTRACE_EXPORT # define CPPTRACE_NO_EXPORT #else # ifndef CPPTRACE_EXPORT # ifdef cpptrace_lib_EXPORTS /* We are building this library */ # define CPPTRACE_EXPORT CPPTRACE_EXPORT_ATTR # else /* We are using this library */ # define CPPTRACE_EXPORT CPPTRACE_IMPORT_ATTR # endif # endif #endif #if defined(__cplusplus) #define CTRACE_BEGIN_DEFINITIONS extern "C" { #define CTRACE_END_DEFINITIONS } #else #define CTRACE_BEGIN_DEFINITIONS #define CTRACE_END_DEFINITIONS #endif #ifdef _MSC_VER #define CTRACE_FORCE_NO_INLINE __declspec(noinline) #else #define CTRACE_FORCE_NO_INLINE __attribute__((noinline)) #endif #ifdef _MSC_VER #define CTRACE_FORCE_INLINE __forceinline #elif defined(__clang__) || defined(__GNUC__) #define CTRACE_FORCE_INLINE __attribute__((always_inline)) inline #else #define CTRACE_FORCE_INLINE inline #endif /* See `CPPTRACE_PATH_MAX` for more info. */ #define CTRACE_PATH_MAX 4096 CTRACE_BEGIN_DEFINITIONS typedef struct ctrace_raw_trace ctrace_raw_trace; typedef struct ctrace_object_trace ctrace_object_trace; typedef struct ctrace_stacktrace ctrace_stacktrace; /* Represents a boolean value, ensures a consistent ABI. */ typedef int8_t ctrace_bool; /* A type that can represent a pointer, alias for `uintptr_t`. */ typedef uintptr_t ctrace_frame_ptr; typedef struct ctrace_object_frame ctrace_object_frame; typedef struct ctrace_stacktrace_frame ctrace_stacktrace_frame; typedef struct ctrace_safe_object_frame ctrace_safe_object_frame; /* Type-safe null-terminated string wrapper */ typedef struct { const char* data; } ctrace_owning_string; struct ctrace_object_frame { ctrace_frame_ptr raw_address; ctrace_frame_ptr obj_address; const char* obj_path; }; struct ctrace_stacktrace_frame { ctrace_frame_ptr raw_address; ctrace_frame_ptr object_address; uint32_t line; uint32_t column; const char* filename; const char* symbol; ctrace_bool is_inline; }; struct ctrace_safe_object_frame { ctrace_frame_ptr raw_address; ctrace_frame_ptr relative_obj_address; char object_path[CTRACE_PATH_MAX + 1]; }; struct ctrace_raw_trace { ctrace_frame_ptr* frames; size_t count; }; struct ctrace_object_trace { ctrace_object_frame* frames; size_t count; }; struct ctrace_stacktrace { ctrace_stacktrace_frame* frames; size_t count; }; /* ctrace::string: */ CPPTRACE_EXPORT ctrace_owning_string ctrace_generate_owning_string(const char* raw_string); CPPTRACE_EXPORT void ctrace_free_owning_string(ctrace_owning_string* string); /* ctrace::generation: */ CPPTRACE_EXPORT ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth); CPPTRACE_EXPORT ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth); CPPTRACE_EXPORT ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth); /* ctrace::freeing: */ CPPTRACE_EXPORT void ctrace_free_raw_trace(ctrace_raw_trace* trace); CPPTRACE_EXPORT void ctrace_free_object_trace(ctrace_object_trace* trace); CPPTRACE_EXPORT void ctrace_free_stacktrace(ctrace_stacktrace* trace); /* ctrace::resolve: */ CPPTRACE_EXPORT ctrace_stacktrace ctrace_resolve_raw_trace(const ctrace_raw_trace* trace); CPPTRACE_EXPORT ctrace_object_trace ctrace_resolve_raw_trace_to_object_trace(const ctrace_raw_trace* trace); CPPTRACE_EXPORT ctrace_stacktrace ctrace_resolve_object_trace(const ctrace_object_trace* trace); /* ctrace::safe: */ CPPTRACE_EXPORT size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth); CPPTRACE_EXPORT void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out); CPPTRACE_EXPORT ctrace_bool ctrace_can_signal_safe_unwind(void); CPPTRACE_EXPORT ctrace_bool ctrace_can_get_safe_object_frame(void); /* ctrace::io: */ CPPTRACE_EXPORT ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color); CPPTRACE_EXPORT void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color); /* ctrace::utility: */ CPPTRACE_EXPORT ctrace_owning_string ctrace_demangle(const char* mangled); CPPTRACE_EXPORT int ctrace_stdin_fileno(void); CPPTRACE_EXPORT int ctrace_stderr_fileno(void); CPPTRACE_EXPORT int ctrace_stdout_fileno(void); CPPTRACE_EXPORT ctrace_bool ctrace_isatty(int fd); CPPTRACE_EXPORT ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame); /* ctrace::config: */ typedef enum { /* Only minimal lookup tables */ ctrace_prioritize_memory = 0, /* Build lookup tables but don't keep them around between trace calls */ ctrace_hybrid = 1, /* Build lookup tables as needed */ ctrace_prioritize_speed = 2 } ctrace_cache_mode; CPPTRACE_EXPORT void ctrace_set_cache_mode(ctrace_cache_mode mode); CPPTRACE_EXPORT void ctrace_enable_inlined_call_resolution(ctrace_bool enable); CTRACE_END_DEFINITIONS #endif cpptrace-1.0.4/res/000077500000000000000000000000001504061443700141125ustar00rootroot00000000000000cpptrace-1.0.4/res/demo.png000066400000000000000000002640541504061443700155570ustar00rootroot00000000000000‰PNG  IHDR¦9Û\õsRGB®ÎégAMA± üa pHYsÃÃÇo¨dciTXtSnipMetadata{"clipPoints":[{"x":0,"y":0},{"x":1049,"y":0},{"x":1049,"y":422},{"x":0,"y":422}]}ÂóøØÿ6IDATx^ìý pW¶ç þgÚ2è€$,>$$#ÑâõEû†ŒnqÙàÕA5aàúñ‚ƒ(OÙ.GÅ} î'èxñˆ¶‚2î޲±Ÿ Ž01ŽºØÊL—ÆæB[XqÝ6ÈÓâ¼€±G–dñ¡#é®À~1³×Þ;Ofž³wž’Ùë‘væÊ}ÖÞ¹3IåZ{íµÿOÿê_ÿ›ÿ†a†a†a†anÿ³þ?Ã0 Ã0 Ã0 Ã0Ì ÁN†a†a†a†aB ÌÍeÙo±ãÅßâçúa†a†a†ùñò/îŸ^ñ¿èý‚¸ïÿñjþÝ ú³–„ÁDù»PV“Äè?ýw-»ÍÌ÷o«Ñqò¬≿û¬zh!&ó%z®’l6~±y ~õðCxè!±Í/U¿¡ßÿO+•ÌÙä¹6—Oá蛃юÿŽ‹Zz«˜³a;žú×ߢ½«O½øKLúÇB\ŸË‰þ üKà›Ôïj±®å<Ù´üâÀ6}äœø¿NÆÑÿvN n{Z¢±”g†a†a†an7ÉðoÞÀ´*`øóßhÁOˆò&lþ»‰ÓÿzµHñ-þñíÿŒ¿ÿ{µýWØü‹ÙÀé?¦dr;4Œ’ËÀR^Sþ‹ÕX8:VÇ­bV.JàóýÝú¸Ž¿Ší/¼ŠÏô!Ð÷[· Y[×B{†a†a†a˜›Æ ­.AQ ³§}‰³ÿ³ãdøLýÏÿÓ'éÃÞÿ‚³‡*0{Ë<\eIUDЇÿñA\ìyÃ(›'tVÉC—«TG?Êßø·¸·÷P5÷‘œôÿ'aˆ y‰,H$qaÏ#ùo´ohÏú_¥sdö–%J‡¯|äTø·ÀÛo·iE3ü ‰ÿçÿ†Ì3 („õ(íøÏøà´IH¾ø/é¿I+OuþOSÑñ÷ÿ_Ì—?§ÆÀÿíÏ…‘7ÿ þßZpPÔÀz¼‹ßK£~žzq¥X¸hŠ8Å×oíÀû=Js Q0¿ Ûß8u-Oc¡¼µçñ‰ÏÑ@¤ýŽ¨Ùˆç]õ4âmBÏQ}Þ­¼O®êî¥ß–&€ê‰ŸÂ×¥õ¢mº]ÔæÇ¥Íme†a†a†ar! ’¦,|º7Ž`ê¿¡c2Ø¿@ù:yRðQì‹bÐ=¾ÄÙß<¨62è%”L;#e=]I”üâ}«üŸÿýö¿`Xœýç®—•ž”¸OÔù-Éþ‘œ ‹¶ý¯¸HÇzû¶7‚éÿ㢤­=⺶,ÁµT²ž.`ú§=aAއÿ÷wë±pô³4ƒ`þÏ…ü+ƒÁT~6~ñoÿFew,x9òÿ<õ¯±TN-¬zþÉ5ðj„‘¿ý…mØþq ÿf£–›È7bažúu-zÞ¢ßþóZîÞ[y’—âs’I9°²Eµ3R |þÖ)$«ëQÚþ:¾N–â3rH<|b(Ï0 Ã0 Ã0 Ãä‡ÕÉp×s£„Fþÿñ²4ÂïÃ<Ü3)‰ë=žóW¿ÄEÇ>ÿ7#2É>u‘ÿðÝeñßirŸH—+‡†îú©÷×áìoTÂ]ÏAtˆx¢ líY÷‘Q%¿Påk¤ÆÁ³#§7äÂøÀ™þ€ŸãïžxPË‰ÙøEÃ,ôžþB™åÕ4 ƒƒ" ç[þ ]cåøÅë÷kI,kÂÂÄÅûú~+λSŽw¡7RªöÃ`ÙTÅÓëóÞ[y’ã¬|ñ%ì -¡$;ÛT„Bò>9.E@U)"ñ.7rA_'ªd†a†a†É«“áî)üóèiÌ÷tMÃlš’Ðû=­à?¢TçÿÜóYö¶²î}é( ©*’AË¡):ºAn©ð ;Ûðö^Ç@nœ>=L)×GŠbÀÿnuPù¿ÿ` ˜, àŸãïd„ÃÏ…á< «ÄþóuÁlTͼh†Z¬k,Åׇܩ ÔT $™–|ñ¦‘C{¼Ð´ '2¶Öwõ †a†a†aæfbu2д…oÿðrÿ‡?<¢Œq=ýAF1à\ê¼ä¿ýÉ«@ÉC4UÁÆß`êCs€«gô±ƒ+ÏšÁˆŽ°Ð‰(%¶öô\Æ?#‚é«þ£ä­ñwO"WßaþüY9ÿµ>ÒQ éy\¨üæ_À— òïerÉûÙ"hý+,(¾ˆüåÇ - ‚òìxq;ÖÕhQójhºƒ-ª@0ç¯jIôë£RÌ”¿_§<‘yÑ›@²zŽ HÓcj­`†a†a†ùñò/îŸ^ñ¿èýLÈ8ú¿¯Ã#¿X)·ºï?Å—çéտĤü'ÄeÁB(DGõ†M-Öµü¸ïèŸdß„Éx½&nf;©_À“Mô þø!U‡Mž jë³ø•|¦p÷qœâþ üKà›ºK?œÿ'üðožÅßÌýí] -d†a†a†ùñad¨Ùˆç>ya¶ëíÍãú“bΆX˜hã¾¹itãýVzþÚЫ% ›<˜9þ%¯ëgzÞïÑ'Ž¿*Ž_Ågú0l>{£ Ë6b]0 Ã0 Ã0 Ãü±/aIN†_—âó4Ãëç¿y +«õC¼ Ûß8švî<>Iý–FŸÆÂˆ<@Ry¿ßÿžzqb²Œ>SØÞú®*”†½Þóâ·…Í™ÀÂEH±Òßg{ºõ5×C‰½åmQ÷?¸Æêœ Û±¾4T?€Hü¾.­õh]ò¸j£O¿O>НßÚïVÙûÙŒn‹¡ìוO?¬ÀS-H$ÄõÈvÚNÂ\¯ý~%àäüþ¨0•Á–W{–-@•n¿|þ$;Û”Á˜<…OœóªRDâ]®!y¼ ½‘RüÝø.1 ½³“Ùöë*¤Üòç Í-`Ì÷+Îíß¡žå8Ðû±z®³?s†þ¼‘ûÕ›@Rï:¨v±ƒa†a†a˜9­.! ŸGUãF-1P³ë_¿¥ 8Q>;çñuç(PZ¡C¤öÐÈ6•u¶ (†qœ9±ùÊÀ¾qgCù^×í臂žŸÛCÁ÷‹M‰~}À0 Ã0 Ã0 óã#ç%,çÌ*Õ{¥˜™‘Ä.ïdy-Ö5:#Ñç‘H>€‡6Ôêc?ßí߽ݵØA!è9aª×Fí¡È‚ê¿Î/1_Ïçè!ݹü†F±«¤–H¤„UÉ„o›Œ×½£(™åm[>×kÀz]!öƒ$×v=¦ûuç’ïý¢CÉ„ßy¢¢7xùS†a†a†a~Øs2Ðüñ´Pyï¼q5—\Ïq×sÛS9(̼3…¥Îœzš«Þ„*:%P ô¼‰üéÉþ2ɬW'~4̑ϯ=Ýל’ Û“ÊM Žer¿zHækx¯õs<¨Û÷Å_yóx*zÚ’K?›¡–\Öëʧü÷K–ñäLȽ„¹^óýBZÿ’” TåˆÈ”G]PþÄéý/zl¹2 ½_ô»¿Fâ-¿>Užs20 Ã0 Ã0 óã 0ñ#“ dÜ?Òv^⓱CάÉ.†a†a†a&89O—`ltãýÖ6àqyg,,û­Lnʆa†a†a~ìp$Ã0 Ã0 Ã0 Ã0¡À‘ Ã0 Ã0 Ã0 Ä;†a†a†a† v20 Ã0 Ã0 Ã0  ìd`–´|qâ'¿ÜÙ\Š¿Õû¨+Åþ¦{õcä‰lÙ]ƒ:}X0aéa†a†aæ¶’%ñã <ÕR7[ß•KðEOëek6âù_×#BE’§°]œ/ˆê—Q±qŠh¬Ýø÷RlÅV>ùZDž{åÅ$TŒ}—:lrµךQ=_?½ýá/ŸËuI«7Cs>ŽîÖÍZfcîoY…)ÃÅw…d\‹òàëµBN†ÇO^xŸiQ!‘zoïèã[IËúj”œŒã?œÕÁß6Ucùp6Ÿø?´¤Ä¿£ V@ß”=ÅcèÆþ×®aµ0ÂÏ<ÓOónÞz!çÀ#À1C»Vï^ŒYro±½1”4´?@O9·3One_›H¿®°Ú“Mµ?éþÔ aOëˆL`ÞFíŠjµŸËûÙV>_= Ã0 Ã0?rŠd˜ƒZÌ,E¢—Ž„ÁôëZô¼µ Û_؆½ÝµØñ›²\~wã\$ß}TÉ"Ÿ‹ÚMÛô9¶òaÉ 2¶•œ6×°6Ë¥ƒaj{JÞð€@ònÙþ‘E¨X³Vý /‚õܵfÊGâÕÇVÈ¡ÒÅXë!Y›\b뇎¿*ž‡s0ÜN–.ú±AŸƒx§-Ž‹ÑûÑR¦·˜‡Œã« è\ȹÞz°Ç`´’±ZÜuZœ;)¶Ú¿Ô'lXôdãvõÏÍæŽ¿ïzï‰?WλÞÿÁï絈<è”ß‹Ðïá¼õ0 Ã0 Ãüt°F2ÌÙ°Í‹¦è#—íŸÇŽù]ØþÆQyL+«Ïç?‚M£@óbèÞ÷’<,Út•qt µµ+ÊR#ë*R`HÉmå÷þ‘ç6½#ýì›äÛp¿0Ò/F¨¨€{¿p u”€Š|8ଵ%]gšY¾C­1{Û¨‡ÚÛˆk¾H"]n»^µX×ò4ʰïs ¢`‰z,”ƒ}£øú­x¿GžÌ€"šÔ°·ËÀ 6´]CËúÀ·×Q••àJLEø“D›'‚¢êUñTy”ãíW†ÉRš^^Ôqx­CZà[p)k4ƒú· t_À'ÓÄÛRÿ~‚0fw!º€î°ŠÆ»Œ(‘e€a p™~;e¨*A¤·±i•ˆNRçÎ.™Ž Í•*I—÷÷éõ6îŠ=ã¢^UG²ë´<׸k±ÐI’Lu-‹1ïìI|ô¼£èò·èÞ­¬zVáË¢ÝUtä¹^iÔÝÕå¶Ó¦Ç$¿°r1–Ëc½ÝrßÖt­îoü×`jòè¿nnUïyÈ~]ýéÓ/¨7ðy3<ŸÞë5q£÷ÑyNÔß±„û^Jû»#ß™k€~…Q> ú{4ûÐ?øË@= Ã0 Ã0?e¬‘ çöïÀö·N CSD/´¡WMÛ0gV)’‰ó¢—/á¡Ä)q.îšQ†ñ‘oĵ‡Q6Ò©FÔ;6£ûèÊVQ%ä`Ʋ­|XrE1Ê7Fm‹Øž{Y˃¼z&îK ²IË[>D$eL’áN²gP>r( P´éDâ»åÈm*ò"+JѯeÒ ’ªÛ¤G´{Í"\?ºY|N{ Ò“/¶~0Ñ÷[)ª…ž‘4"õ¨I¼®"^:…«ì/1°aoç¤ñ¯öÉÁ ¸õ%W•Ll£SdÞç7r;4éü ä|¨û¶/uÎqìüww㘷üúb,¥eŨÃ38ˆ¯còìûTÙ>{CE÷Èíc`åoVh™ûïGžËÁÁ@Dk€o;\(AtJ¿Œ8Ð%ίœª >9Í€"ÄvX.Œ.""Œ®¯öö!YU‰’ŽÓˆ]-Âô%Â(kžŒ¯ å2ë-õ©òb!s'´oUǪ„ Õ-»•A;ë‘ÅrK‹h§2œ£—…Áºõ‚<6é‘LªÄìQ ‘º^éí$½³{œè‰“®±ìѳçÈ8¢Oêë5È϶:m!cXí»†¸¹œßÈúS_¯­=ùôP{lzŠ„¡­å:Š$øºéý¹ºy¾uôÖR{½¦çM:´,ϧ0îc SgêqGvœ²¹°÷Uãú ½·ëa†a†ùq<]¢ªA2áÙy@Ž^—¶oÃï÷÷kY!̆¾¡ï?ø– :6£«„‘» Æ´‹¥|(òÌé j…E^QŠ¢âE¸÷Œ>GŽ‘5ŽAþ.9åé:ô4âʸ¨O :4D1ÕÊ §Í™ã+ÉÔ£¦I¸Ž‹zòÁÖ…pŸïï–{çòÿ…q §<ÆÏÐa°¿¹ZmËRã­˜?+‰céQu¥˜ƒš å³rù{\Ñ»AÐ(èŽ_RÛãhi¸lÍF¬_|­s•д¢AŽF{ ÚP¡°w2þMG7µ^a ƺ„)6íÆWì(¸K¦Ã“ÓÔÅ&φøÝc àFxFü­„Õÿ=NÔ™:åÈÅÙF½·”¸ãûÜwä¥ÁRxÐfä´º©îôA!z†a†a~ X ¿·s½kƒIN›x8Þ%C¿cä`ü|U="ñ.ãÈO d”#ž2Òe¸i_LîÓ¼× ´ãÒ¾v`…ž†`+–ÜÇZD¬ÖÓ*¼xäñïp½x®;E‚" Æ2Gë‹æ‰òqqøׯª3“ƒQ{*=S-Ìšóë2è–I)÷£9ëÉ[?ÜLîAEÎI¯£_OqhYìF& '#X¾ô_è#ÍÙÎÍ*1'pÃYLÆrK½KËïÁ•áôQ |'sNÔb]cz$C)fÖèÝlCi6.gÍ&z® YU–¹¥QõYWíq==¨ª@ã-ð’O½ráµôLÖ,!óXÚ9|µ?{6ØQÖ0Í?²®É”«©%¹1Ž º- ®ËÕØi/¨ÿÓÚ¤GCΆ]ã(žámƒáºLõ¦=W«ˆ…êõay>~VQ QŸ¾0.²-­» ÷SþŸ¿¸ŽSyù÷ˆ V§m¦†a†a˜Ÿ2KX¦–­Äoñü¬6ü^‡¾«å µu#KXš–“2ÊÚ­§IÈc²ÂPž0é!ò’SþŠ P¤’2Zåc¢Å€ò¶sÞö”|n€ÒçIü˜ƒ‰¼æïr’›ëõBË36Aæ\sÏD¿ÙFzf< CmЈßEµáMühHÊè&~¼†S±ë*oƒÎã°³¹säž'ñc]©ošDJ.õ– {ò@¸ìlžá?™“Bzq?ŽâëΖº×«Êé^Í’ø‘椧'M$|Ë>á.)H†ÚZ™ ’P‰õd">Jâ×1ž>Ý:‚è®Zà19eÂæî&04Õ+ÒïÒŒ_w‰JÍU•È‘0éò¶ŸÎ/¯RmgÑc»^[; o›R ½ú=m´É _Ÿê„‡¶~P×B{ã2RCæmÐÓALí‘×’Gÿ¦öØôøï‹êcÇè6é±Õë^—ÐA¹œ%, õ~ŠZëókÈ|>ýí¡H¿†u39R.'ùnú’¾†ò¾w¼½ë)Q°]Ã0 Ã0ÌO™@'Ãü” )JNÆ}ËX’#cù°ëŒ¸é,™.´ýCé–p»êÍ—ÚésVx°Éo |ß³2!î#Ã0 Ã0 “%ñ#Ãü„h}/,+•+WHêJÑ„Á[ç` ¾¼p{ ¾ÛUo¾L”væ ßw†a†aæGG20 Ã0 Ã0 Ã0  ÉÀ0 Ã0 Ã0 Ã0L(°“a†a†a†a˜P`'Ã0 Ã0 Ã0 Ã0¡ÀN&h‰Jëzô·ÍžDŽ-c™.ËÒѼŽÿOZæpw êôaÁ„¥‡a†a†a˜[B'à <Õ²Qîýü7/á©erW ä/¾$ŒÊíXW£E…@k·F-mϽ¬…ØÊ‡"§õÓµLo÷7Éw­ù0%¯X³VHÒÊçr]F²éqο­ƒØ†û¥ž©Ö"·íî¹°Ú_8;›«±_n3ÐR¦…@KRâxïèãBÈpRghà ¼½ô_hA¡Ð¿£Â3eÏF±áYåè %ün•î­·PVï^Œ-r‹¢qÉÍií4q+ûÚDúu…Õžlz¬ýIN –©ú`¢czO¿ÿ͘߷Š|ÞÛ Ã0 Ã0“œ"æ 3KG‘è5ñü‹ {¡ tX8âClã\$ß}Ý­"Ÿ‹ÚMÛô9¶òaɉ1\ÔrÚ.uh±ENFzõÔö”¼ÿà!=€äܲý#‹´ó!_‚õܵfÊGâÕÇV¤C%бÖCþ²B>}þPJ÷Ñ!”?LýP`û¿Ší/¼ŠÏôa¡,]:å±>lØÛ Z‡ô‰‚O¦‰·aûâÍê¡ûµÑG£ÐÃ]Eˆ. ;?ŽØ^m¼Ó(ó#%² 0ŒcÂX¿L¿2T• ÒۇشJD'©sg—LdžæJ¨Õ2¹¯H¯·qWèõª:’]§å¹Æ]‹…N’dê¨kYŒygO⣴@@í?£ËÉߢ{¶^°êY½k:†/‹vWÑ‘çz5éí$\]n;mzLò +c¹<öÐÛ=­#Ö~ kuã¿S{Gÿûukt{¬z|ÏCöërÈèOŸ~A@½Ï›áùô^¯‰½Îs¢þ¾%<ï+‡ô÷¤íý¯È]ÀöÞf†a†ù‘ad8·¶¿uJ| ÃGF-qä8nœ»f”a|ä±GqdàvªÑŽÍpGÕ·¡D޶o¶–K®(FùFë›&`WÏÄ=c D„q®Âh½¡±ôI²gP>r(e mz‘øn9&£¤A/ÊŠÏ~-“NTÝ&=¢ÝkáúÑÍâ³ÙK/‰Üݸö jñ§=„¹ýfºñ~ë6ýŒ¤©GMâuqnöv W­Ð'2!ƒ¦Ha?g™ž.AyÊŠñö2 MF6ˆM<Më‹­ò¥¤Lœ«ÃK‹‚h}O•=§SDÊP7¬¢'^‰õ ÷ÊHU–ºƒ8qñ:&ϾOÕÀgoPÿèíc`åoVh™ûïJžËÁÁ@Dk€o;¼m)AtJ¿Œ8Ð%ίœª ¾G 7ŠÛ`¹0ºˆˆ0º¾ÚÛ‡dU%J:N#vµÓiÚBód|e(ïYo‘¨wH•ÛØ‚ rß¾U¨B2TiŠ´³ÑÓ%ÒÂìÉpŽ^ëÖ òؤG2©³GU4Dêz=¤·“ôÎîq¢'NºÆ²GÏž#ãˆ>©¯× ?Ûê´…Œaµïâæ~p~#7êO}½¶öäÓÿAí±éil(†¶–ë(’àëR¤÷çêæiøÖÑsdXKíõšž7éв<Ÿ6¸…aû»¶÷6Ã0 Ã0ÌàéU¥@â¼ÚO&n8>“¹ÂÐW#åý¿Ó2AÇfôc•ø¨£‘vïh¥|(òÌij…E^QŠ¢âE¸WçʨBùç” x]ž®COÓ(®Œ‹ú¼‘‚†(¦ Z}ÀÒ¶Â;¼©G…Û ÿ@=&ÈÉâ8=+{¦E˜Ú_çñùþn¹wn !ÿoãĉAiÈ· ‹òÜqQ?ínL¸êNo8›À¹ÈÝVyaóÓ“8¦£Èq3—¿Ç½vî9LÄöøZZ OÔC¼ß7r/Gµa84¨M˜šÉˆô¹#Ãô``Òd¹›ìêWò«}ø\G´×Ô`J°ÜÉ—aÖëGì× ý(`$z赘4öŽõG”‘質Þåð ÛN…î§®×ÁÐÎyUny?¹îõüØä6Ìýà8VÒûÓØÑîüûß@€ž —‹m^ŒÕOhA.¤×Kú{ í¨×ô¼ÙžÏ ~ó>J'zNÓ»l¹ê±¾·†a†a~„X ëZ”AYô´0ŽšP©uõ€‡P4•4ôå‡E¨Sš1Œöé}{ù°ä錟1ÄÆ RòþÆÇ:•¢#†ÑâR}à"ËO©,ôJ}ÄÊíÿ^Ÿp!=Ýû¾Á}ÕÅ@%9`È™°*åXä 'EÃ/QŽNíôx —ÞíæÿRóSûª£cì{œÐ‡Fj6bý"àë·œHí´+9í1hC…ÂÞÉøw6Q@ÜÔz…1ëýy㉠nç’éг˜üØäÙ¿{lÜÈψ¿•°úߢljZ8S§¹8¨÷–ö}LÃöw!˜µÖ÷vö$’ Ã0 Ã0«“Bà÷vŽÊ Ò0’Ó&rùÉ2ÊOéE/BQ_LîS† ´ãÒ¾v`…ž†`+–ÜÇZD¬ÖÓ*¼xäñïp½x®;E‚" Æ2Gë‹æ‰òqqøׯª3“(R{* YÈýžŠ5ðªQ] íçª'9I¼NŠÌlÿm‚¢fMJ­ð@Ó*æ$¿·ÊåÈèИøÿd,%)ã=¨°èYZ~® ÿ ‚Hà;™‹¢ëÓ#J1³FïfCJ³q9ûh6Ñsɪ²ÔÈ-ªÏºj»hìéÁ@U…\í!ƒ|ê- ¯Åp g¶d ™ÄÒÎá«%øY–•&ʦùGÖ5™r5µ$7ÆqA·¥±ÁÙ7¶‡FÚ êÿ´öéѳá@×8ŠgxÛ`¸.S½iÏÕj'b!‡z}XžO§ŸUˆZ}Ä!Ìû¨¢‹òušgþ]ÈM?ÂûÞvþ1 Ã0 Ãü˜°&~$(a]ôô6¼‰ßâùYmø½ }§„M¹´’§°½õ]}”üÑ ëëT#ïRñ¦§IÈc²ÂPž0é!ò’SaR¤’2Zåc¢Å€ò¶sÞö”|n€ÒçI –ƒ‰¾f•TÓßË¥ý&lÏC¿Ó*$Ú1µì·Ø1¿+kÞSâGr ü.ênÒF›œçJ†}yhiÌ9z_’Â+ïaMs)N{“IÖ]MýÎWGZâÇÍ30ü§ì+`¸‰Gñug KÝ~P‰ãtogIüHsÒÓ“&Þĉ¶¾¥nHN; Cm­LI¨Äz2%ñ똊 OŸnAtW-ðǘœ2áswšê• éwiÆ/-QéË™yU%r$Lº¼í§óË«T[çYôØ®×ÖNÂÛ¦TÂ@¯~OmrÂ×§:á¡­ÔµÐÞ¸ŒÔyôtS{äµäÑÿ„©=6=þû¢úØùgfÒc«×½.¡ƒr)èþ7Õû)j­Ï[¬!óùô·‡"AüýÖ}ÌLØèïIä{ò»À÷aîzôßIÚ{›a†aæGF “a&2´šDÉÉ´•*B‚œ!ˇû²®,K¦K#m¿ÇPº%Ü®zó¥€vúœlòÛß÷¬LˆûÈ0 Ã0 ó"8ñ#ÃL`h5 ,+MM©ºR4aðÖ9ˆ//܃ïvÕ›/¥ùÂ÷a†a†™`p$Ã0 Ã0 Ã0 Ã0¡À‘ Ã0 Ã0 Ã0 Ä;†a†a†a† v20 Ã0 Ã0 Ã0  ìd`–¨Ì{Ýù›ÏÎæ´Ä´Deº,_HGSðzý?yh9ÃÝ5¨Ó‡–†a†a†an Y?®ÀS-x³õ]¹Öôô6¼y\ˆÉ |üU$y ÛÅù‚¨~A®”ž±–¸[ùPäkyP1zôQ\ê°ÉÕþ]k>Dõ|uR­¡ù\®ËHZ½zœóqtg]oÝY»} ßý’q-nxµ+ªÕ¾W¿M„|&àY/¾pv6WcŽÜ»†SDë<Èã–ä X´íMà- ‚œ§ eÃYÂRüûzqbôYÙ³Q<†nìíÚ-]ªÏ[o äx8f\Zp1fɽqÄöÆPÒÐþ=AäÜÎ<¹ÝË"¦_WXíɦÇÚŸtꆰ§uD &2†÷¤÷]èõ˜çû–a†aæGFN‘ sP‹™¥£HôÒQ-Ö5’1¹ Û_x_£Ïo¨•åòC|ˆmœ‹ä» #ùQÄãsQ»i›>gÂV>,9A…JN›ãH°É¥ƒaj{JÞð€@ònÙþ‘E¨X³Vý /‚õܵfÊGâÕÇVÈ¡ÒÅXë!Y’¯@J?õƒÒ¿‘ùn\DŽí?þªxnÜÁ°té ”Çú°ao\l…;HOýØ ßÁ@œM½¹9‚x§-Ž‹ÑûÑR¦·˜‡Œã« è\ȹÞz°Ç`´’±ZÜuZœ;)¶Ú¿Ô'lXôdãvõÏÍæŽ¿ïÛ{²csê)·£q`ä;}Ò€MO¡ïU†a†a˜ ˆ5’aΆíh^4E¹ô~¬£4Tn=ÞÅï÷wkIŽÐ¨Î¼º÷½$‹6FEe\|ĵ+ÊR#@*R`HÉmå÷þ‘ç6=#OŠ0É·á~ñ1y)0Š@EÜû…ã˜p¢äIùpÀYjKºÎ4=²|)†Zc(ö¶!Pµ·×¼#tž~¿]ô§®QÿOÇ>í@1Q‹u-Oca„öÏ{"TL"Q…rðn_¿µï÷È“V(B`þYSô¬@t£lrAËúÀa¿“‚"êeqÙë‹1mz‘ønq=jTL}`вKÑŸ)½šªÛ¤G´{Í"\?ºY|6{ ÒcaêL½#Úö°ãœð²÷Uãú`Їp7Þo¥¨zFÒˆÔ£&ñº8· {;…«Vè™Á¾¿Yös–UË}™÷ ¬oËé Ù 6ñü5­/¶Ê—’2q®Wp,- ¢õ=Uöœ>N)CÝ°Šžx%Ô7Ü+#TYrHè:<âÄÅë˜<û>UgŸ½Aý£·•¿Y¡eî¿+y.­¾íð¶¥Ñ)ý2:à@—8¿rª2øä4ŠÛ`¹0ºˆˆ0º¾ÚÛ‡dU%J:N#vµÓ—£¬y2¾2”wȬ·HÔ;¤Ê‹mlA…ÌоU¨B2T·ìVí¬GËý--¢ÈpŽ^ëÖ òؤG2©³GU4Dêz=¤·“ôÎîq¢'NºÆ²GÏž#ãˆ>©¯× ?Ûê´…Œaµïâæ~p~#7êO}½¶öäÓÿAí±éil(†¶–ë(’àëR¤÷çêæiøÖÑsdXKíõšž7éв<Ÿ6¸7DÃ/Q>ÒntäG.ïU†a†a˜‰Kðt‰ªR q^í'¡ð?ÿÍÓX˜hKs<äÃ\aè«úþƒžÔŽÍèÇ*a\¯„1íb)Š×.çòÿ6Nœ”†|›°(Ï÷õÓîÆä«îô†³ œ‹Üm•– 0‰c:9sù{\Ñ»AÐhçŽ_R›“ˤPž¨†x¿oä^ŽkÃphPߌɈô¹#Ãô``Òd¹›ìêWò«}ø\G´×Ô`J°œŒÚR#Ìc½ãˆ}⤌D½“ÆÞ±^`àˆ0úÈðK³ªÞåð ÛN…î§®×ÁÐÎyUny?¹îõüØä6Ìýà8VÒûÓØÑîüûß@€ž —‹m^ŒÕOhA.¤×Kú{ í¨×ô¼ÙžÏ ~ó>J'z^Ó»Ö"ò`5FÏèÈ/MþzÂx¯2 Ã0 ÃÜÙX ëZ”AYô´0ŽšP©÷­ CÀKOå<úšÎƒC(š¿Júòc‹"Ô)ÍFûÆô¾½|XòtÆÏ˜‡«RòþÆÇ:•¢#†ÑâR}à"Ë{"ŒôJ91äfHFzº÷}#GÀPIrPr1åXä ÇËø>·ì¥ÁRyæËi$S;ÝéL&äèû'ô¡‘šX¿øú-'’A;í DŽF{ ÚP¡°w2þMG7µ^a ƺÆEÞøŠ·sÉt89V}ØäÙ¿{lÜÈψ¿•°úߢljZ8S§¹8¨÷–ö}t (xÞ÷ÂïU†a†a~ X ¿·sTæ`†‘œ6¡FlhdV: ]U‚ £ñÔG› ×ï‹É}š¯Zv\Ú׬ÐÓlåÃ’ûP£VjZ…<þ®Ïu§HP$ÁXæh}Ñ.®ßàÚXuf²/jOe£gª…ÒC9¼QÝ2¹å^x4g=f¶á~ʃñõá+ûŸ>„ï„ìç-0kRjÉIšV1'ù½U.GF‡ÆÄÿ'cy(IïA…EÏÒò{peø}DßÉ\”45=’¡3kôn6„¡4—³f=W¬*KÜÒ¨ú¬«ö¸‹Æž TU q‰xɧÞ¹ðZ z¦aK–ù@,í¾Z‚Ÿ=ìÀ(k˜æY×dÊÕÔ’ÜÇÝ–Æwdߨi/¨ÿÓÚ¤GCΆ]ã(žámƒáºLõ¦=W«ˆ…êõay>~VQ QŸ¾0.ÊuÉ]ÅðEæû0=wÔ{•a†aæ&¸„ejÙJüÏÏjSÉk6âù_×»I¿$Þ„y`ZÒKÊ( ·ž&!u"È Cy¤‡ÈKNù(2@‘JÊh• Œ‰ÊÛÎyÛ#Pò¹zHŸ'ñcz$òšÛ唕„Ò³ÔšïšLÉ(hÆ&ÈÜjrYÓ~ÿòŒ´Äåü®¬‘/¦Ää@ø]Ô1.ܤ69!Ï• ûò(¸Kcj’Cxå½1¬ñ.SIÉ$뮦~ç«#-ñãÎæÎa‰M7ñã(¾îL`a©Û*qœ¾;Y?Òœôô¤‰„oùÁ'Ü%ÉP[+“A*±žLÄGIü:¦bÓÀ§[GÝU ü1&§LøÃÜ݆¦zeÂCú]šñë.Q©¹ª9&]ÞöÓùåUª­ó,zl×kk'ámS*a W¿§69áëSðÐÖêZho\Fjȼ z:ˆ©=òZòèÂÔ›ÿ}Q}ìü33é±Õë^—ÐA¹œ%, õ~ŠZëókÈ|>ýí¡H¿†uÕ¿;oÂFÿ»Yâüm ÷êƒ ÷œõDžïU†a†a˜‰K “a&2´šDÉÉ´•*B‚œ!ˇû²®,K¦K#m¿ÇPº%Ü®zó¥€vúœlòÛß÷¬LˆûÈ0 Ã0 ó"8ñ#ÃL`h5 ,+MM©ºR4aðÖ9ˆ//܃ïvÕ›/¥ùÂ÷a†a†™`p$Ã0 Ã0 Ã0 Ã0¡À‘ Ã0 Ã0 Ã0 Ä;†a†a†a† v20 Ã0 Ã0 Ã0  ìd`–¨ÌyÝù[ÇÎæ´Ä´Deº,_HGSðzý?yh9ÃÝ5¨Ó‡–†a†a†an Y?®ÀS-x³õ]¹Öôô6¼y¼ëZžÆÂˆ.’<…íâ|AT¿ì®î¬I„­|(òµˆ<÷ Ê‹I¨=ú(.uØäjÿ®5¢z¾:9~z7úÂ_>—ë2’Vo†ç|.k­;k·á⻿B2®Å´þûŠjµïÕo“AN†ÇáY/¾pv6WcŽÜ»†SDë<Èã–ä X´íMà- ‚œ§ eÃYÂRüûzqbôYÙ³Q<†nìíÚ-]ªÏ[o äx8f\Zp1fɽqÄöÆPÒÐþ=AäÜÎ<¹ÝË"¦_WXíɦÇÚŸtꆰ§uD &2†÷¤÷]èõ˜çû–a†aæGFN‘ sP‹™¥£HôÒQ7Þo݆í/¨í“D=žßP+Ë凸Û8ÉwFò£ˆÇç¢vÓ6}΄­|Xr‚> •œ6Ç‘`“KÃÔö”¼ÿà!=€äܲý#‹P±f­úA^ë¹kÍ&”Ä1ª­C¥%бÖCþ²$_!î¦ÖOý ô¯EäAG¾‘cû¿*ž‡w0,]:å±>lØ[áÒS?6èw0gBon† Þi‹ãbô~´”iÁ-æ¡ãø*d:r®÷ƒì1­d¬wçNŠ-†ö/õ =Ù¸]ýs³¹ãïûDÅöžìØœzGÊíhùNŸ4`ÓSè{•a†afbd˜³a;šMÑG.½S4ƒ>€Šj(m÷Êr„FuæÅнï%yX´é0**ãâ#Nh]Q–R‘CJn+†¼õψ<· 8èy’PÄ€I¾ ÷‹ÉKQ*ÚàÞ/Ç„} OêȇþÈ P[Òu¦é‘åK1ÔC±· z¨½¸æ¡óôƒüí ?mtú:öiŠ odËyO$ƒŠ‚I$ê±PÞâë·vàýyÒ EÌ?kŠ>H…θQ6¹ eý à°ßIA‘ õ²¸¿ìÎõÅ+C½^WÑý ÕhRÃí.ƒØÐæZäÈØ‚KY£( hej0Tõ|2M¼ ÛßhL£ÙÃ]Eˆ. ;¯¢¤ñ.£Jd`XF\¦ßNªJéíClZ%¢“Ô¹³K¦cCs%Tj™ÜW¤×Û¸+ ôŒ‹zUÉ®Óò\ã®ÅB'I2uÔµ,Ƽ³'ñÑZ ðŽ¢Ëߢ{¶^°êY½k:†/‹vWÑ‘çz5¦QwW—ÛN›“üÂÊÅX.=ôvË|[?еº¿ñ_ƒ©=È£ÿýº5º=V=¾ç!ûu9dô§O¿  ÞÀçÍð|z¯×ÄÞGç9Q߆ȫ´÷¤Ì¿…éÉå½Ê0 Ã0 3q±F2œÛ¿Ûß:%>„áóBz¥qä8(Ìû%ìxQ—‰¶ü ‚»f”a|ä±Gn‡Q6Ò©F~häèèÊVQ%ä`Ʋ­|XrE1Ê7Fm‹Øž{Y˃¼z&îK ²IË[>D$e4Ò&ÉžAùÈ¡TäCѦg‰ï×£FÅÔ¦(»±ý©‘2a¶§ê6éí^³×nŸÍ^‚ôX˜:S=ì8'¼¬Å}ÕŸ>ô!ìD¶Ð3’F¤5‰×eÄËÞN`áªúD&d°ïoV†ýœeÕr_æ=(+ÆÛrzE6ˆMÞ ¿DùH»Ñi¹¼W†a†a&.ÁÓ%ªJÄyµŸLxFjŽâMm}‚&ìFSă¾¡ï?è AíØŒ~¬Æõ*Q•w4ÞR>yæô5Â"¯(EQñ"Ü{FŸ#ÇÈǨ —œòtzšFqe\Ô—öaÙÅTkG…Ø|ó3õ¨i®ã"E ÔÇt-º|ñHgšÓB9EŒuåÌy|¾¿[îHÈÿÛ8qbPòm¢ÿ$»?×Gy@F9â©6®ß“û4_µí¸´¯X¡§!ØÊ‡%÷¡F­Ô´ /yü;\/žëN‘ H‚±ÌÑú¢y¢|\\¾Áµ±êÌd_ÔžÊFÏT 3¤‡r&x£*ºer1ʽðhÎzÌlÃý”ã/êÃWö?}ß ÙÏ)Z`Ö¤Ô’“4­bNò{«\ŽŒ‰ÿOÆòP’2Þƒ ‹ž¥å÷àÊðú(ˆ¾“¹(j±®1=’¡3kôn6„¡4—³f=W¬*KÜÒ¨ú¬«ö¸‹Æž TU q‰xɧÞ¹ðZ z¦aK–ù@,í¾Z‚Ÿ=ìÀ(k˜æY×dÊÕÔ’ÜÇÝ–Æwdߨi/¨ÿÓÚ¤GCΆ]ã(žámƒáºLõ¦=W«ˆ…êõay>~VQ QŸ¾0.ÊuÉ]ÅðEæû0=wÔ{•a†aæ&¸„ejÙJüÏÏjÃïeè;åch‚Ì¥%Hv¾®å`ZÒKÊ( ·ž&!u"È Cy¤‡ÈKNù(2@‘JÊh• Œ‰ÊÛÎyÛ#Pò¹zHŸ'ñcz$òšÛ唕„Ò³ÔšïšLÉ(üσD.kÚï_ž‘–¸œß•5ï€)ñ#9~uŒ 7i£MNÈs%þ< îÒ˜šä^yo k¼ËTR2ɺ«©ßùêHKü¸³y†sXbÓMü8Н;XXêöƒJ§ïN–Ä4'==i"á[~ð wIA2ÔÖÊd„J¬'ñQ¿Ž©Øð$ðéÖDwÕŒÉ)þ0w7¡©^™ð~—füºKTj®ªDŽ„I—·ýt~y•jë<‹ÛõÚÚIxÛ”JèÕïi£MNøúT'<´õƒºÚ—‘2oƒžbj¼–<úŸ0µÇ¦Ç_T;ÿÌLzlõº×%tP.g KC½Ÿ¢Öú¼Å2ŸO{(Ä߯aÝÇÌ„þw³ÄùÛ@ïÕîß9ë9ˆ<ß« Ã0 Ã0—@'ÃLdh5‰’“i+U„9C–÷e]Y"4–L—FÚ~¡tK¸]õæKíô9+<Øä·¾ïY™÷‘a†aæ'DpâG†™ÀÐjXVššRu¥hÂà­s0_^¸=ßíª7_&J;ó…ï;Ã0 Ã0 3ÁàH†a†a†a†aB#†a†a†a† v20 Ã0 Ã0 Ã0  ìd`†a†a†a&ØÉÀ„-Q™óºó·ŽÍi‰i‰ÊtY¾Ž¦àõúòÐr†»kP§ &,= Ã0 Ã0 Ãܲ$~\§Z*ðfë»r­ÿèémxó¸>…Z¬ky #ç=k„çIõËîÚáΚäAØÊ‡"_‹ÈsÏ ¼˜„ŠÑ£âR‡M®öïZó!ªç«“ã§w£ÿ üås¹.#iõfèqÎç²Öº³vû.¾û+$ãZL뿯¨Vû^ý6yädx…? v6WcŽÜ»†SDë<Èã–ä X´íMà- ‚œ§ eÃYÂRüûzqbôYÙ³Q<†nìíÚ-]ªÏ[o äx8f\Zp1fɽqÄöÆPÒÐþ=AäÜÎ<¹ÝË"¦_WXíɦÇÚŸtꆰ§uD &2†÷¤÷]èõ˜çû–a†aæGFN‘ sP‹™¥£Hôj`ΆX˜8(OÄ‡ØÆ¹H¾û¨0’E<>µ›¶és&låÃ’ôQ¨ä´9Ž›\:¦¶§äýé$ÿà–íY„Š5kÕò"XÏ]k6¡|$ŽQ}l…*-QŒµò—%ù ¤ôS?(ýkyБïÆEäØþã¯b{†¥Kg <Ö‡ {ãb+ÜÁ@zêÇýâlBèÍÍÁÄ;mq\ŒÞ–2-¸Å<´`_…l@çBÎõ~Ѓ=£•ŒÕâ®ÓâÜI±ÅÐþ¥>aâ'·«n6wü}Ÿ¨ØÞ“›SïH¹#ßé“lz }¯2 Ã0 ÃL@¬‘ s6lGó¢)úÈ¥÷ãmx³w#žÿu)>¡ ÑGbå¨Î¼º÷½$‹6FEe\|ĵ+ÊR#@*R`HÉmå÷þ‘ç6=#OŠ0É·á~ñ1y)0Š@EÜû…ã˜p¢äIùpÀYjKºÎ4=²|)†Zc(ö¶!Pµ·×¼#tž~¿]ô§®QÿOÇ>í@1áD´Ð¾7ªEEÁ$õX(ïFñõ[;ð~‚O¦‰·aûâÍ‚i4{¸«ÑtçUt€4Þe@‰, Ëh€ËôÛ)ã@U "½}ˆM«Dt’:wvÉtlh®„êQ-“ûŠôzwEžqQ¯ª#ÙuZžkܵXè$I¦Žº–Řwö$>ú@ ÞQtù[ôaÏÖ V=«wMÇðeÑî*:ò\¯Æ4êîêrÛiÓc’_X¹Ë屇Þn9‚oëºV÷7þk0µyô¿_·F·ÇªÇ÷òØ£·Ã(éT#?4rtt嫨ƒr0cÙV>,¹¢å£¶ElϽ¬e„A^=÷Œ%Ù¤å-"’2é“dÏ |äP*ò¡hÓ3ˆÄw‹ëQ£bêS”ÝXŠþÔH™0ÛSu›ôˆv¯Y„ëG7‹Ïf/Az,L©wDÛvœ^Öâ¾êb\ úîÆû­Ûô3’F¤5‰×ŹmØÛ ,\µBŸÈ„ öýÍʰŸ³¬Zî˼eÅx[No È±šÖ[åKI™8W‡+8–Ñúž*{N§ˆ”¡nXEO¼êî•‘ ª,9$tqââuLž}Ÿª3€ÏÞ þÑÛÇÀÊ߬Ð2÷ß•<—ƒƒˆÖßvxÛR‚è”~p Kœ_9U|ršE ˆí°\]DD]_ííC²ª%§»Z„éK„QÖ<_Ê;dÖ[$êRåÅ6¶ BæNhߪŽT! ª[v+ƒvÖ#‹åþ–ÑNd8G/ ƒuëylÒ#™T‰Ù£*"u½ÒÛIzg÷8Ñ']cÙ£gÏ‘qDŸÔ×kŸmuÚBưÚw qs?8¿‘õ§¾^[{òéÿ öØô46 C[ËuIðu)Òûsuó4|ëè92¬¥özMÏ›thYžOaÜÇ¢á—(i7: ò#—÷*Ã0 Ã0ÌÄ%xºDU)8¯ö“ éTPÓ$Ú<¹n„¹ÂÐW#ôý=!¨›ÑU¸^cÚÅR>yæô5Â"¯(EQñ"Ü{FŸ#ÇÈǨ —œòtzšFqe\Ô—öaÙÅTkG…Ø|ó3õ¨i®ã"E ÔÇt-º|ñHgšÓB9EŒuåÌy|¾¿[îHÈÿÛ8qbPòm¢֩t1Œ—êYÞ1`¤ïPʉ!7CB0ÒÓ½ï9†JrÀs€’‹)Ç‚$=^Æ÷¹e/ –¢È3×XN#™ÚéN§`2!GÇØ÷8¡ÔlÄúEÀ×o9‘ ÚiW r4ÚcІ …½“ñïl:¢€¸©õ c0Ö5.úóÆWì(¸K¦ÃɱêÃ&φøÝc àFxFü­„Õÿ=NÔ™:åÈÅÙF½·”°ï£E1Àó¾/~¯2 Ã0 óSÀêd ø½£2ƒ4ŒâmBê„Æ;›w…ú]ÎQŽxê£M†ë÷Åä>ÍW­@;.íkVèi¶òaÉ}¨Q+5­Â‹Gÿ׋çºS$(’`,s´¾hž(×opm¬:3Ùµ§²Ñ3Õ 顜 Þ¨Šn™\Œr/<š³3Ûp?åÁø‹úð•ýOÂwBösŠ˜5)µä$M«˜“üÞ*—#£Ccâÿ“±<”¤Œ÷ Â¢giù=¸2üƒ> "ïd.ŠZ¬kLd(Å̽› a(ÍÆåì£ÙDÏ$«ÊR#·4ª>ëª=§Uh\¢^ò©·@.¼ÞiØ’%d>K;‡¯–àgÏ;0ʦùGÖ5™r5µ$7ÆqA·¥±ÁÙ7¶‡FÚ êÿ´öéѳá@×8ŠgxÛ`¸.S½iÏÕj'b!‡z}XžO§ŸUHÔ§/Ìû¨¢‹r]rWG1|‘ù>ÌGÏõ^e†a†¹‰.a™Z¶¿Åó³Úð{úîRø|Ó’^RFY¸õ4 y¬AVÊ&=D^rÊ@‘ŠTRF«\`L´PÞvÎÛ’Ï Ðã@ú<‰sÐ#‘×Ü.§Œ¨$”ž¥Ö|×ä`JFé@ÏE´xHžÂöÖ~ÿ³AK\ÎïÊšwÀ”ø‘¿‹:Æ…›´Ñ&'ä¹’a_wiLMr¯¼7†5Þe*)™dÝÕÔï|u¤%~ÜÙ<Ã9,±é&~Å× ,,uûA%ŽÓw'KâGš“žž4‘ð-?ø„»¤ jke2HB%Ö“‰ø(‰_ÇTlxøt뢻j?Æä” ˜»›ÀÐT¯LxH¿K3~Ý%*5WU"G¤ËÛ~:¿¼JµužEízmí$¼mJ% ôê÷´Ñ&'|}ªÚúA] íËH ™·AO1µG^KýO˜ÚcÓã¿/ªf&=¶zÝë:(—‚³„¥¡ÞOQk}Þb ™Ï§¿= âï×°îcfÂFÿ»Yâüm ÷êƒ ÷œõDžïU†a†a˜‰K “a&2´šDÉÉ´•*B‚œ!ˇû²®,K¦K#m¿ÇPº%Ü®zó¥€vúœlòÛß÷¬LˆûÈ0 Ã0 ó"8ñ#ÃL`h5 ,+MM©ºR4aðÖ9ˆ//܃ïvÕ›/¥ùÂ÷a†a†™`p$Ã0 Ã0 Ã0 Ã0¡À‘ Ã0 Ã0 Ã0 Ä;†a†a†a† v20 Ã0 Ã0 Ã0  ìd`–¨ÌyÝù[ÇÎæ´Ä´Deº,_HGSðzý?yh9ÃÝ5¨Ó‡–†a†a†an Y?®ÀS-x³õ]¹Öôô6¼y¼ëZžÆÂˆ."èý˜äú ª_v×wÖ$ÂV>ùZDž{åÅ$TŒ}—:lrµךQ=_?½ýá/ŸËuI«7Cs>—µÖµÛÇpñÝ_!×bZÿ}EµÚ÷ê·Éƒ 'Ããð¬_8;›«1Gî]é? ¢uHäq Kr,Úö&ðŽANŠÓ†²á,a)þ}½¸±ú¬ìÙ(C7ö¿ví–.Õç­7r<3.-¸³äÞ8b{c(ih€ž rngžÜîeÓ¯+¬ödÓcíOº?uCØÓ:¢Ã{Òû.tÈúNÌó}Ë0 Ã0 ó##§H†9¨ÅÌÒQ$zµ£øú­mØþ‚Ú r0Ð‡ØÆ¹H¾û¨0’E<>µ›¶és&låÃ’ôQ¨ä´9Ž›\:¦¶§äýé$ÿà–íY„Š5kÕò"XÏ]k6¡|$.îDÈ¡ÒÅXë!Y’¯@J?õƒÒ¿‘ùn\DŽí?þªxnÜÁ°té ”Çú°ao\l…;HOýØ ßÁ@œM½¹9‚x§-Ž‹ÑûÑR¦·˜‡Œã« è\ȹÞz°Ç`´’±ZÜuZœ;)¶Ú¿Ô'lXôdãvõÏÍæŽ¿ïÛ{²csê)·£q`ä;}Ò€MO¡ïU†a†a˜ ˆ5’aΆíh^4E¹láu¬kÙüüߣ……@£:óbèÞ÷’<,Út•qñÔ®(K©H!%·•CÞúgDžÛôŒ'uäÃd¨-é:ÓôÈò¥j¡ØÛ†@=ÔÞF\óŽÐyúAþv П6ºFý?û´Å„7²å¼'’AEÁ$õX(ïÈ1•ý™¡ùgMÑNèLÒB°É-ëg‡ýN Šl¨—Åýew®/ÆðXêå𺊞èo¨F“nwĆ6×Ð"GÆ\ÊÍ@Q@+Sƒ¡ªà“iâmØþ†x@³`Íî*BtÝy wP"ËÃ2à2ývÊ8PU‚HobÓ*¤Î]2š+¡zTËä¾"½ÞÆ]Q g\Ô«êHv–çw-:I’©£®e1æ=‰>Ðw]þ}سõ‚UÏê]Ó1|Y´»ŠŽ<׫1º»ºÜvÚô˜äV.Æryì¡·[ŽàÛú®ÕýÿLíAýï×­Ñí±êñ=Ù¯Ë!£?}úõ>o†çÓ{½&nô>:ωúû–0D^¥½'}dþ-(LO.ïU†a†a˜‰‹5’áÜþØþÖ)ñ$ ŸÚÐ+#gÄ þú%ìxQläp(€»f”a|ä±Gn‡Q6Ò©F~häèèÊVQ%ä`Ʋ­|XrE1Ê7Fm‹Øž{Y˃¼z&îK ²IË[>D$e4Ò&ÉžAùÈ¡TäCѦg‰ï×£FÅÔ¦(»±ý©‘2a¶§ê6éí^³×nŸÍ^‚ôX˜:S=ì8'¼¬Å}ÕŸ>ô!Ü÷[)¢…ž‘4"õ¨I¼.£]öv W­Ð'2!ƒ}³2ìç,«–û2ïAY1Þ–Ó(²AlǦõÅVùRR&ÎÕá Ž¥EA´¾§ÊžÓÇ)"e¨VѯĀú†{e¤‚*K ]‡ÇÁ@œ¸x“gß§ê à³7ܨŸí+³BËÜWò\"Z|ÛámK ¢SúetÀ.q~åTeðÉi1 ¶#Àratat}µ·ɪJ”tœFìj¦/FYód|e(ïYo‘¨wH•ÛØ‚ ™;¡}«:P…$d¨nÙ­ ÚY,–û[ZD;=á½, Ö­ä±IdR%fªhˆÔõzHo'éÝãDOœtež=GÆ}R_¯A~¶Õi Ãjß5ÄÍýàüFnÔŸúzmíɧÿƒÚcÓÓØP$ m-×Q$Á×¥HïÏÕÍÓð­£çȰ–Úë5=oÒ¡ey>m„qoˆ†_¢|¤Ýè4È\Þ« Ã0 Ã0—àéU¥@â¼ÚO&ôHcTªí“D=v£©0æ C_Ð÷ô„ vlF?V ãz ŒiKùPä™ÓÔ4 ‹¼¢EÅ‹pï}Ž#k£þ%\rÊÓuèiÅ•qQ_Ú‡eCSP­bóÍÿÍÔ£¦I¸Ž‹z PÓµèòÅ#iN å1Ö•3çñùþn¹wn !ÿoãĉAiÈ· ‹òÜqQ?ínL¸êNo8›À¹ÈÝVya “8¦£Èq3—¿Ç½vJ‡m? ¥òD0Äû}#÷rX†Cƒú.ÖLF¤wÈþ “&ËÝdW¿’_íÃç:¢ ½¦³P‚ådüÓ–aÖëGì× ý(`$z赘4öŽõG„ÑG†_ʘUõ.ï–t×Ìp*t?u½†vΫrËûñÈuÿ¨çÇ&·aîDZ’ÞŸÆöˆvçßÿô\¸\„hób¬~B r!½^ÒßkhG@½¦çÍö|õs˜÷Q:ÑóšÞµ‘«1zFG~iòׯ{•a†aæÎÆêdX×¢ ¢È¢§…qÔ„ªH½qõ€ÏNk'Džü08„¢ù«¤¡/?¶(2@ÒŒa´oLïÛˇ%OgüŒy¸*%ïO`|¬Sé :b-.Õ.²¼'bÀHß¡”Cn†„`¤§{ß7r •ä€!ç%SŽIz¼ŒïsË^,E‘g®±œF2µÓNÁdBŽŽ±ïqB©Ùˆõ‹àæ0ù¸°/r4ÚcІ …½“ñïl:¢€¸©õ c0Ö5.úóÆWì(¸K¦ÃɱêÃ&φøÝc àFxFü­„Õÿ=NÔ™:åÈÅÙF½·”°ï£E1Àó¾/~¯2 Ã0 óSÀêd h…½£råiÉié#6µX×ø’N´C>QŽxê£M†ë÷Åä>ÍW­@;.íkVèi¶òaÉ}¨Q+5­Â‹Gÿ׋çºS$(’`,s´¾hž(×opm¬:3Ùµ§²Ñ3Õ 顜 Þ¨Šn™\Œr/<š³3Ûp?åÁø‹úð•ýOÂwBösŠ˜5)µä$M«˜“üÞ*—#£Ccâÿ“±<”¤Œ÷ Â¢giù=¸2üƒ> "ïd. õïÅO)fÖèÝlCi6.gÍ&z® YU–¹¥QõYWíq==¨ª@ã-ð’O½ráµôLÖ,!óXÚ9|µ?{6ØQÖ0Í?²®É”«©%¹1Ž º- îȾ±=4Ò^Pÿ§µ'H†œ ºÆQ<ÃÛÃu™êM{®V; 9ÔëÃò|:ý¬¢@¢>}aÞG]”ë’»:Šá‹Ì÷a>zî¨÷*Ã0 Ã0ÌM$p ËÔ²•ø-žŸÕ†ßËÐwZv¯ 2—– Ùùº–€iI/)£,Üzš„<Ö‰ + å “"/9å? ÈE*)£U.0&Z (o;çm@Éçèq }žÄ9è‘Èkn—SFTJÏRk¾kr0%£tð?’ä)loí÷/ÏHK\ÎïÊšwÀ”ø‘¿‹:Æ…›´Ñ&'ä¹’a_wiLMr¯¼7†5Þe*)™dÝÕÔï|u¤%~ÜÙ<Ã9,±é&~Å× ,,uûA%ŽÓw'KâGš“žž4‘ð-?ø„»¤ jke2HB%Ö“‰ø(‰_ÇTlxøt뢻j?Æä” ˜»›ÀÐT¯LxH¿K3~Ý%*5WU"G¤ËÛ~:¿¼JµužEízmí$¼mJ% ôê÷´Ñ&'|}ªÚúA] íËH ™·AO1µG^KýO˜ÚcÓã¿/ªf&=¶zÝë:(—‚³„¥¡ÞOQk}Þb ™Ï§¿= âï×°îcfÂFÿ»Yâüm ÷êƒ ÷œõDžïU†a†a˜‰K “a&2´šDÉÉ´•*B‚œ!ˇû²®,K¦K#m¿ÇPº%Ü®zó¥€vúœlòÛß÷¬LˆûÈ0 Ã0 ó"8ñ#ÃL`h5 ,+MM©ºR4aðÖ9ˆ//܃ïvÕ›/¥ùÂ÷a†a†™`p$Ã0 Ã0 Ã0 Ã0¡À‘ Ã0 Ã0 Ã0 Ä;†a†a†a† v20 Ã0 Ã0 Ã0  ìd`–¨ÌyÝù[ÇÎæ´Ä´Deº,_HGSðzý?yh9ÃÝ5¨Ó‡–†a†a†an Y?®ÀS-x³õ]¹Öôô6¼y\ñ®ïŸì|¿ßß-÷ó¢úewípgMò låC‘¯Eä¹gP^LBÅèÑGq©Ã&Wûw­ùÕóÕÉñÓ»Ñþò¹\—‘´z3ô8çsYkÝY»} ßý’q-¦õßWT«}¯~›<r2<Ïzñ…³³¹säÞ5œúÓ Z‡äAÞ—°$Á2 moïhQä¤8m(Ζâß׋ + ÏÊžâ1tcÿk×néR}Þz!çÀ#À1ãÒ‚‹1Kî#¶7†’æ€öè "çvæÉí^1ýºÂjO6=Öþ¤ûS7„=­#Z0‘1¼'½ïB‡¬ïÄ<ß· Ã0 Ã0?2rŠd˜ƒZÌ,E¢W“ƒ¡ô¿bû ÛäVƒ>Ä6ÎEòÝG…‘ü(âñ¹¨Ý´MŸ3a+–œ B%§Íq$ØäÒÁ0µ=%ï?x@H ù·lÿÈ"T¬Y«~ÁzîZ³ å#qŒêc+äPi‰b¬õ¿,ÉW ¥ŸúAé_‹ÈƒŽ|7."ÇöU< 7î`XºtÊc}ذ7.¶Â ¤§~lÐï` Î&„ÞÜ A¼ÓÇÅèýh)Ó‚[ÌC ÆñUÈt.ä\ï=Øc0ZÉX-î:-Î[ í_ê6,z²q»úçfsÇß÷‰Ší=Ù±9õŽ”ÛÑ80ò>iÀ¦§Ð÷*Ã0 Ã0ÌÄÉàTð²ý…¶‚G_}Шμº÷½$‹6FEe\|ĵ+ÊR#@*R`HÉmå÷þ‘ç6=#OŠ0É·á~ñ1y)0Š@EÜû…ã˜p¢äIùpÀYjKºÎ4=²|)†Zc(ö¶!Pµ·×¼#tž~¿]ô§®QÿOÇ>í@1Q‹u-Oca„öÏ{"TL"Q…rðn_¿µï÷È“V(B`þYSô¬@t£lrAËúÀa¿“‚"êeqÙë‹10Iö ÊG¥"Š6=ƒH|·¸5*¦>0EÙ¥èO” ³=U·Ih÷šE¸~t³ølö¤ÇÂÔ™zG´íaÇ9áe-î«.ÆõÁ án¼ßJ-ôŒ¤©GMâuí²·X¸j…>‘ ìû›•a?gYµÜ—yÊŠñ¶œÞ@‘ b;4­/¶Ê—’2q®Wp,- ¢õ=Uöœ>N)CÝ°Šžx%Ô7Ü+#TYrHè:<âÄÅë˜<û>UgŸ½¡"~äö1°ò7+´Ìýw%Ïåà` ¢âßÛ·Þ¶” :¥_FèçWNUŸœf@b;,FF×W{û¬ªDIÇiÄ®aúa”5OÆW†ò™õ‰z‡Ty±-¨¹Ú·ªãUHB†ê–ÝÊ õÈb¹¿¥E´ÓÎÑËÂ`ÝzA›ôH&Ubö¨Š†H]¯‡ôv’ÞÙ=NôÄI×XöèÙsdÑ'õõäg[¶1¬ö]CÜÜÎoäFý©¯×Öž|ú?¨=6= EÂÐÖrE|]Šôþ\Ý< ß:zŽ k©½^Óó&Z–çÓF÷ñ†hø%ÊGÚNƒüÈå½Ê0 Ã0 3q ž.QU $ΫýdBÔYD¥§£)…³QÉŸ¹ÂÐW#ôý=!¨›ÑU¸^cÚÅR>yæô5Â"¯(EQñ"Ü{FŸ#ÇÈǨ —œòtzšFqe\Ô—öaÙÅTkG…Ø|ó3õ¨i®ã"E ÔÇt-º|ñHgšÓB9EŒuåÌy|®§ÒœHÈÿÛ8qbPòm¢ƒú9Ìû(èyEã­EäÁjŒžÑ‘_šüõ„ñ^e†a†¹³±:Öµ(ƒ(²èia5¡*R/þÿ[ 7dòT*$Žw¡7Rš÷ª? ¡hþ*ièË-Š P§4cíÓûöòaÉÓ?c®JÉûëT:ˆŽF‹Kõ‹,ï‰0Òw(åÄ›!!ééÞ÷C%9`È9@ÉÅ”cA’ƒ/ãûܲ—KQä™k,§‘Lít§S0™£cì{œÐ‡Fj6bý"àë·§œvÚˆö´¡Baïdü;›Ž( nj½ÂŒu‹þ¼ñ; nç’épr¬ú°É³!~÷ظ‘ž+aõ¿Eµp¦N9rq6„Qï-%ìûè@Q ð¼ï „ß« Ã0 Ãü°:(~oç(z?Ö†‘œ6ñ*ÐÓáH­;EbÙT9Qù@F9â©6®ß“û4_µí¸´¯X¡§!ØÊ‡%÷¡F­Ô´ /yü;\/žëN‘ H‚±ÌÑú¢y¢|\\¾Áµ±êÌd_ÔžÊFÏT 3¤‡r&x£*ºer1ʽðhÎzÌlÃý”ã/êÃWö?}ß ÙÏ)Z`Ö¤Ô’“4­bNò{«\ŽŒ‰ÿOÆòP’2Þƒ ‹ž¥å÷àÊðú(ˆ¾“¹(Ä¿›ÆôH†RÌtþ-eCJ³q9ûh6Ñsɪ²ÔÈ-ªÏºj»hìéÁ@U—h—|ê- ¯Åp g¶d ™ÄÒÎá«%øÙ³ÁŒ²†iþ‘uM¦\M-Éq\ÐmilpGöí¡‘ö‚ú?­=Az4äl8Ð5ŽâÞ6®ËToÚsµÚ‰XÈ¡^–çÓégõé ó>ªè¢\—ÜÕQ _d¾óÑsG½W†a†an"KX¦–­Äoñü¬6w‰šxþ×õ:Á—7Ù_ž˜–ô’2Ê­§IÈc²ÂPž0é!ò’SþŠ P¤’2Zåc¢Å€ò¶sÞö”|n€ÒçIü˜ƒ‰¼æv9eD%¡ô,µæ»&S2JZ†± 2·šCò¶·öû„Ò—󻲿0%~$Âqá&m´É y®dØ—GÁ]S“Â+ïaw™JJ&Yw5õ;_i‰w6ÏÀpKlº‰Gñug KÝ~P‰ãôÝÉ’ø‘椧'M$|Ë>á.)H†ÚZ™ ’P‰õd">Jâ×1ž>Ý:‚è®Zà19eÂæî&04Õ+ÒïÒŒ_w‰JÍU•È‘0éò¶ŸÎ/¯RmgÑc»^[; o›R ½ú=m´É _Ÿê„‡¶~P×B{ã2RCæmÐÓALí‘×’Gÿ¦öØôøï‹êc矙I­^÷º„Ê¥à,ai¨÷SÔZŸ·XCæóéoE‚øû5¬û˜™°Ñÿn–8è½ú`Âýûá!g=‘ç{•a†afâèd`˜‰ ­&Qr2m¥Š gÈòᾬ+K„Æ’éÒHÛï1”n ·«Þ|) >g…›ü¶À÷=+â>2 Ã0 Ãü„NüÈ0ZMËJSS*B£®M¼uâË ·Çà»]õæËDig¾ð}g†a†a&ÉÀ0 Ã0 Ã0 Ã0L(p$Ã0 Ã0 Ã0 Ã0¡ÀN†a†a†a†aB Ã0 Ã0 Ã0 Ä;˜p %*s^wþÖ±³9-ñ#-Q™.ËÒѼ^ÿOZÎpw êôaÁ„¥‡a†a†a˜[B–Ä+ðTKÞl}W®õ=½ oB“? Ïk’§°]”É›ê—ݵÃ5Ƀ°•E¾‘çžAy1 £GÅ¥›\íßµæCTÏW'ÇOïFÿAøËçr]FÒêÍÐãœÏe­ugíö1\|÷WHƵ˜Ö_Q­ö½úmò ÈÉð8<ëÅÎÎæjÌ‘{×pêOƒh’yc\Â’Ë€¶½ ¼£EA“â´¡l8KXŠ_/.@¬€>+{6ŠÇÐý¯]»¥Kõyë „œÇŒK .Æ,¹7ŽØÞJšÚ 'ˆœÛ™'·{YÄôë «=ÙôXû“îOÝö´ŽhÁDÆðžô¾ ²¾ó|ß2 Ã0 ÃüÈÈ)’aj1³t‰^qpüUla›»}|Hô«‚y!>Ä6ÎEòÝG…‘ü(âñ¹¨Ý´MŸ3a+–œ B%§Íq$ØäÒÁ0µ=%ï?x@H ù·lÿÈ"T¬Y«~ÁzîZ³ å#qŒêc+äPi‰b¬õ¿,ÉW ¥ŸúAé_‹ÈƒŽ|7."ÇöËçâÆ K—Î@y¬öÆÅV¸ƒôÔ ú ÄلЛ›ƒ!ˆwÚ⸽-eZp‹yhÁ8¾ ـ΅œëý { F+«Å]§Å¹“b‹¡ýK}†EO6nWÿÜlîøû>Q±½';6§Þ‘r;F¾Ó' Øôú^e†a†™€X#ælØŽæESô‘KïÇÛðæq}€Z¬kÙüüߣE¹B£:óbèÞ÷’<,Út•qñ'´®(K©H!%·•CÞúgDžÛôŒ'uäÃd¨-é:ÓôÈò¥j¡ØÛ†@=ÔÞF\óŽÐyúAþv П6ºFý?û´Å=Oca„öÏ{"TL"Q…rðn_¿•ý9¡ùgMѲAÒB°É-ëg‡ýN Šl¨—Åýew®/ÆðXêå𺊞èo¨F“nwĆ6×Ð"GÆ\ÊÍ@Q@+Sƒ¡ªà“iâmØþ†x@³`Íî*BtÝy wP"ËÃ2à2ývÊ8PU‚HobÓ*¤Î]2š+¡zTËä¾"½ÞÆ]Q g\Ô«êHv–çw-:I’©£®e1æ=‰>Ðw]þ}سõ‚UÏê]Ó1|Y´»ŠŽ<׫1º»ºÜvÚô˜äV.Æryì¡·[ŽàÛú®ÕýÿLíAýï×­Ñí±êñ=Ù¯Ë!£?}úõ>o†çÓ{½&nô>:ωúû–0D^¥½'}dþ-(LO.ïU†a†a˜‰‹5’áÜþØþÖ)ñ$ ŸÚÐ+#¯ƒA°¬ ÿ5ƒà®eùFìчÛa”tª‘9::„ò‡UÔA 9„±l+–\QŒò‡QÛ"¶ç^Ö2 ¯ž‰{ƈlÒò–IôI²gP>r(ùP´éDâ»Åõ¨Q1õ)Ên,Ej¤L˜í©ºMzD»×,Âõ£›Åg³— =¦ÎÔ;¢m;Î /kq_u1®}wãýVŠj¡g$H=j¯Ëˆ—½ÀÂU+ô‰LÈ`ß߬ û9˪å¾Ì{PVŒ·åôŠl›xþšÖ[åKI™8W‡+8–Ñúž*{N§ˆ”¡nXEO¼êî•‘ ª,9$tqââuLž}Ÿª3€ÏÞðFþ+³BËÜWò\"Z|ÛámK ¢SúetÀ.q~åTeðÉi1 ¶#Àratat}µ·ɪJ”tœFìj¦/FYód|e(ïYo‘¨wH•ÛØ‚ ™;¡}«:P…$d¨nÙ­ ÚY,–û[ZD;=á½, Ö­ä±IdR%fªhˆÔõzHo'éÝãDOœtež=GÆ}R_¯A~¶Õi Ãjß5ÄÍýàüFnÔŸúzmíɧÿƒÚcÓÓØP$ m-×Q$Á×¥HïÏÕÍÓð­£çȰ–Úë5=oÒ¡ey>m„qoˆ†_¢|¤Ýè4È\Þ« Ã0 Ã0—àéU¥@â¼ÚO&ÒFjj±®ñôžÎÍ023Wúj„¾ÿ 'µc3ú±J׫aL»Xʇ"Ïœž ¦QXä¥(*^„{ÏèsäYãõ/á’Sž®COÓ(®Œ‹úÒ>,¢˜‚jí¨›oþo¦5MÂu\¤Ôc€ú˜®E—/éLsZ(§ˆ±®œ9Ï÷w˽s ù'N JC¾MX”çŽ{ŒúiwcòÀUwzÃÙÎEî¶Ê K˜Ä1@Žƒœ¹ü=®èÝ h´sÇ‹/©-=ŸI¾ªè¢\—ÜÕQ _d¾óÑsG½W†a†an"KXz—­|~V~¯Cßår…‰Â–­ôbZÒKÊ( ·ž&!u"È Cy¤‡ÈKNù(2@‘JÊh• Œ‰ÊÛÎyÛ#Pò¹zHŸ'ñcz$òšÛ唕„Ò³ÔšïšLÉ(hÆ&ÈÜjrIÓ~ÿòŒôÌÌïÊšwÀ”ø‘¿‹:Æ…›´Ñ&'ä¹’a_wiLMr¯¼7†5Þe*)™dÝÕÔï|u¤%~ÜÙ<Ã9,±é&~Å× ,,uûA%ŽÓw'KâGš“žž4‘ð-?ø„»¤ jke2HB%Ö“‰ø(‰_ÇTlxøt뢻j?Æä” ˜»›ÀÐT¯LxH¿K3~Ý%*5WU"G¤ËÛ~:¿¼JµužEízmí$¼mJ% ôê÷´Ñ&'|}ªÚúA] íËH ™·AO1µG^KýO˜ÚcÓã¿/ªf&=¶zÝë:(—‚³„¥¡ÞOQk}Þb ™Ï§¿= âï×°îcfÂFÿ»Yâüm ÷êƒ ÷œõDžïU†a†a˜‰K “a&2´šDÉÉ´•*B‚œ!ˇû²®,K¦K#m¿ÇPº%Ü®zó¥€vúœlòÛß÷¬LˆûÈ0 Ã0 ó"8ñ#ÃL`h5 ,+MM©ºR4aðÖ9ˆ//܃ïvÕ›/¥ùÂ÷a†a†™`p$Ã0 Ã0 Ã0 Ã0¡À‘ Ã0 Ã0 Ã0 Ä;†a†a†a† v20 Ã0 Ã0 Ã0  ìd`–¨ÌyÝù[ÇÎæ´Ä´Deº,_HGSðzý?yh9ÃÝ5¨Ó‡–†a†a†an Y?®ÀS-x³õ]¹Öôô6¼yaâ'·«n6wü}Ÿ¨ØÞ“›SïH¹#ßé“lz }¯2 Ã0 ÃL@¬‘ þh—í/´á©›€UTE8<”x¿ßß­KäêÌ‹¡{ßKò°hÓaTTÆÅGP»¢,5¤"†”ÜV> yëŸynpÐ3ò$¡ˆ“|î“—£T´Á½_8Ž 'ú@žÔ‘ü‘ ¶¤ëLÓ#Ë—b¨5†boõP{qÍ;BçéùÛ5@ÚèõÿtìÓµX×ò4Fhÿ¼'’AEÁ$õX(ïr‹x¡ùgMѲAÒB°É-ëg‡ýN Šl¨—Åýew®/ÆðXêå𺊞èo¨F“nwĆ6×Ð"GÆ\ÊÍ@ÿFV¦CUÁ'ÓÄÛ°ý ñ€fÁ4š=ÜU„èºó*:@ï2  D–†e4Àeúí”q ª‘Þ>ĦU":I;»d:64WBõ¨–É}Ez½»¢@ϸ¨WÕ‘ì:-Ï5îZ,t’$SG]ËbÌ;{} ï(ºü-ú°g뫞ջ¦cø²hwy®Wcuwu¹í´é1É/¬\ŒåòØCo·Á·õ]«ûÿ5˜Úƒ<ú߯[£ÛcÕã{²_—CFúô ê |Þ Ï§÷zMÜè}tžõ÷œåéŽÑ´÷¤Ì¿…éÉå½Ê0 Ã0 3q±F2œÛ¿Ûß:%>„áóBz¥qD#þGñæ ¯#Ñøv¼¨¦Päí`Ü5£ ã#߈=úp;Œ²‘N5òC#GG‡Pþ°Š:(!ƒ0–måÃ’+ŠQ¾ñ0j[ÄöÜËZFäÕ3qÏX‘MZÞò!")£‘>0Iö ÊG¥"Š6=ƒH|·¸5*¦>0EÙ¥èO” ³=U·Ih÷šE¸~t³ølö¤ÇÂÔ™zG´íaÇ9áe-î«.ÆõÁ án¼ßJQ-ôŒ¤©GMâuñ²·/d°ïoV†ýœeÕr_æ=(+ÆÛrzE6ˆí8д¾Ø*_JÊĹ:\Á±´(ˆÖ÷TÙsú8E¤ uÃ*zâ•Pßp¯ŒTPeÉ!¡ëð8ˆ¯còìûT|ö†Žú‘‘?ÀÊ߬Ð2÷ß•<—ƒƒˆÖßvxÛR‚è”~p Kœ_9U|ršE ˆí°\]DD]_ííC²ª%§»Z„éK„QÖ<_Ê;dÖ[$êRåÅ6¶ BæNhߪŽT! ª[v+ƒvÖ#‹åþ–ÑNd8G/ ƒuëylÒ#™T‰Ù£*"u½ÒÛIzg÷8Ñ']cÙ£gÏ‘qDŸÔ×kŸmuÚBưÚw qs?8¿‘õ§¾^[{òéÿ öØô46 C[ËuIðu)Òûsuó4|ëè92¬¥özMÏ›thYžOaÜÇ¢á—(i7: ò#—÷*Ã0 Ã0ÌÄ%xºDU)8¯ö“ =R£F®kºÉxl Ïo¨•gòg®0ôÕ}ÿAOjÇfôc•0®W˜v±”Ež9=AM£°È+JQT¼÷žÑçÈ1²Æ1ê_Â%§<]‡ž¦Q\õ¥}X6D1ÕÚQ!6ßüßL=jš„ë¸H¨Çõ1]‹._<Ò™æ´PNc]9sŸkÔ¹„ü¿'¥!ß&,ÊsÇ=Fý´»1yàª;½álç"w[å…%L☎F ÇAÎ\þWôn4ÚI9¹=þ€–È5Âï÷ÜËQ`m ê»X3‘Þ!wdøƒ Lš,w“]ýJ~µŸëˆ‚öšÌB –“ñO[j„Yc¬w±O\ƒô£€‘è¡×bÒØ;Ö F~)cVÕ»þt;éÐýÔõ:Ú9¯Ê-ïÇ#×ý£ž›Ü†¹ÇJzÛ#ÚÿÐsár¢Í‹±ú -È…ôzI¯¡õšž7ÛóÔÏaÞGéDÏkz×ZD¬Æèù¥É_OïU†a†a˜;«“a]‹2ˆ"‹žÆQª"õrõ,kÂBœÒÑ Gñæ[§€EMy¯*ðÃàŠæ¯’†¾üØ¢ÈuJ3†Ñ¾1½o/–<ñ3æáª”¼?ñ±N¥ƒèˆa´¸T¸Èòžˆ#}‡RN ¹‚‘žî}ßÈ0T’†œ”\L9$9èñ2¾Ï-{i°Ež¹ÆrÉÔNw:“ 9:Æ¾Ç }hDæ0¾~ˉdÐN»‘£Ñƒ6T(ìŒgÓÄM­Wƒ±®qÑŸ7¾bGÁí\2NŽU6y6Äï[7rÀ3âo%¬þ·èq¢ÎÔ)G@.Ά0ê½¥„}(Šž÷}ð{•a†a˜ŸV'…ÀïíEïÇÚ0’Ó&^’ $#¥®S¡ª‘T”CQŽxê£M†ë÷Åä>ÍW­@;.íkVèi¶òaÉ}¨Q+5­Â‹Gÿ׋çºS$(’`,s´¾hž(×opm¬:3Ùµ§²Ñ3Õ 顜 Þ¨Šn™\Œr/<š³3Ûp?åÁø‹úð•ýOÂwBösŠ˜5)µä$M«˜“üÞ*—#£Ccâÿ“±<”¤Œ÷ Â¢giù=¸2üƒ> "ïd.ŠZ¬kLd(Å̽› a(ÍÆåì£ÙDÏ$«ÊR#·4ª>ëª=§Uh\¢^ò©·@.¼ÞiØ’%d>K;‡¯–àgÏ;0ʦùGÖ5™r5µ$7ÆqA·¥±ÁÙ7¶‡FÚ êÿ´öéѳá@×8ŠgxÛ`¸.S½iÏÕj'b!‡z}XžO§ŸUHÔ§/Ìû¨¢‹r]rWG1|‘ù>ÌGÏõ^e†a†¹‰.a™Z¶¿Åó³ÚR¹T²+')¤7Ù_ž˜–ô’2Ê­§IÈc²ÂPž0é!ò’SþŠ P¤’2Zåc¢Å€ò¶sÞö”|n€ÒçIü˜ƒ‰¼æv9eD%¡ô,µæ»&S2JZ†± 2·šCò¶·öû—g¤%.çweÍ;`JüH„ßEãÂMÚh“ò\ɰ/‚»4¦&9„WÞÃï2•”L²îjêw¾:Ò?îlžá–Øt?ŽâëΖºýàû·”%ñ#ÍIOOšHø–|Â]R µµ2$¡ëÉD|”įc*6< |ºuÑ]µÀcrÊ„?ÌÝM`hªW&<¤ß¥¿î•š«*‘#aÒåm?_^¥Ú:Ï¢Çv½¶vÞ6¥zõ{Úh“¾>Õ mý ®…öÆe¤†ÌÛ §ƒ˜Ú#¯%þ'Lí±éñßÕÇÎ?3“[½îu ”KÁYÂÒP裡µ>o±†ÌçÓߊñ÷kX÷Qý»ó&lô¿›%Îßz¯>˜pÿ~xÈYÏAäù^e†a†™¸:f"C«I”œL[©"$Ȳ|¸/ëÊ¡±dº4Òö{ ¥[Âíª7_ h§ÏYáÁ&¿-ð}ÏÊ„¸ Ã0 Ã0?!‚?2̆V“À²ÒÔ”ŠÐ¨+EoƒøòÂí1ønW½ù2QÚ™/|߆a†a˜ G20 Ã0 Ã0 Ã0  ÉÀ0 Ã0 Ã0 Ã0L(°“a†a†a†a˜P`'Ã0 Ã0 Ã0 Ã0¡ÀN&h‰Êœ×¿uìlNKüHKT¦Ëò…t4¯×ÿ“‡–3Ü]ƒ:}X0aéa†a†aæ–%ñã <ÕR7[ß•kýGOoÛDž˜ ÊÇPE’§°]œ/ˆê—ݵÃ5Ƀ°•E¾‘çžAy1 £GÅ¥›\íßµæCTÏW'ÇOïFÿAøËçr]FÒêÍÐãœÏe­ugíö1\|÷WHƵ˜Ö_Q­ö½úmò ä3Ïzñ…³³¹säÞ5œúÓ Z‡äAÞ—°$Á2 moïhQä¤8m(Ζâß׋ + ÏÊžâ1tcÿk×néR}Þz!çÀ#À1ãÒ‚‹1Kî#¶7†’æ€öè "çvæÉí^1ýºÂjO6=Öþ¤ûS7„=­#Z0‘1¼'½ïB‡¬ïÄ<ß· Ã0 Ã0?2rŠd˜ƒZÌ,E¢WÔlÄóҘ܆íbÛÛ]‹ç7Ôª‚y!>Ä6ÎEòÝG…‘ü(âñ¹¨Ý´MŸ3a+–œ B%§Íq$ØäÒÁ0µ=%ï?x@H ù·lÿÈ"T¬Y«~ÁzîZ³ å#qŒêc+äPi‰b¬õ¿,ÉW ¥ŸúAé_‹ÈƒŽ|7."ÇöU<7î`XºtÊc}ذ7.¶Â ¤§~lÐï` Î&„ÞÜ A¼ÓÇÅèýh)Ó‚[ÌC ÆñUÈt.ä\ï=Øc0ZÉX-î:-Î[ í_ê6,z²q»úçfsÇß÷‰Ší=Ù±9õŽ”ÛÑ80ò>iÀ¦§Ð÷*Ã0 Ã0ÌÄÉ0gÃv4/š¢\¶|;æwaûG•€œü>ßhÕ™C÷¾—äaѦ訌‹8 vEYjHE )¹­|òÖ?#òÜ&à gäIB&ù6Ü/>&/F¨hƒ{¿pNô<©#ø#+@mI×™¦G–/ÅPk ÅÞ6ê¡ö6âšw„ÎÓò·k€þ´Ñ5êÿ騧(&j±®åi,ŒÐþyO$ƒŠ‚I$ê±PÞâë·vàýyÒ EÌ?kŠ>’n‚M.hY?8ìwRPdC½,î/»s}1†ÇÊP/‡×UôDC5šÔp»ËÀ 6´¹†92¶àRÖhŠZ™ U}ŸLosÿ]`Íî*BtÝy wP"ËÃ2à2ývÊ8PU‚HobÓ*¤Î]2š+¡zTËä¾"½ÞÆ]Q g\Ô«êHv–çw-:I’©£®e1æ=‰>Ðw]þ}سõ‚UÏê]Ó1|Y´»ŠŽ<׫1º»ºÜvÚô˜äV.Æryì¡·[ŽàÛú®ÕýÿLíAýï×­Ñí±êñ=Ù¯Ë!£?}úõ>o†çÓ{½&nô>:ωúû–0D^¥½'}dþ-(LO.ïU†a†a˜‰‹5’áÜþØþÖ)ñ$ ŸÚÐ+#=â_Z¡þ/øùªz÷c3îšQ†ñ‘oÄ}¸FÙH§ù¡‘££C(XE”ƒA˶òaÉÅ(ßxµ-b{îe-# òꙸg,È&-où‘”ÑH˜${å#‡R‘E›žA$¾[\S˜¢ìÆRô§FʄٞªÛ¤G´{Í"\?ºY|6{ ÒcaêL½#Úö°ãœð²÷Uãú`Їp7Þo¥¨zFÒˆÔ£&ñºŠxé®Z¡OdBûþfeØÏYV-÷eÞƒ²b¼-§7PdƒØŽMë‹­ò¥¤Lœ«ÃK‹‚h}O•=§SDÊP7¬¢'^‰õ ÷ÊHU–ºƒ8qñ:&ϾOÕÀgo¨¨¹} ¬üÍ -sÿ]És98ˆh ðm‡·-%ˆNé—ѺÄù•S•Á'§PĀ؎˅ÑED„ÑõÕÞ>$«*QÒq±«E˜¾DeÍ“ñ•¡¼Cf½E¢Þ!U^lc *dî„ö­êx@’¡ºe·2hg=²Xîoiíô@†sô²0X·^Ç&=’I•˜=ª¢!R×ë!½¤wv=qÒ5–=zöGôI}½ùÙV§-d «}×7÷ƒó¹Qêëµµ'ŸþjMOcC‘0´µ\G‘_—"½?W7OÃ·Žž#ÃZj¯×ô¼I‡–åù´Æ}¼!~‰ò‘v£Ó ?ry¯2 Ã0 ÃL\‚§KT•‰ój?™P#5Ç_Å'‰zìxñ%¹E§„V(s…¡¯FèûzBP;6£«„q½ Æ´‹¥|(òÌé j…E^QŠ¢âE¸÷Œ>GŽ‘5ŽQÿ.9åé:ô4âʸ¨/íò!Š)¨ÖŽ ±ùæÿfêQÓ$\ÇEŠ@=¨éZtùâ‘Î4§…rŠëÊ™óø|·Ü;7ÿ·qâÄ 4äÛ„Eyî¸Ç¨Ÿv7&\u§7œMà\än«¼°IÓÑä8È™ËßãŠÞ ‚F;/©\&…òD0Äû}#÷rX†Cƒú.ÖLF¤wÈþ “&ËÝdW¿’_íÃç:¢ ½¦³P‚ådüÓ–aÖëGì× ý(`$z赘4öŽõG„ÑG†_ʘUõ.‡ÝÎp*t?u½†vΫrËûñÈuÿ¨çÇ&·aîDZ’ÞŸÆöˆvçßÿô\¸\„hób¬~B r!½^ÒßkhG@½¦çÍö|õs˜÷Q:ÑóšÞµ‘«1zFG~iòׯ{•a†aæÎÆêdX×¢ ¢È¢§…qÔ„ª9ÔêÞ‘Ù7JIô«åÁƒC(š¿Júòc‹"Ô)ÍFûÆô¾½|XòtÆÏ˜‡«RòþÆÇ:•¢#†ÑâR}à"Ë{"ŒôJ91äfHFzº÷}#GÀPIrPr1åXä ÇËø>·ì¥ÁRyæËi$S;ÝéL&äèû'ô¡‘šX¿øú-ýoæcí´+9í1hC…ÂÞÉøw6Q@ÜÔz…1ëýyã+vÜÎ%ÓáäXõa“gCüî±p#<#þVÂê‹'jáLräâl£Þ[JØ÷Ñ¢àyß¿W†a†ù)`u2PüÞÎQô~¬ #9m"}Äfžz¼_Ê-ÌÛ刧>Úd¸~_LîÓ|Õ ´ãÒ¾vQ…ž†`+–܇µRÓ*¼xäñïp½x®;E‚" Æ2Gë‹æ‰òqqøׯª3“}Q{*=S-ÌÊ™àªè–ÉÅ(÷£9ë1³ ÷SŒ¿¨_Ùÿô!|'d?§hY“RKNÒ´Š9Éï­r92:4&þ?ËCIÊx*,z––߃+Ã?è£ øN梨źÆôH†R̬ѻÙ†Òl\Î>šMô\A²ª,5rK£ê³®Úã.{z0PUÆ%Zà%Ÿz äÂk1虆-YBæ±´søj ~öl°£¬ašd]“)WSKrct[Ü‘}c{h¤½ þOkO 9t£x†· †ë2Õ›ö\­v"r¨×‡åùtúYED}ú¼*º(×%wuÙïÃ|ôÜQïU†a†a˜›Hà–©e+ñ[g…›ü¶À÷=+â>2 Ã0 Ãü„NüÈ0ZMËJSS*B£®M¼uâË ·Çà»]õæËDig¾ð}g†a†a&ÉÀ0 Ã0 Ã0 Ã0L(p$Ã0 Ã0 Ã0 Ã0¡ÀN†a†a†a†aB Ã0 Ã0 Ã0 Ä;˜p %*s^wþÖ±³9-ñ#-Q™.ËÒѼ^ÿOZÎpw êôaÁ„¥‡a†a†a˜[B–Ä+ðTKÞl}W®õ=½ o×ò›PEëþ¿µï÷ÈÂ@ÍF<ÿëzDh?y ÛÅï©~Ù];ÜY“<[ùPäkyP1zôQ\ê°ÉÕþ]k>Dõ|urüônô„¿|.×e$­Þ =Îù\ÖZwÖnÃÅw…d\‹iý÷Õjß«ß&‚œ ó^|áìl®Æ¹w §þ4ˆÖ!y7Æ%,ÉA° hÛ›À;Z9)Nʆ³„%ý;Z€X}Völ¡û_»vK—êóÖ9Ž—\ŒYro±½1”4´?@O9·3On÷²ˆé×V{²é±ö'ÝŸº!ìiÑ‚‰Œá=é}:d}'æù¾e†a†ù‘‘S$ÃÔbfé(½â€ Ò0jºƒé×µèyk¶¿° {»k±ã7+ô9âClã\$ß}TÉ"Ÿ‹ÚMÛô9¶òaÉ ú(TrÚG‚M. SÛSòþƒ„ô’pËö,BÅšµêy¬ç®5›P>Ǩ>¶B•–(ÆZùË’\ÜG?õƒÒ¿‘ùn\DŽí?þª¸ï7î`XºtÊc}ذ7.¶Â ¤§~lÐï` Î&„ÞÜ A¼ÓÇÅèýh)Ó‚[ÌC ÆñUÈt.ä\ï=Øc0ZÉX-î:-Î[ í_ê6,z²q»úçfsÇß÷‰Ší=Ù±9õŽ”ÛÑ80ò>iÀ¦§Ð÷*Ã0 Ã0ÌÄÉ0gÃv4/š¢\z?öF3ü5N$dÏïÂö7ŽÒIù°²ú¼}d›FuæÅнï%yX´é0**ãâ#¨]Q–R‘CJn+†¼õψ<· 8èy’PÄ€I¾ ÷‹ÉKQ*ÚàÞ/Ç„} OêȇþÈ P[Òu¦é‘åK1ÔC±· z¨½¸æ¡óôƒüí ?mtú:öiЉZ¬ky eøŠ÷~«(˜D¢ åà]ZÔ‹Š˜Ö} +$Ý(›\в~pØï¤ È†zfã+»s}1†ÇÊP/‡×UôDC5šÔp»ËÀ 6´¹†92¶àRÖhõoAè>‚O¦‰·¥þýaÍî*BtÝy wP"ËÃ2à2ývÊ8PU‚HobÓ*¤Î]2š+U$’.ï½ éõ6îŠ=ã¢^UG²ë´<׸k±ÐI’Lu-‹1ïìI|ô¼£èò·èÞ­¬zVáË¢ÝUtä¹^iÔÝÕå¶Ó¦Ç$¿°r1–Ëc½ÝrßÖt­îoü×`jòè¿nnUïyÈ~]ýéÓ/¨7ðy3<ŸÞë5q£÷ÑyNÔß·„áïSÚ{ÒGæß‚Âôäò^e†a†™¸X#ÎíßíoHÂð‘Q d9†LæÌ*E2q^ì‘ÑùJœJ‹tðs׌2Œ|#öèÃí0ÊF:ÕÈBùÃ*ê „ ÂX¶•K®(FùÆÃ¨mÛs/kaWÏÄ=c D6iyˇˆ¤ŒFúÀ$Ù3(9”Š|(Úô "ñÝâzÔ¨˜úÀe7–¢?5R&z0U·Ih÷šE¸~t³ølö¤ÇÂÔ™zG´íaÇ9áe-î«.ÆõÁ án¼ßJÑ+é‘-‚H=j¯«È–N`á*{d ìû›•a?gYµÜ—yÊŠñ¶œÞ@‘ bÏ_Óúb«|))çêpÇÒ¢ ZßSeÏéã‘2Ô «è‰Wb@}ý2RA•%‡„®Ãã` N\¼ŽÉ³ïSuðÙ*ºGn+³BËÜWò\"Z|ÛámK ¢SúetÀ.q~åTeðÉi1 ¶#Àratat}µ·ɪJ”tœFìj¦/FYód|e(ïYo‘¨wH•ÛØ‚ ™;¡}«:P…$d¨nÙ­ ÚY,–û[ZD;=á½, Ö­ä±IdR%fªhˆÔõzHo'éÝãDOœtež=GÆ}R_¯A~¶Õi Ãjß5ÄÍýàüFnÔŸúzmíɧÿƒÚcÓÓØP$ m-×Q$Á×¥HïÏÕÍÓð­£çȰ–Úë5=oÒ¡ey>m„qoˆ†_¢|¤Ýè4È\Þ« Ã0 Ã0—àéU¥€t’‰Bá£Ú¥íÛðûýýZÄ\aè«úþƒžÔŽÍèÇ*a\¯„1íb)Šßß-÷Î $äÿmœ81( ù6aQž;î1ê§ÝÉWÝé g8¹Û*/,A`Çt49ræò÷¸¢wƒ ÑÎ/¾¤¶ÇÐÒy¢Fâý¾‘{9 ¬ áA}k&#Ò;äŽ ЃI“ån²«_ɯöásQÐ^SƒY(Ár2þiK0kŒõŽ#ö‰k~0=ôZL{Çz#Âè#Ã/e̪z—Ã?‚ng8ºŸº^C;çU¹åýxäºÔóc“Û0÷ƒãXIïOc{D»óïz.\.B´y1V?¡¹^/éï5´# ^Óóf{>ƒú9Ìû(è¶(;#ky°£gtä—&=a¼W†a†aîl¬NŠF ƒ(²èia5¡*R¸z‘‘EMÒÁ £j*Ä'§‡P4•4ôåÇE¨Sš1Œöé}{ù°ä錟1W¥äý Œu*DG £Å¥úÀE–÷D é;”rbÈÍŒôtïûFŽ€¡’0ä äbʱ ÉA—ñ}nÙKƒ¥(òÌ5–ÓH¦vºÓ)˜LÈÑ1ö=NèC#5±~ðµÎU²ýcí´+9í1hC…ÂÞÉøw6Q@ÜÔz…1ëýyã+vÜÎ%ÓáäXõa“gCüî±p#<#þVÂê‹'jáLräâl£Þ[JØ÷Ñ¢àyß¿W†a†ù)`u2PüÞÎQ™ƒAFrÚDÀˆÍñ.úÓÓ)~¾ª‘x—½<刧>Úd¸~_LîÓ|Õ ´ãÒ¾v`…ž†`+–܇µRÓ*¼xäñïp½x®;E‚" Æ2Gë‹æ‰òqqøׯª3“}Q{*=S-ÌÊ™àªè–ÉÅ(÷£9ë1³ ÷SŒ¿¨_Ùÿô!|'d?§hY“RKNÒ´Š9Éï­r92:4&þ?ËCIÊx*,z––߃+Ã?è£ øN梨źÆôH†R̬ѻÙ†Òl\Î>šMô\A²ª,5rK£ê³®Úã.{z0PUÆ%Zà%Ÿz äÂk1虆-YBæ±´søj ~öl°£¬ašd]“)WSKrct[\—«±=4Ò^Pÿ§µ'H†œ ºÆQ<ÃÛÃu™êM{®V; 9ÔëÃò|:ý¬¢@¢>}aÞG]”ë’»:Šá‹Ì÷a>zî¨÷*Ã0 Ã0ÌM$p ËÔ²•ø-žŸÕ†ßËÐwgùJÎr•rCmD岄¥iI/)£,Üzš„<Ö‰ + å “"/9å? ÈE*)£U.0&Z (o;çm@Éçèq }žÄ9è‘Èkn—SFTJÏRk¾kr0%£t°=ýþåÓƒÚ0%~$Âqá&m´É y®dØ—GÁ]S“Â+ïaw™JJ&Yw5õ;_i‰w6ÏÀpKlº‰Gñug KÝ~P‰ãôÝÉ’ø‘椧'M$|Ë>á.)H†ÚZ™ ’P‰õd">Jâ×1ž>Ý:‚è®Zà19eÂæî&04Õ+ÒïÒŒ_w‰JÍU•È‘0éò¶ŸÎ/¯RmgÑc»^[; o›R ½ú=m´É _Ÿê„‡¶~P×B{ã2RCæmÐÓALí‘×’Gÿ¦öØôøï‹êc矙I­^÷º„Ê¥à,ai¨÷SÔZŸ·XCæóéoE‚øû5¬û˜™°Ñÿn–8è½ú`Âýûá!g=‘ç{•a†afâèd`˜‰ ­&Qr2m¥Š gÈòᾬ+K„Æ’éÒHÛï1”n ·«Þ|) >g…›ü¶À÷=+â>2 Ã0 Ãü„NüÈ0ZMËJSS*B£®M¼uâË ·Çà»]õæËDig¾ð}g†a†a&ÉÀ0 Ã0 Ã0 Ã0L(p$Ã0 Ã0 Ã0 Ã0¡ÀN†a†a†a†aB Ã0 Ã0 Ã0 Ä;˜p %*s^wþÖ±³9-ñ#-Q™.ËÒѼ^ÿOZÎpw êôaÁ„¥‡a†a†a˜[B–Ä+ðTKÞl}W®õ=½ o×ò›PEëþ¿µï÷È›ÜBõËîÚáΚäAØÊ‡"_‹ÈsÏ ¼˜„ŠÑ£âR‡M®öïZó!ªç«“ã§w£ÿ üås¹.#iõfèqÎç²Öº³vû.¾û+$ãZL뿯¨Vû^ý6yädxžõâ ggs5æÈ½k8õ§A´Ƀ¼1.aI‚e@ÛÞÞÑ¢ ÈIqÚP6œ%,éßËÄ è³²g£x ÝØÿÚµ[ºTŸ·Þ@È9ðp̸´àbÌ’{ãˆí¡¤9 ýz‚ȹyr»—EL¿®°Ú“Mµ?éþÔ aOëˆLd ïIï»Ð!ë;1Ï÷-Ã0 Ã0ÌŒœ"æ 3KG‘è5ñ¼4ŒÚ@‡)lr+âClã\$ß}TÉ"Ÿ‹ÚMÛô9¶òaÉ ú(TrÚG‚M. SÛSòþƒ„ô’pËö,BÅšµêy¬ç®5›P>Ǩ>¶B•–(ÆZùË’|Rú©”þµˆ<èÈwã"rlÿñW±=ÃÒ¥3PëÆ½q±î` =õcƒ~q6!ôææ`â¶8.FïGK™ÜbZ0ޝB6 s!çz?èÁƒÑJÆjq×iqî¤ØbhÿRŸ°aÑ“ÛÕ?7›;þ¾OTlïÉŽÍ©w¤ÜŽÆ‘ïôI6=…¾W†a†a& ÖH†9¶£yÑ}äÒû±7šá¯‘ȈX°ÉÓ Qy1tï{Im:ŒŠÊ¸øˆjW”¥F€T¤À’Ûʇ!oý3"ÏmzFž$1`’oÃýâcòR`Š6¸÷ Ç1áDÈ“:òá€?²Ô–tizdùR µÆPìmC jo#®yGè<ý »èO]£þŸŽ}Úb¢ëZžÆÂíŸ÷D2ˆç ¥‰D=ÊÁ»Ü¢[(B`þYSô¬@t£lrAËúÀa¿“‚"êeqÙë‹1D$e4Ò&ÉžAùÈ¡TäCѦg‰ï×£FÅÔ¦(»±ý©‘2a¶§ê6éí^³×nŸÍ^‚ôX˜:S=ì8'¼¬Å}ÕŸ>ô!Ü÷[·ég$H=j¯‹sÛ°·X¸j…>‘ ìû›•a?gYµÜ—yÊŠñ¶œÞ@‘ bÏ_Óúb«|))çêpÇÒ¢ ZßSeÏéã‘2Ô «è‰Wb@}ý2RA•%‡„®Ãã` N\¼ŽÉ³ïSuðÙÔ?zûXù›Zæþ»’çrp0ÑàÛo[JÒ/£t‰ó+§*ƒON3 ˆ±– £‹ˆ£ë«½}HVU¢¤ã4bW‹0}‰0Êš'ã+Cy‡Ìz‹D½Cª¼ØÆTÈÜ í[Õñ€*$!CuËneÐÎzd±ÜßÒ"Úé çèea°n½ Mz$“*1{TEC¤®×Cz;Iïì'zâ¤k,{ôì92Žè“úz ò³­N[ÈVû®!nîç7r£þÔ×kkO>ýÔ›žÆ†"ahk¹Ž" ¾.Ez®nž†o=G†µÔ^¯éy“-Ëói#ŒûxC4üå#íF§A~äò^e†a†™¸O—¨*çÕ~2qáð™Ì†¾¡ï?è AíØŒ~¬Æõ*@Ó.–ò¡È3§'¨iyE)ŠŠáÞ3ú9FÖ8FýK¸ä”§ëÐÓ4Š+㢾´ˆ(¦ Z;*Äæ›ÿ›©GM“p)õ >¦kÑå‹G:ÓœÊ)b¬+gÎãóýÝrïÜ@Bþ߯‰ƒÒoå¹ã£~Úݘ×í55˜…,'㟶Ô³ÆXï8bŸ¸éG#ÑC¯Å¤±w¬8"Œ>2üRƬªw9ü#èv†S¡û©ëu0´s^•[ÞG®ûG=?6¹ s?8Ž•ôþ4¶G´;ÿþ7 çÂå"D›cõZ éõ’þ^C;ê5=o¶ç3¨ŸÃ¼Ò‰ž×ô®µˆ,y:ãgÌÃU)yãcJÑÃhq©>p‘å=Fú¥œr3$#=Ýû¾‘#`¨$ 9(¹˜r,HrÐãe|Ÿ[öÒ`)Šlòlˆß=¶nä€gÄßJXýoÑãD-œ©SŽ€\œ aÔ{K û>:P<ïûá÷*Ã0 Ã0?¬N ßÛ9*s0HÃHN›ÈoÄ&2ÊO}´Épý¾˜Ü§ùªhÇ¥}íÀ = ÁV>,¹5j¥¦UxñÈãßázñ\wŠEŒeŽÖÍåãâ:ð ®Ug&û¢öT6z¦Z˜!=”3ÁUÑ-“‹Qî…GsÖcfî§<Q¾²ÿéCøNÈ~Nѳ&¥–œ¤is’ß[årdthLü2–‡’”ñTXô,-¿W†ÐGA$ðÌEQ‹ué‘ ¥˜Y£w³! ¥Ù¸œ}4›è¹‚dUYjä–FÕg]µÇ]4öô` ªK´ÀK>õÈ…×b8Ð3 [²„ÌbiçðÕüìÙ`FYÃ4ÿȺ&S®¦–äÆ8.è¶46¸#ûÆöÐH{AýŸÖž =r6èGñ o ×eª7í¹ZíD,äP¯Ëóéô³Š‰úô…yUtQ®NsÅðEæû0=wÔ{•a†aæ&¸„ejÙJüÏÏjÃïe軳L¥‡ä)loí·ÈßÕLKzIeáÖÓ$ä±NYa(O˜ôyÉ)ÿE(RI­r1Ñb@yÛ9o{J>7@éó$~ÌAD^s»œ2¢’Pz–Zó]“ƒ)¥CÐóàYž‘–¸œß•5ï€)ñ#9~uŒ 7i£MNÈs%þ< îÒ˜šä^yo k¼ËTR2ɺ«©ßùêHKü¸³y†sXbÓMü8Н;XXêöƒJ§ïN–Ä4'==i"á[~ð wIA2ÔÖÊd„J¬'ñQ¿Ž©Øð$ðéÖDwÕŒÉ)þ0w7¡©^™ð~—füºKTj®ªDŽ„I—·ýt~y•jë<‹ÛõÚÚIxÛ”JèÕïi£MNøúT'<´õƒºÚ—‘2oƒžbj¼–<úŸ0µÇ¦Ç_T;ÿÌLzlõº×%tP.g KC½Ÿ¢Öú¼Å2ŸO{(Ä߯aÝÇÌ„þw³ÄùÛ@ïÕîß9ë9ˆ<ß« Ã0 Ã0—@'ÃLdh5‰’“i+U„9C–÷e]Y"4–L—FÚ~¡tK¸]õæKíô9+<Øä·¾ïY™÷‘a†aæ'DpâG†™ÀÐjXVššRu¥hÂà­s0_^¸=ßíª7_&J;ó…ï;Ã0 Ã0 3ÁàH†a†a†a†aB#†a†a†a† v20 Ã0 Ã0 Ã0  ìd`†a†a†a&ØÉÀ„-Q™óºó!BKN6ß„äŽas‹Ú¹3½ŽÛÕ?aÔK:šœ%J#´läîÔé KÃ0 Ã0 ó“'‹“ažjÙ(÷h­ÿ§–É]¿ø’0*·c]ÒÐ$¹Øôï©~-‡QKÛs/ka¶ò¡È×"òœ–éíþ† ¹â®5¦äkÖ IZù\®ËH6=Îù·õ±ZãÝÑaÔ•¦'kù F†êÎæjaÓ6-eZx›Èpä-ى㠼£ ¡zÃÀXïÙÚ0o/ýZP(ô¾*ÌVölžUÏ-‰x«Œpo½…²z÷bl‘[KnNûÃh§‰[Ù×&Ò¯+¬ödÓcíOrµLÕïßÛßßþæxÿöÖ¶|ˆˆxý1 Ã0 sgS$ÃÔbfé(½â f#žqb/´]j±®øä…mØþÂëøõx~C­>gbîß8ÉwEw룈Çç¢vÓ6}΄­|Xrb µœ¶KZl‘ÓGNõÔö”¼ÿà!=€äܲý#‹´ó!_‚õܵfÊGâÕÇV:6§tÈíhùNŸ4èÉRÞÊñWÅ}ŸéÃ[†0D7ì½1ÚXºtÊc}BW\lƒhÒ' ¤e}š£"„vþmSµÕà¦ö× â?œÕ‡ú'oBª÷¶8.Fï¿mNŸ‡Œã«×®é£[GÎõ~Ѓ=Ïô ý¶“±ZÜuZœ;)¶Ú¿Ô'lXôdãvõÏÍæŽ¿ï–µˆ<ý÷e7.Âù»–çßÍê—1}þPª|÷Ñ!”?ôýÀ0 Ã0̭ĺ„åœ ÛѼhŠ>réýxÞè÷ëñ.~¿¿[KÒ ÑŒy1tï{Im:ŒŠÊ¸øXjW” £þWH ûVòô1Ar[ù0ä­Fä¹MÀAU¯ °˜äÛpK—Z7ëcôÛgpïŽcB—Ë“?½[9&(²bã"I)µ%]gšY¾C­1{Û“ϵØô¤°]»—Z¬ky #´Ÿ¤ SD¢ åÓ(¾¶<+É¿+Ɔ6÷›F·O“¡JÑËd‚$Ú´ñJ#÷õRìÊ$eÅxûß•a²>ÄÀ O¯ 2àçŸûtK½V¹†""Èa±ùÄÿ!míܹ¾Ãce¨ŸEG×pêOƒèo¨F“<öài?éªûÖÕí@N ö;G¬7®—ž-¸”qÍéP´ÕÊÔh¦zá“iâmØþ†xd õÇÐýÚè£Qèá®"Dп°qÄöjãF™)‘e€aÆúeúí”q ª‘Þ>ĦU":I;»d:64WB=AZ&÷éõ6îŠ=ã¢^UG²ë´<׸k±ÐI’Lu-‹1ïìI|ô¨ýgt9ù[ôaÏÖ V=«wMÇðeÑî*:ò\¯&½„«Ëm§MI~aåb,—Çz»±§uÄÚt­îoü×`jòè¿nnUïyÈ~]ýéÓ/¨7ðy3<ŸÞë5q£÷ÑyNÔwDÂówÁý}ŸŽ}ÚAïþwÓ¤‡þþ®Žª2ôw¼lDÿ=e†aæ¶cd8·¶¿uJ—âƒ\F-ÐG»ã`ÈF-¬‚ዃAp׌2Œ|#ö胂>:Õh:¤§F%¶¡DŽVl¶–K®(FùFS¸¦A^=÷Œ%7™ášôD²gP>r(õ¡T´éDâ»ÕÈ ÖÈ"Ь(EjDFô^ªn“Ñî5‹pýèfñ9ç%H¦á—BO»vØôxð•·Ñ÷[)z%=²E©GMâuqnöv W­Ð'29qbçfMrCæ…!?gà*Þ!‡Á2c•" Ä&ž¿&a¬.EZßS²sê)vþ»É8û'§|RKÍKS$ÈгLO— é–zmrjAû b[;)CÝ°Šžx%Ô7Ü+GîUY2Ðuƒ›t+©ôOmªÃK‹¾³Þ|»Þ¯còìûR}lã³7è9ÔÛÇÀÊ߬Ð2÷ý%Ïåà` ¢5À·Þ¶” :¥_FèçWNUß#†E ˆí°\]DD]_ííC²ª%§»Z„é4m¡y2¾2”wȬ·HÔ;¤Ê‹mlA… ¹oߪŽT! ª4E‚ ÚYèéiaöd8G/ ƒuëylÒ#™T‰Ù£*"u½ÒÛIzg÷8Ñ']cÙ£gÏ‘qDŸÔ×kŸmuÚBưÚw qs?8¿‘õ§¾^[{òéÿ öØô46 C[ËuIðu)Òûsuó4|ëè92¬¥özMÏ›thYžOaÜÇÜX‹ûª‹q}Ðq ˜ÿnšy —ZwãÚƒêïoñçï)Ã0 Ã0wÁÓ%ªJÄyµŸLä ÿóß<…‰¶s…¡¯F,úzBò;6£«ÄÇTxG×-åC‘g†kªiyE)ŠŠá^ñq£Œú!”¯qŒzúÒåé:ô4âʸ¨/íC¨!Š)¨–Jr[ávÍÔ£¦7>Àõky°£gT‡UO ùÂ8Ïu$˹„ü§"˜¯',ÿm]çΊÛiwc29”X†áŸ‹ÜmŸ×,Ã9Ow ç¶mÂÂ:wÜcäZêµÉOó¡y†ÿ” HÇ´3‚ é\!Ürì÷87ò£°zoœë½ü=®èÝ h´3•æñ´´@ž¨†x¿oä^ŽkÃphP»æj&#Ò;äŽ ЃI*†&ÙÕ¯äWûð¹Ž(h¯©Á,”`¹“/!5¬1Ö;ŽØ'®AúQÀHôÐk1iì뎣 ¿”1«ê]ÿºáTè~êz íœWå–÷ã‘ëþQÿ~mræ~p+éýilhwþýo @Ï…ËEˆ6/Æê'´ Òë%ý½†vÔkzÞlÏgP?‡yå`…%Šœîþ¿A¦¿›ŠL=4Hà8í+8-‘a†a˜›Õɰ®E}¨G=->Ú›P©ÿÏž,¹5Н¦UxñÈãßázñ\wŠEŒeŽÖÍåãâ:ð ®Ug޶P{*³fÆ&=kà‹ª ºÚÖ££¾pœþè ¯Õ/éåoÂP?[< [WŠòoÿY•4Ší™FAÓæ$¿·Žä¦—ß™Ê'–z³µç?ìÇËì sçT’R¾ƒå4åÀ;¥€ú “±<”äˆæzÕ´’›¹ê†¹^biù=¸2üƒ> "ïdÎJB›ÉPŠ™5z7ÂPšËÙG³‰ž+HV•¥FniT}ÖU{ÜEcOª*äjäSo\x-†=Ó°%KÈ| –v_-Áϲ¬4QÖ0Í?²®É”«©%¹1Ž º- îȾ±=4Ò^Pÿ§µ'H†œ ºÆQ<ÃÛÃu™êM{®V; 9ÔëÃò|:ý¬¢@Ôê#aÞG]äœßÉÁ`p¦;¸7zÈÉïuêSd¡áï/Ã0 Ã0·kâG‚¢¢§·áMüÏÏjÓI)ácdŽ'‡ä)lÿàù_×»Iª$ÞD€(ù£Ö?¦?:¤Œ²OëiòX'‚¬0”'Lzˆ¼ä4”Fô©¤ŒV¹À˜h1 ¼íœ·=%Ÿ Çôy6õhý&ÜëÏÀ '°¼ËóÐÚ/ä´ ‰¾ÿ´¼éü®¬.dÌþ. ™Йò dÎG¯›LòÌQBEr¯¼7†:é—ÉEYÊ™Pw5k~SâG[½6¹o"F[;×8‰-IFÉ$=íôÕ¡!R—g&}$dùŒÄ™áÔ+¡rË"rJI¶é ¡Ö+Ø)§¡d_ñÃMü8Н;XXê>o*qœþ×”%ñ#ÍIOOšHx'RØú–º!9í€ µµ2$¡ëÉD|”įc*6< |ºuÑ]µÀcrÊ„?ÌÝM`hªW&<¤ß¥¿´D¥/gæU•È‘0éò¶ŸÎ/¯RmgÑc»^[; o›R ½ú=m´É _Ÿê„‡¶~P×B{ã2RCæmÐÓALí‘×’Gÿ¦öØôøï‹êc矌I­^÷º„Ê¥ ûßT裡µ>o±†ÌçÓߊñ÷kX÷Qý»ó$lôýÍt ¿±À¿wz*)´7áüýe†aæN ÐÉÀ0ÌÄ¢JNæ“"(ç„L¨yãË{æCc妰dº4Òö{ ¥[Âíª7_ h§ÏYáÁ&¿-ð}ÏÊ„¸ Ã0 ÃÜ'~dfÂ@«:`Y©»BGH¡¿ÿ68(ÚrOÜ2ñå…ÛcðÝ®zóe¢´3_ø¾3 Ã0 ÄG20 Ã0 Ã0 Ã0  ÉÀ0 Ã0 Ã0 Ã0ÿÿöþ?6ªãúÿÇŸRâ†]°MlÀŽldôY°)Brñ«¢*ˆDƒxWHDÂi”¤úGC*Gâ¯H±(äª ˆ$Ô7ID‹ÔX!Eð…¾!HUCÉìKX±aÛᇽÆ6¼ ÉKúΙ™»÷ÇÎÜÝ5—5NÎCºò½çÎ3wîõÞ9gÎÌ0‘ÀN†a†a†a†a" Ã0 Ã0 Ã0 ÃD;˜h %*ë¡Z ±5úÉ#gªÊY$½»‚:¾ç÷›Ez)õÎÒ¨ŒZ¾qoôᤉ*†a†a&‹N†5x¾m«Ü£5èŸ_%wBþÆnaTîÄ3uZ„z<ÓF2½éëB¡5³ÛŽ¡ž¶—ßÔÂlé#‘oBüe-ÓÛCÍar­ÕíÈ«6n’@ú|îËH®|œóïéc Íï¹yóòžóäï½§ú¶¿#^«OLG 0ÜvµÖ C‘¶¹h+×Â)"Ëh/“ÑKKgâTïëãÉ0î7 Œz»ÓèÀ\¼·òGZ0Yè÷yr¿ò_'°å×êÿ…–&,–îÕ;Y6ì]†çä–@Ëò»Sþ(Êi¢˜um"x_Q•'W>Öú$'PÛl}0Í1~g o'|¯¾Ë Ã0Ì÷ž¼" óÊF‘îu[ñêK|­tèÒƒÛw`çkjû4Ý„W·Ôës&và¡­ 1vðqô´?ŽTj!ê·íÐçLØÒG%'ÆqUËi»vV‹-rúè×Î>‘9,¤‡1ö'7íÀõFí|(”ð|îÛ¸ ×SÕÇVÎnÏä!·)àú7â„hä¬OMÊ÷â*tþµo¢rñ°'ý0* {.šSÏýÏø—>,Â0Û²ÿÎ \båʹ¨Hö‹¼Rb»ó¥Û6•³`"ÐKKXÚ _ª·¦ñËø}p‘üi|¿“""½ïw¤p5ñД9¹~ºdŸ¿uK¼õ~Ô‹}/õ"øº‘±ë:/Î[§ÿ£Oذ䓋©ªŸ»Í=ÿܧ-–ïl¡í„É~—†afа.a¹`ËN´6ÎÒG.}ŸìÀ;§hzËþ éw_LJ½ò”Šjxe§´È»¿(‰ž»åaɶc¨ªN‰§¸zM¹0ê1aKCž>®$·¥BÞþÄ_ÞQz]¨ÇÁ$߇Ú¸Ö¾]› k_Âÿvê¸"&Obâü^嘠Ȋ­(‘R*K0Ï@>2}†Û“ˆyËW>¦{Qõ\‰¢< Å}­N(]T?å×u9¨g½4Nû—ðiÆÑ@Q0UH§›°Tö¸ŒâK㻢 cõ·¥#ØÒá68©·÷<n°J*Œ¡CsÔƒÞ$Å®LRÃ{O•c¦>Äàe_¾&È^Üò˽V¹†""Èa±ýÌÿÊc[9wmŽad¼Móéè¾øÛe 4×b½<öGùMD©—òjøÚ½'r¦à˜ß)ó}¸ßBˆZ/ý/<‡kYu„¢ËÖfz3Õÿ|2Mª;ß?|9 Cý ôà6ú¨z¤«‰%ô‹2ä~m¼S/óc¥2 0‚“ÂX¢kgM5¥ˆ÷õ#9§‰Õ¹î啨ÒZ YE:½÷ß,¨·eOèz•ޱ®óò\Ëže"O’dçÑж ‹ºÏáã´@@å¿ ÓÉkÑ}¯\±æ³aO%F†D¹kèÈs¿š`9 7/·œ¶|Lò+k—aµ<öÐ׃}í×­õ@÷ê^ã¿SyP@ýûóÖèòXóñ½¹ïË!«>}ù Bô†¾o†÷Ó{¿&îô9:ï‰j7¥=ßA?îwÖû= ¶LùP{£ï2Ã0 ÃL-ÖH†‹‡^ÇÎw¿ƨh Ê¨jĆ8 $Î0 at¦;BÓÞ7·׿{ô¥f§ê•§ž÷Œ—~J¥÷~»5}TrE [Má‹yí<ÌO#.>öÙá‹Ô ÙK¨¸~4Óp(Ùö⩽ª'‚z/d"+Ê0é¡f{F·)Qî¸}b»hÞx ËGÓü¤Èçt–ƒòüqm ·/SyvãZû^ÜZ¡î+vÁ)§ '‚%Ù"ˆ7¡.ýÙ²¿Xºn>‘Í™3—qqþƒn¹0ä ÞÄûä0Xa¼Q„ØÄ;µ^o+E’ö”좺"î§f¢ûoNú1-5C ‘ ÃoÁ*=\‚†WXôÚäT‚ 8•òˆ¶r"^ކ=ñÇ$ÐÔü€ìÉViÉ`Õ:&aøQꥼN–Vû‡žˆºhÀ œ D}|î·¢Ö{æêmÌ|øÇ™wÊÆ¿Þv#Çv~¬}a–¹¿×ò\"Q|}Ö[–R$f Èè€Ã]âüÚÙÊà{ Âp£ˆ±V £‹ˆ £ëóýý«©FéÙóHÞ,A% [h‰Ï é²õ–½Ã*½ØÆ—TÉûÓ¯¨ãA•HB†* ‘ ƒvþcz¸D Ìž çÄ0X_¹"MùH¬ÆÃ£*"s¿‚å¤|îu¢'ιƲ'Ÿ}Ç'ø¥¾_ƒ¼»Ý) Ãjß5ÄÍõà\#7ªO}¿¶òRÿaå±åÓÒ\" m-×Q$á÷¥Öç†Ö9øÚÉçøˆ–ÚõšÞ7éв¼Ÿ6¢xŽùáýÎæv‚™B¿Ë Ã0 3µ„—¨)Ò—ÔþX:PøxG7l?Åz¼.½á,†¾òà¡~ÍÙíÀ:ñ1%Ͻ·7Þ’>yvø¢Fa‘W•¡$ÖˆÄÇ^õèØèõÔ Ðéé>ô0XuJè 4 š˜…ZÙpÛo7dv>j˜„¡Aš± ñµ½ "8¼óÃçÄu£œ!G5Ç&9܃¸„ÏõȽ‹ƒiù7Œóƒq,ÖxŸmˆãb·hì͹3ÉÙ Ä2,ýbü~û8_éœÉ{¸97ÈÐëÇÅS£Ï¢×&?C·ֹù[ "”1œÔÎ2,‹Çäô’Lsò8U czÝï3I½Cßâ†Þ ƒz;3sàüü-$O× C|À×s/{µa8|Y»4ëf"Þ7ìö Ô‹ÁUÌÐX×€’ßìÇg:¢àt]棫ù2=Ì£Þ $?u ÒCz¢‡ßJJcïd0x\}døeŒY¥w5ü=èvF2¡û™ûu0”sQ›ÞG®ëGý^Ùä6Ìõà8V‚õi,(wáõo $Ÿ+C%H´.Ƨµ ‚z)ÿ>C9BôšÞ7ÛûVÏQ>GÙ9c‰bðg S;A‘O”ße†a†¹ûX rGÑp7¾(±ëQoóŸLì_ç/eUú(›ï.£dñ:ièË.E¨SšqŒöë}{ú¨äA&.du÷K2ò4&Æ;UÄÙ$FceúÀE¦Ÿ=OYè?*™íO¿Ó'\(Ÿž_ÉžT“†œ ë2ŽIX>ÅOy5r¸ÈìÎÌð'r†ˆFÐÁN`ñ“êÜ]æý³Ã¨hP=å‹cÃ8b³,îAžÇöý—QúT-v…[ÓR²—±åƒqœÑ2æ.@ެñoÃë¸n+67_¾«£>ÑÎàI"{£=m¤PØ;ÿΦ# ˆ»ªWƒÉ® QŸ†‰ dÒå\^ =:ÎMž qÝKàFxzü­DUÿ–|œ¨… ʳ! ½EåŸcÖw6@ÎvÂ~—†af2X ¿¿sTÎÁ °rØ„ÙCoâg‹ÁXÏgúÈåHeŒÞ’GQÒŸ”û4n± §qíÀi`†`K•܇êõWÃ*¼xä©op;¶Ð"A‘ãÙ½õ%‹Dú”¸|…[ãµÙ½Tžê–œ3ES>Uዪ  ºÚÏGG1üÛï¼õL ¯3‚œ'^g Elîë® õîØƒx¶¡ _ÿ2²¨W×3Œ‚†7,ûÖÚ³L¿+3wBXôæ*Ïï÷‹Æâ*ûD‰ù3U†ÉÿÔðŽ»¹ú…Y/Í;°šBÿ½¡ýô¼0«#)˽u¿S¥—XY17F¾ÓGa¤ñœã¤Ï´#Ê0¯NïæBJc(wo6Ñ{c5噞[êUŸÓwÑÒÛ‹Áš*¹ÚC…è$WÞJâpï<—#d>K9Gn–â'9Vš(ožãïY×dËÕÐ’ü˜À]––f·gßXêiŸTýÊ–†œ ‡»&›ë-ƒá¾LzïÕ'b!½>,ï§SÏ* D­>âåsTÑEþÎãw6€ÛNPdå3•ße†a†™Ö‰ šX,q~ÞÁoðêüüA†¾Ó¼ ë!çþç¢êØù—1åcÓëޗȃæRÐõoÒûOÔ[ß·dsöûé/E‚øë5ªç˜5a£õ;› ý¾gå#(ì»Ì0 Ã0SK¨“a˜{ šûANlyçËlFE9”ž+d.Š˜ªû"½a»ÂòJi¤òJEaªôÊ$ÊésVx°É§~î9™Ï‘a†aîQÂ'~dæž ÐC÷˜ƒ Õ°ªÌ]$"¦ê~§¬žÊäœEs0ÿ¹25ßTé-”éRÎBáçÎ0 Ã0Ì]„#†a†a†a†‰Žd`†a†a†a&ØÉÀ0 Ã0 Ã0 Ã0L$°“a†a†a†a˜H`' ´De`}ð¢@K¶F?é`äLU9¿çzwup=‡(ôRëõ2žŒZ¾qoôᤉ*†a†a˜<ÈádXƒçÛ¶Ê=Z“ýùUrW äoìFåNž \ÏÅÁ¨·;ÌÅ{+¤“…¾G“sp–ÿ:-¿V¿´4a±Œp¯Þɲaï2<'·Z–ßòGQNŬkÁûŠª<¹ò±Ö'9ÚfëƒiŽ·Íak_åÑ.ò¶­¾m†a˜yE2,@=æ•"Ý'ê¶âÕ7– ùZè0È‚-[±4}ÉxÎ0d·.ÄØÁÇÑÓþ8R©…¨ß¶CŸ3aK•œÇU-§íÚY-¶Èé#X;ûtF>pä°ÆØŸÜ´×µó¡PÂó¹oã6T\OaT[9»=“‡ÜN¤€ë߈–zއÆÛæÎÛË©?cçkÆ¿ôaцʖýwfh+WÎEE²_ä•Û/]ض9ਈ¨œ3õÒR’6”žWÓøeü>¸X=×sqˆHïû)\M<4eN½Ÿ.™ÀçoÝÒGÅ#o½õbßK½¾æd¬ÆºÎ‹sçÄ–Äéÿè6,ùäbªêçnsÏ?÷iË&ÄW@·7öâ*œvKí"Ñ©\<œIßsb:í4†af:`]ÂrÁ–hmœ¥\ú>ÙwNÑõýÒホ{å)倸U>{­ éˆ1:ÉÛ½(‰ž»åaɶc¨ªN‰ P¿¦\õ¿À˜°‡¥!O’ÛÒG!oÿâ/oŽ(½.ä7É…‘.ŒñkíÛõ± ºö%<ðoÇ1¡Ž+bò$&ÎïUŽ 2ì·6¢DJ©,Á<ùÈôenO"æ-C^ùè{©²Ô溿ÜÒÏÁE®¼ˆ¥qÚ¿„O3Ïœ¢`ªN7a©ìÅ—Þw%¿-Á–·F½ŸçÉ¡h„UR` Ú¸¡žì&)ve’òÞ{ª3õ!/ûò5Aíâî”ßhµèµÊ5A‹ígþWÛʹks #ãåhšOG·ðÅß.c ¹ë屇<Êoâû —òjøÚ­Krâà˜ßÄõ¬˜nzéÿ9\ËzÆA(šnm¦7SýÖÀ'Ó¤:°ómñCŸ2ÔŸ@i£z¡GºJXB¿ Hîׯ;õ2?V*Ó#8)Œõ!ºvÖPSŠx_?’sª‘xPë^^‰-­ÕU¤Ó{V‚z[ö$€Þ ¡Wéë:/ϵìY&ò$Iv m˰¨û>þH Tþ :¼ýØ÷Êk>öTbdH”»†Ž<÷« –“pórËiËÇ$¿²vVËc}=Ø×~ÝZt¯î5þ{0•Ô¿?o.5ßûû¾²êÓ—¿ Doèûfx?½÷kâNŸ£óž¨vbÚóÝ÷Cí·JÐ0Áv‘)j¬N¨4Ô>)¿®ÛK Ã0Ì´ÀÉpñÐëØùîÂÀ 6µ@:ÇÁ`B›ÿ§ #Ÿä×›}ßÜrL\ÿJìч> ªçœzÞ3^ë(•ÞìíÖôQÉ1Tl5…óäµó0c<¸øøe‡óÑ’d/¡âúÑ̇´dÛKˆ§öŠûÑÞ|ùÁ¤ˆ‚2 d<ö¢&3ºMùˆrolÄíÛÅçÞKX>šæ'E>§•ó&´ ¡¶ïÐïH€xêÒçv`'°tÝ}"›3g.ãâüÝjaÈ/¼‰÷Éa° ˜¡±‰÷o½0fVŠ$í(ÙEuE†]OÍD÷ßœôcZj† "A†Ð‚Uz¸ ¯°èµÉ©<9p*å3˜låD¼ #*zâI ©ùÙ³«Ò’§uLÂ$¾z)¯“¥Õþ!/â4àN¢M¸ž5ÓL·1óágþ‡lüëmúÑÛ'ÀÚÖh™û}’çòp0‰:àë³Þ²”"1k@Fîç×ÎVßc†E ˆí8°Z]D\]ŸïïÇXM5JÏžGòf *iØBëL|nHï­·DèVéÅ6¾¤J†ÜŸ~EªD2Tiˆ´óÓÃ%aöd8'†„ÁúÊylÊGò`5UÑ™ûõ,'åûp¯=qÎ5–=ùì;>Ä/õýäÝíNYÈVû®!n®ç¹Q}êûµ•§ú+-Ÿ–æahk¹Ž" ¿/E°>7´ÎÁ×N>ÇG´Ô®×ô¾I‡–åý´ÅsÌMøqm ·/;Žs»ÈÌn\kß‹[+Tû*vÁi/1 Ã0Ó…ðá5e@ú’ÚK‡:Ô0‰Ž'„‰…ÂÀUí#¯9»X'>.äÉööÆ[ÒG"ÏçSÃ(,òª2”Äñ€øø)£~£ž>:=݇¦«N }es³P+?¤r[ãí–ËÎG “0| Có!6!¾¢£Tä‚ÂV?Qq Ÿê‘{ÓòoçãX¬´>ÛÇÅnÑø™s?f’³A‰e˜öÅøýöq¯Ò91’÷prnáÓ!ZàOyŒ ‹^›ü 9ZçbäohˆPÆpR;#ÈÐ*ÓK/ª4vÿÇ™S\ÏÅa’z‡¾Å ½õv¾.çÛÏÑÒIòt0Ä|=÷²X†Ã—µ ·n&â}ÃnÏðG½|PÅHu (ùÍ~|¦# N×Õa>J±Ú™/!Óì1ê@òS× ý8¤'zø­¤4öNöƒÇ•‘è³Jïjø{ÐíŒdB÷3÷ë`(ç¢7½\×ú}¶Ém˜ëÁq¬ëÓXQîÂëß@H>W†Jh]† OkA>õRþ}†r„è5½o¶÷3¬ž£|޲3ÊÅ@*þ¶Š©]¤È·:?œN™£Àšc“vÊ0 ÃLV'ƒœ¼Q4äâ/ŠFÝzÔÄ›B&s¬ÇŠúY@ízÝéñÖŠ}w²H?ß]FÉâuÒÀ•!Š P§4ãí×ûöôQɃL\0ÈÈÒ˜ïTyg“•é™~ö<}d¡ÿ¨rT8ÛŸ~§O¸P>=¾’=¨& 9Öe ’°|(Šnyó­‡bòþÙaT4¨ëűa±µ´ïAžÇöý—QúT-v…·¤™¡¡,ëq[>Ç-c¾GãnüÛðg[·›/ßu"´ó{’ÈÞhA)öNÆ¿³éˆâ®êÆ`²kBÔ§abÁ™t9—WBôc“çB\÷ĸ‘ž+QÕ¿%'jáBƒräãlˆBoQ¹Ãç(‡_ÎîÌ Ç ’³]¤Û+ªSf7®ì‚'Õ9†afZ`u2PüþÎQ9ƒlÐÉa¶¡Nȼ³å1¼‚Œr¤2FoÉ£(éOÊ}ÇW…Ó¸và4°FC°¥JîCõú«á^<òÔ7¸[è‘ H‚ñìÞú’E"}Jܾ­ñÚlo<•§º%çÌÉ”OÕFø¢*ÈÃO÷Bûáùè(†{œyÕC‘†zwìA<ÛP†Š¯ÿGÔËéFAÃŒ}kíé ¦ß•™;¡@,zs•ç÷ûEãi•}ÂÂü™*ÃdxjxÇÝ\ýâÞÒKãÿWS¾7ÄžÞÌÄêHÊÂõLL•^beÅ ÜùN…‘Æ7rN—z<ÓŒd(ü:½› a(=Œ¡Ü½ÙDï ŒÕ”gzn©W}þM{ÜEKo/kªäjY¢w’\y+‰Ã½sð\ŽùP,å¹YŠŸäXi¢¼yŽ¿g]“-WCKòcWtYZšÝž}cy¨§}Rõ(OX>r6îš@l®· †û2é ¼Wœˆ…<ôú°¼ŸN=«(µúˆC”ÏQEù;Ÿdû †Î·]¤Èʇ:q¼69jh_1 Ã0÷.Ö‰ šh+q~ÞÁoðêüüA†¾Ó„©àaì ìl?¨J“câG‚&tÂúÇõGIÊhvb=LBë‰ i²Â`z”QœÆ Rd€"3)£U.0N´’ÞvÎ[’/ ÉÇòóLühÌGç¿"íÞ¿Cõ ñÖ]¶÷aÀÿ¬ú ^_Ü•s¼4;¿M@Nç yP2§QDc»Õds4ÿÁ%TŒ ㌣z¾åds"-Í™Ðp3çøsÓÄ6½6¹ïÄ„¶rnt&¶$M&é)§O‡w¢<=é$ íÈ5,cºë¥g²z${ÒGB¦Ïš(”ëyÚéìj¥aF¹Wtq'~Å—i,-sOÔÄqúW+ÇÄ4&=8i"á8‘ÂÖŸk–ÃÈPÛ$'ƒ$ÔÄzr">šÄïìllù%ðÏW®#±§økR™ð‡¹»šôÊ 麀ñKKTúæÌ¼©&r$LyyËOçWר².²äc»_[9 o™2zó÷”Ñ&'|uª'<´ÕƒºÚ›‘rÞ=ÄTy/Ô?a*-ÿsQuìü˘ò±éuïKäAs)èú7éý'ê­ï[²9ûýô—‡"AüõÕsÌš°Ñ×&r ¶Q2´=“•@MúíÄMx'¥f†a¦¡N†aî1hî9±å/³YS¥7Šr(=—Û(ž?´zž"½aޤ»ÂòJi¤òJEaªôÊ$ÊésVx°É§~î9™Ï‘a†™6„OüÈ0Ì=d‡¦Èœ ½¹ U°ªÌ]‘$"~hõ Ëÿž¥€†ý®ÖZaHÐ6måZ8Ed±E‚õ憖ÎÄ©4Þ×Ç“ë¹8ÜSz»ÓèÀ\¼·òGZ0Yèû;9‡nù¯Øòkõ{HKË÷ê,ö.ÃsrK eùÝ)å4Q̺6¼¯¨Ê“+k}’¨m¶>˜æPÛ)«Vx;ÐÛF«oû;ââ3Ã0 3É+’aê1¯lé>qP·¯¾±É×:@‡~Fñå»;°ó5µ½sJ‹ìÀC[bìàãèi©Ô€‘Ä–>*91Ž«ZNÛµ³Zl‘ÓG¡vöéŒ|àÈa!=Œ±?¹i®7šúœ„çs߯m¨¸ž5žs9Ã)$É©?‹gþgüK Ñpß²ÿÎ >båʹ¨Hö‹¼Rb»ó¥üÚ6•³`XoÁÐ’Ž6CÞ“¦ñËø}pÑx®çâ0Íõ¾ß‘ÂÕÄCSæÄüé’ |þÖ-}T<òÖûQ/ö½Ô‹à¿«±®óâÜ9±%qú?ú„ K>¹˜ªú¹ÛÜóÏ}Ú² ñÐm«½¸ §V`;°öMT.Τï91ŒŠGÃÚà Ã0÷>Ö%,lÙ‰ÖÆYúÈ¥ïÇy@½)ÿ…ô»¯ãÃ^:¦H†­ÀÿuŽs@ÞßEIôØ-K¶CUuJü¸ŠœÖ” £þKi™~|InK…¼ýˆ¿¼ 8¢ôºGÚ$߇Ú¸Ö¾]› k_Âÿvê¸"&Obâü^eðSdÅÖF”H)•%˜g ™¾ ÃíIļe0æRN_zAÿQU/¶üÐsKã´ Ÿf S…tº K¥GžPöwƒŒ·ß–Ž`K‡Û ¡ÞÀóÔ°§h„UR` º±O=ÊMRìÊ$å1¼÷T9fêC ^öåk‚ ËÅÝ)¿ñhÑk•k("‚ÛÏü¯<¶•s׿FÆËÑ4ŸŽná‹¿]Æ@s-ÖËcy”ßëULF/åÕðµû Èy„c~'׳‚õæ§—~ëžÃµ¬w+E®ÍôfªßVødšTv¾->l9 Cý ôà6ú¨z¤«‰%ô˜@r¿6Þ©—ù±R™ÁIa¬ѵ³&€šRÄûú‘œSăê\÷òJli­†¬"Þû3ÔÛ²'ôN½JÇX×yy®eÏ2‘'I²óhh[†EÝçðñGZ  ò_ÐéäµèǾW®XóÙ°§#C¢Ü5tä¹_M°œ„›—[N[>&ù•µË°Z{èëÁ¾öëÖz {u¯ñ߃©<( þýykty¬ùøÞ‡Ü÷åUŸ¾ü!zCß7Ãûé½_wú÷Dµ‹ÓžvŽj¯Vâ@ #'Ø4åCí´uÀ •†Ú¥å×uûafšbu2H(jaÝþð6ð¼Œ^ðþ°šœ ޱ)û;ÛêƒlÜcÈàxª·—)ƒ6ã€@Æ@¶¥9_‰üšt2¸Œw¢çO¿;~Ç@FN†øFq›×QQ-Od#ÎCºhÃ]`þpœtï+ÒZ·)ïË{­%qŸærRúÜrÊœ©ó¯,ùç‚Þï;BÇëQÚùüáPú¨–ý¿ÐÆpÆ©@dÈ7ÜÄ–³÷ã½§îÇI¯|Ù·øãã8CÇßuòx.FDƒ_‚N>ãB:7pN2 ,z·ChyÈÁ€Sg…&»œ®3"èd ¦½Xïäô’ãi=<"9¯öyÞ=/Óý~ …õNR¯á7,'4çÍâ.ýûü­ÍiÔþÕ5˜høÁ|mè‘A¸iÖö}ú€0øfâs¯Ù|‡{ç`Ó’ ñ»w?áøñói®—ù•¶ºÆ¾“žŒ}‡ ^i𠹩×Y<–å’NºÌN:á,ŒY¯Kdç»LFC™¹_Ï5Ár’Aþ“QmÈ{ðæã½_›\]ã/ ‘«$”OðLc/çº<êŸ0é²åcºÞÁXfMöû–Àˆc°{îˤ×ö¾þëñýôÞo(Ÿc¸“!èL0µßæ|ܶæ¨v60 ÃLg‡KÔ”éKj,£AÓƒÛÝ¡Ÿ¦›ðú kô9 3?ÊG¾Ñ2ÁÙíÀ:ÔKÏ®×Àµ¤DžÞ¦†QXäUe(‰5â ú…·mtÆÝ톹NO÷¡‡iĪSB_À3Ýœ¡Zw,Þo7Uv>jÃÑì-[9)}ÿé@t†3LÂÿ¤¸„ÏõȽ‹ƒiù7Œóƒq,Ö<Ÿmˆãb·øÈϹ3oºõî4.Æï· øƒ#yw8sF‘ûSèeåå.¼þ „äse¨‰ÖeØð´äCP/åßg(Gˆ^Óûf{?Ãê9ÊçxñÐëÖa¡%Û^ ´Lí@Ev>ÊÁOíékŽ…¯`†™X rGѰ‰7¾(9ëQoóŸlê_çµsÂÂw—‡Q²x4ôårí<ÌP§4ãí×ûöôQɃL\XßšŒ| ‰ñN•q6‰ÑX™>p‘égÏÓGÈË­?Fr“Q ~(Š2øqm ¨& 9ÈK® S>y–“Ëß;ÙåÝâý³Ã¨hPQ‹cÃ8bkyÞƒ<;<Žíû/£ô©Zì oY2Ó„L$C!=Î “ rTŽþNÕmÅæF¸s}þ=ÍEKs‰Ï Š,s%èÍÛ‹~7õ c0Ù5!êóÎ'jœt9—W rôa“çB\÷Ä ¹_×åñ}"„¨êß’Ow»:¾Ð ù8¢Ð[Tîð9Êa·³;}Ñ ^r¶›ŸD:u'Ôn\;Ø)AOªs Ã0Ó«“¢öwŽÊ9d'ÕaõàfSgZÁ˜a‚Œ]¤2ÆoÉ£(éOÊ}Q…Ó¸và4°FϲkK•ÜÇ&ÄWÔbâúWúØÁ#O}ƒÛ±…î À0žÝ[_²H¤O‰ûÀW¸5^›í¦òT·äœI˜ò‘ör’ó¡:¡…;ðКZÔo[hÍß©¯»Š0Ô»câÙ†2T|ý?ªN½~ó…L&PÃŒ}kíù ¦ß•™;¡@,zs•ç÷ûEcb•}âÀü™*Ãäp¤ïî®~Áz ‡¿z¤?þ.¡÷3±:’²p=?4½ÄÊŠ¸1ò> #oœ¡ˆâ{ê§ ó2+;å@Jc(wo6Ñ{c5噞[êUŸÓwÑÒÛ‹Áš*¹ÚC…è$WÞJÊÐúçöTjÉ$°”säf)~’c¥‰òæ9þžuM¶¼•¦:22+º,-ÍnϾ±<ÔÓ>©ú”', 9wM 6×[Ã}™ôÞ« NÄBz}XÞO§žUˆZ}Ä!Ê稢‹üm²½JCçƒÛTdåCí1oçE Ú“ Ã0Ó‰Ð9hâ©Äùx¿Á«ó;äØzg¬½oÎ9ÿ€O>¦Çâ‡Bó8aýÎ\RF³õzæp&‚¬2¤'LùÉ=ãç™I­ru¢EKzÛ9oyJ¾0$Ê/0CV>âc9ŇONz©d'ÄwÍ3!¦"¿‘°÷Á3nØ7¦Ø5þ›€œDÍò dN#Á|ÆD/PBÅØ°çÜ@=Ðrò5‘ö°>dNÓÄ6½6¹ï¶rnôŽÛ¦qÚžrútx'Ž£t«ârhG®a¬wrzé]X=¢ÆÚ‘é=cï ®gÖ›·^oΘ܉GñegKËÜßO5¦[¨ äw•Ƥ'M$²ÆÂë1òr¾‚Ì<ž‰øh¿³³±å—À?_¹ŽÄ5' ™ð‡¹«ñì6½¶1þ4Þ7gæM5‘#aÊË[~:¿ºF•u‘%ÛýÚÊIxË”™0Л¿§Œ69á«S=§„­Ô½ÐÞ„ŒÔHÌRå$Lå‘÷R@ý¦òØòñ?ÿD‹¦|lzÝûyVëú7éý'ê­ï[²9ûýô—‡"AüõÕsÌšK!8y¶„ÚXÉÐö[V>rVÔ.vâ&ÜvÃ0Ìt%|âG†aî-äÊ3ч‘)?4½!P”Cé¹ÜÆé¤àç[¦Ho˜ë®°¼Ri‡<†RQ˜*½…2‰rÚ&;´É§~î9™Ï‘af>ñ#Ã0÷ d š"Ã臤7íИ²Ì™¨àç[¦ì½j(“s|ÍÁ@üçÊÔ|S¥·P¦K9 …Ÿ;Ã0 3Åp$Ã0 Ã0 Ã0 Ã0‘À‘ Ã0 Ã0 Ã0 ÃD;†a†a†a†‰v20 Ã0 Ã0 Ã0  ìd`¢–¨ ¬]hé¸Öè'ዜ©*'ë-EÒ»+¨ƒë¹8°Þ¡<Öëe<3´|ãÞ:4èÃIU> Ã0 9œ kð|ÛV¹Gkv?¿Jî „üݨ܉gê´HCëÿ¾.ÏíÆ«[êµÔ­1Üv õ´½ü¦†`K‰|â/k™Þj“+hmcG^µq“Òçs_FråãœO‡“]Îšß Ñ;Í( ¡Këá’Û\´•ká‘eL Ö[&£—–ÎÄ©4Þ×Ç“ë¹8°^Aw˜‹÷VþH & µ7&çÀ.ÿu[~­~ÿiiÂbá^½“eÃÞexNn ´,¿;墜&ŠY×&‚÷Uyråc­OrµÍÖÓcû°ðv¯·MZßöwÄÅça˜ïyE2,@=æ•"Ý'ê¶âÕ7– ùZèÐ 9ZËþv¾¶Cn8ԣϘ؇¶.ÄØÁÇÑÓþ8R©…¨ß¶CŸ3aK•œÇU-§íÚY-¶ÈéG²vöéŒ|àÈa!=Œ±?¹i®7æ6ê„çs߯m¨¸žÂ¨>Ã\Nâc±:í^\Ežå?õgñÌÿŒéâ!²[öß™áE¬\9É~‘WJlw¾´]Ûæ€£"¢r ë-襥m½ŸMã—ñûàâí\ÏÅõNŠ÷;R¸šxhÊœ¶?]2Ïߺ¥ŠGÞz?êž—zü·&c5Öu^œ;'¶$NÿGŸ°aÉ'SU?w›{þ¹O[líÃÛ½µo¢rñp&}ωaT<Öþgf:b]ÂR: gé#—¾OvàS´G½ ÿ…ô»¯ãÃ^瘜yšä ]”DÏÝò°dÛ1TU§Ä P¿¦\õ¿ÀXJÈôcDr[ú(äíÿ@üåmÀ¥×…<´&ù<Ô–ÀµöíúØ]ûø·ã˜PÇ1yç÷*ƒŸ"+¶6¢DJ©,Á<ùÈôenO"æ-ƒ1{9Õ½ë£^Uÿ•8☨Ç3m/biœö/áÓÌó§(˜*¤ÓMX*=Ô£ø2ó®dCFÔoKG°¥Ãý@SïØyjèR4Â*©@0†Ýø¥žÝ&)ve’òÞ{ª3õ!/ûò5AÞâî”߈³èµÊ5A g=|[9wmŽad¼Móéè¾øÛe 4×b½<öGùM°^ÅtÒKy5|í¾;ä´Â1¿ó‹ëYÁzïm½ôÛþ®e½ÓA(Zrm¦7S}Kà“iRØù¶øç€ õ'ЃCÚè£^è‘®$–ÐrÉýÚx§^æÇJe`'…±>D×ΚjJïëGrN5ªsÝË+±¥µ²Štzïg#¨·eOèz•ޱ®óò\Ëže"O’dçÑж ‹ºÏáã´@@å¿ ÓÉkÑ}¯\±æ³aO%F†D¹kèÈs¿š`9 7/·œ¶|Lò+k—aµ<öÐ׃}í×­õ@÷ê^ã¿SyP@ýûóÖèòXóñ½¹ïË!«>}ù Bô†¾o†÷Ó{¿&îô9:Òžvsû0Øî5åCíÒuÀ •†Ú¢å×u{˜a˜ï V'ƒ„¢Ö àoÃà@8(íÿßâ>íåôpú(²?v‚N&:^ÒοȈ–L”KHã0ãT 2änbËÙûñÞS÷ã¤W¾ì[üñƒqœ¡cï:y<#¢, 2'Kc[:7ðFj$[ôn9†Ðòƒ§Î Mv9]gDÐÉL{'°Þ饗^ëá1ÔÈiö8°ÏóÎ{™î÷[(¬wšé5üfç„æøYÜ¥¿v`h¤QûW×`¢áóµ¡Gá¦YØ÷éÂà›‰Ï½fó îƒMK&Äïü üD„ãÇÏc¤¹^æWÚêûNz2ö‚z¥Á7ä¤^gAðX–K:A<è2;é@†³0f½.‘ï2 Afæ~=×ËIùOFµ!ïÁ›÷~mru¿,D®zP> Ã2½<žëò¨¤˖ézc™5Ùï[#ŽÁî¹/“^Ûûvú¯CÆ÷Ó{¿A¢|ŽáN†`ûÐÜî%Ìù¸mëQíl`æûEøp‰š2 }Ií¥Ã?ð"m<Þ„²ój¨ÄÎOÒXúÔ|vf~¤Ž|£e‚³Û1€u¨—žNoïº%}$òìp/5ŒÂ"¯*CI¬\Ðç(Ük£3m·0öuzº=L#VúžÚæ„øQ®uǦ­ñvÛd磆IÍþA¶åRNrܘõ*J¶‰€IWÞ\ÂgzÈÌÅÁ´üÆùÁ8ëÏ6Äq±[|ôæÜ™ƒ7ÝÆkwã÷ÛÇEŠí‚Á‘¼‡;œ9#Œ¹ý)t Š2ž¢¡b£†³E¯M~† AéÜ0;ÌŒá¤h°g®Þ–‹ë-“ÓK!æ4–ýÐæVjYap=Ö›Cßâ†Þ ƒ g>§×þˆ–Na,%††âNjwøò„ü‹º™ˆ÷ »FãG½|PÅÀu (ùÍ~|¦# N×Õa>J±Zfr¾„L³Æ¨wÉO]ãþc‹‘J ¿•”C$NöƒÇi¨„Ø2Ž¥×ä`03’ ÝÏܯƒ¡œ‹jÜô~ÊÁOíékŽå7,—a˜i…ÕÉðL›úÐÇ_ýõ¨‰7‰¿!“/õ¥16ö…J!8Õ…¾x™5ýw—‡Q²x4ôåTí<ÌP§4ãí×ûöôQɃL\võ+2ò4&Æ;UÄÙ$FceúÀE¦Ÿ=OY ¯¯þq–›ŒbðCùôø ?®Õä€!çy•cAbÊÇVN9&î\'ü÷+‡RÌî D6Ü]Þ?;ŒŠU°86Œ#¶–Ø=ȳÃãØ¾ÿ2JŸªÅ®ð–Ä’‰d(¤ç—aîUÈ1;þmø»\·›/ßu:)tçÆ$ii.ñ´‘B‘r®½y{Ñï¦^a &»&D}ÞùD“.çòJ8A>lò\ˆëžX$÷ëº<>¢O„Uý[òénWÇ”# gCz‹Ê>Ç\íÜíÞæ'QNÝé¶×vŠFß“êÃ0߬N†Ûw`稜ƒA~ðSáûõ`$^ï®6±j j¢ÈØE*cü–<Úˆ’þ¤Ü§ö*œÆµ§5zÖY[ú¨ä>6!¾¢׿ÒÇyêÜŽ-tgÄ¥H‚ñìÞú’E"}Jܾ­ñÚlo-•§º%ç̺”öà‰ª 0Ý í[ó -ç°6¡îËAÖ?}@ ÎŽ»Š0Ô»câÙ†2T|ý?ªQJ½`ó…L&PÃŒ}kí ¦ß•™;¡@,zs•ç÷ûE…®²Oà—?3Pe˜,ôÝÝÕ/X/1Uzi<üê‘þLº„þ/0«#) ×3Áz£Æ¬—XY17F¾ÓGa¤ñ^)Ú-ÁH†2Ì ¬deEJc(wo6Ñ{c5噞[êUŸÓwÑÒÛ‹Áš*¹ÚC…è$WÞJÊÐúçöTjÉ$°”säf)~’c¥‰òæ9þžuM¶¼•¦:22+º,-ÍnϾ±<ÔÓ>©ú”', 9wM 6×[Ã}™ôÞ« NÄBz}XÞO§žUˆZ}Ä!Ê稢‹ü‹ù´Ýv¯"+êüòvÊQÄ­¡ýÌ0Ìô&tNšˆ)q~ÞÁoðêü½Z„kï›gì ìl?¨æeøU“žÐÆ;  šÀ Ówæ:2š½Ö3·€3d•!=aʇ(HîO&ÈLÊh• ¬-ZÒÛÎyË#Pò…!ù8P~y²ò×Ë)>™‰Çqõü0*f'Ñóÿ›çIë`žRa{üãh}clíPcø· ÈIÅœ!Jæ|4ÝÉÈhŒð%TŒ Ëq¿ Ô,'#iOëCædp0MühÓk“{ñNÔg+çFï8f·ì)§O‡w"5J·*.‡vä–Áz§—^zW¨1ïAdzÏx‚ë™õÞóz¾9rBp'~Å—i,-s¿jL·þR‡GÈw„Ƥ'M$²ÆÂë1òr¾‚Ì<ž‰øh¿³³±å—À?_¹ŽÄ5' ™ð‡¹«ñì6½¶1þ4Þ7gæM5‘#aÊË[~:¿ºF•u‘%ÛýÚÊIxË”™0Л¿§Œ69á«S=§„­Ô½ÐÞ„ŒÔHÌRå$Lå‘÷R@ý¦òØòñ?ÿD‹¦|lzÝûyVëú7éý'ê­ï[²9ûýô—‡"AüõÕsÌšKÁ×–t öa2´½š•€œµ‹¸‰°6&Ã0Ó•ð‰†¹·+gÌDwöHa½SE9”žËm$N ~¾Åá¦7ÌqvWX^)´CC©(L•ÞB™D9m“ÚäS?÷œL‹çÈ0Ì÷ v20Ì4AŽÕŸ¯–Ž+¶¡Àzï ¢\=ÀŸoqøÁ½ÏhfzÂÆé÷~Ž Ãv20 Ã0 Ã0 Ã0  áKX2 Ã0 Ã0 Ã0 Ãä ;†a†a†a†‰v20 Ã0 Ã0 Ã0  ìd`¢–¨ ¬§\hr±Ö2<«ïY¦ªœ¬·8|ÏõÒ„“>\ÏÅõ‡(ôRëõ2žŒZ¾qoôᤉ*†a殑ÃɰÏ·m•{´†õó«ä®@Èߨ-ŒÊx¦N‹¤‘I2Ϧ¯µBkî¶C=m/¿©…!ØÒG"ß„øËZ¦·‡šÃä ZëבWmÜ$$ôùÜ—‘\ù8çßÓÇád—S`­ÿxHÊÿޏ\·|S@ÃÖ‡?$·¹h+×Â)"˨+¬·8L'½´t&NÝÙŠ\ÏÅõ£Þî4:0ï­ü‘Lj_MÎa_þë¶üZ}ïhÕ€bá^½“eÃÞexNn ´,¿;墜&ŠY×&‚÷Uyråc­OrµÍÖÓœæ÷2mg«½G;ßÛÿ^´­&òŠdX€zÌ+EºOÔmÅ«o,AòµÐa†SÆÎ×v¸Û'—€ô€>iB²[bìàãèi©ÔBÔoÛ¡Ï™°¥JNŒãª–Óví¬[äô£Q;ûtF>pä°ÆØŸÜ´×]£¾ Âó¹oã6T\OaT‡a.§¥¤ã!ñö£yåA>ÿ?ã_ú°hˆ†Ý––ô[¹r.*’ý"¯”Øî|©·¶ÍGEDå,Ö[¦±^ZâÐfÑÿEÓøeü>¸¾×sq`½Å!"½ïw¤p5ñД9©ºdŸ¿Uü%CóÖûQ/ö–K$c5Öu^œ;'¶$NÿGŸ°aÉ'SU?w›{þ¹O[6!¾ºí¼Wá´Ã lç‹vuåâáLúžèxÔ±;æû‹u Ë[v¢µq–>réûdÞ9E{ämÿ/¤ß}öÊSêñ E1ü_Ó9 y%Ñs`·<,Ùv UÕ)ñÏ'®^S.Œú_`,¥ dúç$¹-}òö þò6àˆÒëBK“\é¿־]› k_Âÿvê¸"&Obâü^eð“a¿µ%RJe æÈG¦/Ãp{1oŒùXÊi«ÿL:º®·ôs°CÏúE,Óþ%|šq4PLÒé&,•ÛQ|i|WdÌü¶tÄ·¦:õ§†E#¬’ cèÐAêam’bW&)á½§Ê1SbðrεÚÉÐZÜòS½V¹†""Èa±ýÌÿÊc[9wmŽad¼MóéH­c?ÐLkÚËÓ.y”ßëU°ÞÜz)¯†¯ÝwÖœe8æwºq=+X/ë•ôÒ·ì9\Ëú_ BÑ¡k3½™êÛ ŸL“êÀηEÃ%d¨?ÒFõBt• ±„ZHîׯ;õ2?V*Ó#8)Œõ!ºvÖPSŠx_?’sª‘xPë^^‰-­ÕU¤Ó{?“A½-{@ï„ЫtŒu—çZö,y’$;†¶eXÔ}¤*ÿN^‹~ì{åŠ5Ÿ {*12$Ê]CGžûÕËI¸y¹å´åc’_Y» «å±‡¾ìk¿n­ºW÷ÿ=˜Êƒêߟ·F—Çšï}È}_YõéË_¢7ô}3¼ŸÞû5q§ÏÑyO”Ý“ö´cý=R‰º£Î!ØÎ7åCíéuÀ •†ÚÛå×uûŸa¾ÇX ŠZX7€?¼ zÉѶƒ‰œuû<ÿk^¦ûý ëe½yé5|£râk3¿¥ù!Ú¿º ?˜¯ =27ÍÀ¾OßL|î50›oàpïlZ2!¾k7ðaŽ?‘æz™_i«kì;éÉØwê•ßkzÁcY.éñ Ëì¤Θõ¸Dv¾Ëd4˜™ûõ\,'ä?Õ†¼o>ÞûµÉÕ5þ²¹êABù4 Ë4öòx®Ë£þ “.[>¦ëŒeÖd¿o Œ8»ç¾LzmïÛé¿ßOïý‰ò9†;‚ÎSû\aÎǵ%Fµ³a¾ï„—¨)Ò—ÔþX:Ï^=žiy}çs{Þ…™Ú#ßh™àìv `ê¥çÏ1z KúHäÙáOj…E^U†’X#¸ ÏQøÓFg\Öna¬ëôtz˜F¬:%ô<—Í ñ#UëŽÕZãíÆÈÎG “8šýeË'´œ¶ú‰ŠKøìPÜ»8˜–Ã8?Çb=ðÙ†8.v‹Àœû1sð¦Û˜ëNãbü~û8AÑÀ[08’÷p‡3g„Qµ?…ŽAQÆS4TBlÔ´èµÉÏA&fƒ™1œ XâÌÕÛòoq`½Åazé¥PoS~hs +µ¬0¸ž‹ë-“Ô;ô-nèÝ0ÈÉÌ_õóG´t’c)140GpR¸Ã—'ä_ÔÍD¼oØ5?êÅàƒ*æo¬k@Éoöã3Qpº®óQŠÕÂ0“ó%dz˜5F½H~ê÷[ŒTbø­¤"q²U‹]á-†¹'ÉD2ÒË0ŒrDþ?T·›/ßõÌau´4—ø ÚH¡È9W‚Þ¼½èwS¯0“]¢>ï|¢ÆI—sy%œ V6y.ÄuO,’ûu]Ñ'Bˆªþ-ùt·«ã ʳ! ½E埣N<»Ó­à%g;¿ùIT Sw2îÆµƒ¢‘û¤:Ç0ßc¬N†Ûw`稜ƒA~SyLì§£NÔÇ!±‹TÆø-y´%ýI¹OC#ªpלÖèYXm飒ûØ„øŠZL\ÿJ;xä©op;¶Ð!–" Ƴ{ëK‰ô)qø ·Æk³½—Tžê–œ3ÍR>r؃'ª‚<¢t/´oÍÇVμê¡ÈC½;ö žm(CÅ×ÿ£iÔ+4_Èd5¼aÁØ·Öž‘`ú]™¹ Ä¢7Wy~¿_|lVÙ'ÒËŸ¨2LFúîî꬗ø¡é¥qé«Gú3áàúÄL¬Ž¤,\Ïëš{K/±²bnŒ|§ÂHã9ÔTµ›ü”až³rW.„¡ô0†r÷f½70VSžé¹¥^õù7íq-½½¬©’«=dQˆÞIrå­¤ ­nO¥–LK9Gn–â'9Vš(ožãïY×dËKPiª##¸¢ËÒÒìöìËC=퓪ÿ@yÂòѳáp×bs½e0Ü—Ioà½ÚàD,ä¡×‡åýtêYE¨ÕG¢|Ž*ºÈß™*ír0:ÿÜv¾"+êìóvBR„±Á^`˜ï¡Ã%jÊ܉Çœar¼ …ø­G faé¯6lùØÊ™«(J"¦ò ]žÇû><‚µù,]Âɯg`ýªèþo¦J†Î¾¤µªå%›¸Ïü$[€¸:OaÞ"ýÉA}LãfO©| Å¢7¬<ähè~ø¡L×XNuÊÊ‘$Ðô”ºÎ»§ Û}¥sÔq¬—õ1é¥Ó¤ÄɯEúf·|×3ë Âz]6&„Áæ|¿lôÄg)ý½|C|/{¼‘ 'ði'Tۊο >Ú!4¬­ÆøÙ<{‡ÿsÿì*É„¯Ëqñ9z–?>>!Ãée¸»Øœ% Ò+ ±ðt}&|>OÇ ©89T-®UË)š­œ§_9‡ñ%‹³î˦A²MuCYÃ7²åÀg]pë(léFQÿŸ÷9ùˆ¥×d°•g2õo*-§>i£÷Á;ôÀ”Q¯ï¾êpÁ¡aÓk¢…¿ŸQ>Ç,äª1 Ö˜i+«eã lŸ§~‡+çËÝ<2íp†ù~>ñ#Ã0÷r匙èv&¶,¬·8L•Þ(Ê¡ô\!s?ßâÀz‹9ìV˜vw…啨òKàP±CЧJo¡L¢œ¶ÉÃ&A,:üÜs2-ž#Ã|Ïa'ÃLä˜ùùj)µb7œYïÝgªôæC”³ø;ðó-¬·HЪ!+1Ó6N¿ðsd˜©‡ Ã0 Ã0 Ã0 ÃDBø– Ã0 Ã0 Ã0 Ã0yÂN†a†a†a†a" Ã0 Ã0 Ã0 ÃD;˜hXõÿºÀÅ‚&Ûj-óúðžeªÊÉz‹ë½+Є“>\ÏÅõ‡é¬—ò,ßÉxºÎÒ›wDTù0 ÑN†5x¾m«ÜûÙ »ñü*¹+r¹¾óN§°ûš§þ,žûŸñ/}X4DCgKKë­\9É~‘WJlw¾ôYÛæ€£"¢r ë-¬·wª7’hIDAT`h©A›BÿMã—ñûà:c\ÏÅõ‡i®÷ýŽ®&š2§üO—Làó·Š¿dhÞz?êÅ>Ãr‰d¬ÆºÎ‹sçÄ–Äéÿè6,ùäbªêçnsÏ?÷iË&ÄW@Û{qŽÝQ ]Sû&*gÒ÷œFÅ£aöó}ź„%E%´6ÎÒG.}ŸìÀ;§h¼Ïÿ…ô»¯ãÃ^çx= ÏSäÃOÓÁõÐÉlÈ[¶(‰ž»åaɶc¨ªN‰—¨_S.Œß_`,¥ yzYInK…¼ýˆ¿¼ 8¢ôºÏ$߇Ú¸Ö¾]› k_Âÿv xu\“'1q~¯rLPdÅÖF”H)•%˜g ™¾ ÃíIļe0æc+¿MîÔ‰>0–ÇK=ži{Kã´ Ÿf S…tº K¥s_fÞ•lȨøméˆoqê=‘kóS4Â*©@0†Ý8¢žÎ&)ve’òÞ{ª3õ!/ç\»œ žÅÝ)¿QcÑk•k("‚ÛÏü¯<¶•s׿FÆËÑ4ŸŽÔºîÍ´Æ»<í’GùM°^ë½wõR^ _»ÿ+ä¤Ã1¿³ëYÁzY¯äÑKßîçp-ë8µ ×fz3U[>™&Õo‹†ZÈP=8¤>ê…é*Ab µ€&ܯwêe~¬T¦FpRëCtí¬  ¦ñ¾~$çT#ñ :×½¼[Z«!«H§÷6 ‚z[ö$€Þ ¡Wéë:/ϵìY&ò$Iv m˰¨û>þH Tþ :¼ýØ÷Êk>öTbdH”»†Ž<÷« –“pórËiËÇ$¿²vVËc}=Ø×~ÝZt¯î5þ{0•Ô¿?o.5ßûû¾²êÓ—¿ Doèûfx?½÷kâNŸ£óž(;:‡Í„dUâ€î@uÚ5¦|È>ZœPiȦ(¿®íæ…ÕÉ ¡¨…uøÃÛÀó2zÁû" „kpºÎ3îË ùÂÆS¸½¸LÌ2†¼-ýÈùòHäפ“Áu`¼=úØñ;2r2è7Šðë¨9E8»ú“.šþ£>‡Fö?ZÀYA÷¾"­u›òñþƒ{¯µåsº°û pÙ¡÷ÁûŽ(§Si§r4É¡²ÿÚxÈ8è€ ù†›Ørö~¼÷Ôý8é•/ûü`gèXà»NÏňh IÅÉÇÒ“ÎD üQ½[Ž!´<ä`À©€³B“]N×t²ÓÞ ¬—õÞËzÉÁ·Ã…œ„û<ÿã^¦ûý ëe½÷´^Ã79'4‡Óâ.ݶòCµu &~0_zdnš5€}Ÿ> ¾™øÜk`6ßÀáÞ9Ø´dB|Çoà' ?~#Íõ2¿ÒVרwÒ“±ïÔ+ ¾!× õ: ‚Dz\Ò âA—ÙI2œ…1ë5p‰ì|—Éh203÷ë¹&XN2È2ª yÞ|¼÷k“«küe!rÕƒ„òi–iìåñ\—Gý&]¶|L×;ˬÉ~ßq vÏ}™ôÚÞ·Ó2¾ŸÞû ås w2 f»†0çãÚ£ÚÙÀüð.QS¤/©ý±tŽ€r0Ôõü;_ë~¾¯n?Ú¡,̼ÄG¾Ñ2ÁÙíÀ:ÔKO˜·Ý’>yv8Fa‘W•¡$Öˆ.ès´Ñ§´[û:=݇¦«N }O^sBüÓÖºc—ÖxÝúÙù¨aG³ÿa­ùx_rè˜ËS(—ð™Žd¹8˜–Ã8?Çb= îÙ†8.v‹Å9÷cæàM·qÓÆÅøýöqs¢Á³`p$ïágÎãf ƒ¢Œ§h¨„بaeÑk“Ÿ!ÃH:7Ì3c8)tÄ™«·åßâÀz‹ëÍ ¹¦±Ý‡6ǰRË ƒë¹8°Þâ0Íô}‹z7 2D2svýü-$ÂXJ  ÅœÔîðå ùu3ïvÆz1ø ŠqëPò›ýøLGœ®«Ã|”bµ0Ìä| ™fQï’ŸºÆýÇ#•~+)‡HœìÓP ±eJ¯ÉÁ`f$ºŸ¹_C9Õ¸éýxäº~TûÊ&·a®r€¨¹'üõi,(wáõo $Ÿ+C%H´.Ƨµ ‚z)ÿ>C9BôšÞ7ÛûVÏQ>Ç‹‡^·s.ÙöRÀÖ0Ù5Šì|”ƒ!žÚ+ÒÖ›ä°qfºcu2<Ó¦>ñÆÅGa=jâMâoÈä<«Öc)¾ÐÃ#Nàw¿×[Ówy%‹×IC_¾Äµó0CÒŒc´\ïÛÓG%2q!0†@“‘¤11Þ©ò Î&1+Ó.2ýìyúÈyõ?¯Ü ”OϯðãÚPMrWQ9$yæc"#—c©àÎÕpœþnðþÙaT4¨¨‚űa±}©ïAžÇöý—QúT-v…‰†ñ‰d(¤'”a˜{r¼þ¿+çì¾|×™³Kw^M’–æŸA)Y çJЛ·ýnêÆ`²kBÔçOÔ8ér.¯„ÜêÃ&Ï…¸î‰%@r¿®Ëã#úDQÕ¿%Ÿîvu|¡A9òq6D¡·¨Üás”C¦gwZ#˜sÚ5ÍO¢ºSu7®ìú'Õ9æ…ÕÉðaûìï•Ãä!Õ>±__cñ2שPS†xXôåHeŒô’GQÒŸ”ûÔ“^…Ó¸và4°FÏJjK•ÜÇ&ÄWÔbâúWúØÁ#O}ƒÛ±…I0žÝ[_²H¤O‰ûÀW¸5^›íÍ£òT·äœy•ò‘Ã3<Ñä!¤{¡ýüòÉã¾$Ãz؇’ a¨wÇij e¨øúT£…zIæ ™L †7,ûÖÚSL¿+3wBXôæ*Ïï÷‹Š[eŸÐ.f Ê0™é»»«_°^‚õFY/_=ÒŸ Ë–Ðïfbu$eáz&XoÔ°^‡•3pcä;}FßÈ¡µõx¦%ÉP†y¹V#s†ÒÃÊÝ›MôÞÀXMy¦ç–zÕçß´Ç]´ôöb°¦J®öE!z'É•·’2´þ¹=•Z2 ,å¹YŠŸäXi¢¼yŽ¿g]“-/A¥©ŽŒLàŠ.KK³Û³o,õ´Oªþå ËGCΆÃ]ˆÍõ–Áp_&½÷jƒ±‡^–÷Ó©g¢Vqˆò9ªè"ç±´¿ÈÁ`>­qíEV>Ô ëít¥Èoƒ}Ä|ÿ .QSæNü8æ ›ãç(äm=j0 K%öi™ËÞƒø ³ k3ápÀ§íõ5&vãM¨Ãò3^³æ÷ÔDÒƒFi†Q±• –ô¶| –Óx#%«oSa>Ê g“‹|~…øV}n ô?¥7½wžp·¿”9§TºG÷%·åc#Ÿ|ò¸¯Ôï0Üï »Ø&ŽsE2x߇GÔó×ËžN†“_ÏÀúU3Ðýß*l“ Ž}I!kUËKþ6qžùH¶qužÂ­Eú“ƒú˜Æ‘žSùŠEoXyÈÑÐýðC™Ÿ±œê”•#I é)uw NÆúJç¨ã0X/ë r/é¥Ó¤ÄɯEúf·|×3ë ÂzS¥—ؘ›ó½¶!Ú‡Ÿ¥tûà Ñ>èñF2œÀ§PmI:ÿBتd@ÃÚjŒŸÍ³wø?WðÏ®’LøºŸ£gùãã2œ^†»‹ÍY²° ½ O×gÂçótЊ“CÕâZµœb¡ùØÊyú•s_²8뾜a$ÛT7”5|#[|Ö·ŽÂ–nõÿyŸ“xQzÝH[y&Sÿ¦òØòqê“6z¼CLùõúî«<6½&ZPøûåsÌBF2Ç€X£¶h£eò ´G„qå|¹›GÆ>b~h„OüÈ0̽…\9c&º‰-‹ë-?4½!P”Cé¹Bæ8)~¾Åõ‡)ÒKŽÂÕ#fGá]ay%¶ü8Tìô©Ò[(“(§m²Ã°I‹?÷œL‹çÈüà`'ÃLäØõùji±b7$YïÝ燦7¢œMߟoq`½ÅaªôæZ¹‰™°qúý€Ÿ#s/ÂN†a†a†a†a"!| K†a†a†a†a˜¹˜ªú¹ÛÜóÏ}Ú² ñÐvÖ^\…cghÇÕ¾‰ÊÅÙô='†Qñh˜=ûÃÁº„å‚-;ÑÚ8K¹ô}²=òÆþÒホ{Å!E1,îÂηOÐIåtø?ÀÚªã ä=Z”DÏÝò°dÛ1TU§ÄÃê×” £þKiCžÉm飷ÿñ—·G”^òh™ä;ðP[×Ú·ëctíKxàߎcBWÄäILœß«Y±µ%RJe æÈG¦/Ãp{1olùÓ«òß:_ŽŠÅT r¤è{´åo¤Ï´½ˆ¥qÚ¿„O3ŽŠ‚©B:Ý„¥Ò£7Š/wÅ5î[:â[s›zäù°J*Œ¡C7¨Ç±IŠ]™¤<†÷ž*ÇL}ˆÁË9×ò&ÃcqwÊo\XôZ劈 ‡Åö3ÿ+måܵ9†‘ñr4ͧ#µÎù@3­y.O»äQ~¬WÁzY¯Ä£—òjøÚýu ç ŽùŒ\Ï ÖËz%?p½ÔVyײ~;‚PôïÚLo¦jÁ'Ó¤:Üvsd¨?ÒFõBt• ±„Z|Hîׯ;õ2?V*Ó#8)Œõ!ºvÖPSŠx_?’sª‘xPë^^‰-­ÕU¤Ó{›AA½-{@ï„ЫtŒu—çZö,y’$;†¶eXÔ}¤*ÿN^‹~ì{åŠ5Ÿ {*12$Ê]CGžûÕËI¸y¹å´åc’_Y» «å±‡¾ìk¿n­ºW÷ÿ=˜Êƒêߟ·F—Çšï}È}_YõéË_¢7ô}3¼ŸÞû5q§ÏÑyO”]›öØ)~ÈÞ¬ÄÝaì´ãLù=µ8¡Ò]Y~]Ûw?p¬‘ ½Žï~!ŒNñ(£èGÒq0X(«Ò;âv]“û2¸on9&®%öèÒéT½æg·Ãõí@©ôm·¦J®ˆ¡b«)<Æ ¯‡ãiÄÅË”C/É^BÅõ£™³dÛKˆ§ö*OyÇä H‘eÈxÀ„ÙžÑmÊG”{c#nŸØ.þ}¼Øò±¥'jÅ3: Ó‹:ߘ+½‰|ØNÑ+È"Þ„ºô_TdK'°tÝ}"›3g.ãâüÝEaÈ/¼‰÷Éa° ¢q@bïßzÑ8X)’´ dÕv=5ÝsÒi©j0Ð jX,X¥‡KÐð ‹^›œÊCƒ§R¾ˆ­œˆ—£aDEOü1 45? {JTZji“hX¬WÃzYo@/åu²´Ú?”Jüo7àN¢˜¸ž5¬—õ²^œ¹z3þqæ›oã_o«¨^¹}¬}a–¹íiy.‘¨¾>ë-K)³dtÀá.q~íleð=a¸QĀ؎«…ÑEÄ…Ñõùþ~ŒÕT£ôìy$o– ’†-´ÎÄç†ôÙzK„Þa•^lãKªdÈýéWÔñ J$!C•†HA;ÿ1=\"fO†sbH¬¯\‘Ǧ|$VãáQ ‘¹_ÁrR¾÷:Ñç\cٓϾãHüR߯AÞÝî”…Œaµïâæzp®‘Õ§¾_[y ©ÿ°òØòii.†¶–ë(’ðûRësCë|íäs|DKízMï›thYÞOQ<ÇüØ„ׯpû²ã0ÛqfvãZû^ÜZ¡ìÁØǾc‡KÔ”éKj, êÏø4Ý”™“!‘þBüLçb¡0ô•‡hàÈ7Z&8»X'y†¼½è–ô‘ȳÃcÔ0 ‹¼ª %±F< ^&¿‘NÐ §ÓÓ}èa±ê”Ðxñš˜%Œ}å¨Û¯›;;5ŒÁðÂ[ò±¦—xÊs6‰ÑXYŽô…r Ÿê‘{ÓòoçãX¬ˆ=ÛÇÅnñc2ç~Ì$gƒ˰NjñûíãȤsb$ïáäÜ †D‡ø¢]<åiTXôÚägÈùÐ:# DC„2†“ÚA —âÁz‹ë-“ÓK±>äq×sq`½ÅõæÅз¸¡wàÞÎÌe?DK'ÉÓuÂðõÜË^`m_Ö]Bu3ïv{†?êÅàƒ*¦s¬k@Éoöã3Qpº®óQŠÕÎ| ™fQï’ŸºéÇ!=ÑÃo%¥±w²<.Œ>2ü2Ƭһþt;#™ÐýÌý:ʹ¨ÆMïÇ#×õ£Ú“6¹ s=8Ž•`}Ë#Ê]xýÉçÊP ­Ë°ái-ȇ ^Ê¿ÏP޽¦÷Íö~†Õs”ÏQvž[¢¨Øoû˜ì8Ev>Ôiít"Ö›ä0ùïV'Ã3mê‡1Þø¢ø‘\š89Â'«ñznß,C<= ÏdóÝåa”,^' }ùP)2@ÒŒc´\ïÛÓG%2qÁ76"CF>ÆÄx§ÊƒÐFz™~ö<}d¡ÿ¨rT8ÛŸ~§O¸P>=¾’ž6T“†œ ë2ŽIV>§ÃÓ{õp­ýÖôÞÉ.ïïŸFEƒêY\ÆÛ—ëäÙáqlߥOÕbWø—‰a˜{"µ—±åƒqœÑ2†a˜P¨£aüÛðߌº­ØÜ|ù®n¢;ë&‰ìö´‘Baïdü;›Ž( îª^a &»&D}&,I—sy%h°p6y.ÄuO,9àéñ·Uý[òq¢.4(G@>Ά(ô•;|ŽrØüìÎÌ0ú 9í¸æ'QNÝi»×v #æIuîŽÕÉ@!ðû;Gå òR›0{€²Yƒç^†/†„‘QŽTÆH/y´%ýI¹OãbªpלYéa¶ôQÉ}lB|E­VáÅ#O}ƒÛ±…î Š$Ïî­/Y$Ò§Ä}à+ܯÍönQyª[rÎDJùTm„/ª‚r6îš@l®· †û2é ¼Wœˆ…<ôú°¼ŸN=«(µúˆC”ÏQEù;Ë¥½IC箃kÇ)²ò¡Ngo'3EºìÁ"¡Ã%jÊ܉Çœa™å+×£³°ôWb_.WIêÐ0ï„VvãÍ {Ì=^¤æ÷ÔDÒ£Di†Q±• –ô¶| –;ãohSa/™9Œr‘ÏÁ¯wæjXý’zÓÓœNz2øâöâ—2ç”ÃÊC÷è^£ä¶|lØò Ã^Q[û•®‡Bñ¾`-íç³|©…“_ÏÀúU3Ðýß*Œ‘þû’BFs%ˆí·‰ÛèÐ=Î’“ Wç)ìY¤?9¨iâÈs2X±è +9º~(Ó2–S²r$ 4=¥®óŽWa tŽ:ƒõ²Þ ¬WE0˜&}$N~-Ò7»å#¸žYoÖ«ø¡é%6&„Áæ´OlôÄg)ÝzC´‡z¼‘ 'ð)ÍQEmg:ÿ‚}®*¢am5ÆÏæÙ;üŸ+øgWI&|}Ó’ œÌѳü1[oÕáîbs–,,H¯ÀY¢2>Ÿ§ã€†Tœªתå ÍÇVÎÓ¯Ð< ‹³î˦A²MuCYÃ7²åÀg4„SGaK7Šúÿ¼Ïɇ&†t#lå™Lý›ÊcËÇ]:T½Þ¡¦|Œz}÷U‡ ž ›^-(üýŒò9fQK«BÄ€XcÆRËöh¥~‡+çËÝ<2ö c]]‚a˜{šûANlyçËlë-¬wÊ¡(‡Òs…Ì­Rü|‹ë-?0½ä \=bvPÞ–WbË/CÅAŸ*½…2‰rzWµðb“O üÜs2-ž#ÃN†™.È1äóÕR[ÅnX±Þ»ë½wÈ,Ÿ«£€Ÿoq`½ÅᇦW.]ÝpsÒ«\0÷lœ~?àç8=`'Ã0 Ã0 Ã0 Ã0‘¾„%Ã0 Ã0 Ã0 Ã0Lž°“a†a†a†a˜H`'Ã0 Ã0 Ã0 Ã0‘ÀN&VýÁõg‹MÆÔZ†gõá=ËT•“õÖ[¾çziÂIŸ®çâÀz‹ë-Qè¥<Ëw2ž®ƒ³ôæU> s‘ÃɰÏ·m•{?{a7ž_%wµA©×øÕç%u[ñªIn£öMT9늾ü¦†`K‰|â/k™Þj“+îÛø÷Œ¼jã&! ¤Ï羌äÊÇ9Okº†1‰òú\îe øP:ëij‹¶r-œ"²Œ"Áz‹ë-ÓI/-‰Sw¶¢×sq`½Åõ£Þî4:0ï­ü‘LaGL²ªü× lùµj¿ÑªÅ2½z'ˆ½ËðœÜhY~wÊE9M³®Mï+ªòäÊÇZŸäj›­¦9ÍïìÁÂíD¯í[ßöwÄEóÅD^‘ Pye£H÷ÑQ=ži>}mv¾ö|‰&¼º¥^ÈÅɯêÑû.Éw`O=^a ]`aÚºcGOûãH¥¢~Û}΄-}TrbWµœ¶kgµØ"§J®}:#8rXHcìOnÚëÚùP(áùÜ·q*®§0ªíZžBŸ‹æÔŸÅsÿ3þ¥‹†øn‰`©¹•+ç¢"Ù/òJ‰íΗÆjÛpTDT΂a½Åõ‡i¬—–Þ³5Øé÷§iü2~\w‹ë¹8°ÞâÀz‹CDzßïHájâ¡)ëtùé’ |þVñ— Í[ïG½ØgX.‘ŒÕX×yqîœØ’8ý}†%Ÿ\LUýÜmîùç>mÙ„ø h{p/®Â± ´kßDåâáLúžèxÔl'Z—°\°e'Zgé#—¾OvàSú@@é6ã þ0¸¯/îÂηOH9E>¬­½„Om†'yS%Ñs`·<,Ùv UÕ)QX ~M¹0ê±”6äéfHnK…¼ýˆ¿¼ 8¢ôº‡Ç$Æx[×Ú·ëctíKxàߎcBWÄäILœß«9°µ%RJe æÈG¦/Ãp{1o&•O ½í¹Xï³Ï´½ˆ¥qÚ÷>oŠ‚©B:Ý„¥ÒÃ5Š/ß}öÊ“YP#û·¥#¾5¨É».ת§h„UR` úãI=MRìÊ$å1¼÷T9fêC ^ι¶5‹»SþF¾E¯U®¡ˆrXl?ó¿òØVÎ]›c/GÓ|:Rë~4Óàò´Kå7Áz¬—õJî½”WÃ×îoƒ9%qÌïÜäzV°^Ö+a½yµ^j›=‡kY¿YAT›_è¶ |2Mª#c'„A†úèÁ!môQ/ôHW K¨Å:ä~m¼S/óc¥2 0‚“ÂX¢kgM5¥ˆ÷õ#9§‰Õ¹î啨ÒZ YE:½·ÙÔÛ²'ôN½JÇX×yy®eÏ2‘'I²óhh[†EÝçðñGZ  ò_ÐéäµèǾW®XóÙ°§#C¢Ü5tä¹_M°œ„›—[N[>&ù•µË°Z{èëÁ¾öëÖz {u¯ñ߃©<( þýykty¬ùøÞ‡Ü÷åUŸ¾ü!zCß7Ãûé½_wú÷DÙñi«Nöu%èr‡ ÝjʇìßuÀ •†ìÄòëÚž `u2HhøÃºüámàù7– ™UPe`–ÞOçkgÃ!HY]Ïi,3\£poò†â©NÜ^\¦ 挡‹Œ!oK?r¾<ù5édpïDÏŸ~'vüŽŒœ ôâ¸ÞˆŠjy"ãq€tÑôõìÙ"ଠ{_‘ÖºMùx_ïµ…æcNO‘ Öç …Äyß:^Òοˆw¢G½¤eÿ/ôã’q*Ðò 7±åìýxï©ûqÒ+_ö-þøÁ8ÎбÀw<ž‹ñ• v'ËÇZ:7ð(úÈZôn9†Ðòƒ§Î Mv9]gDÐÉL{'°^ÖËzô’Cs=< yrŠ>ìóü¦y™î÷[(¬—õ²Þ;'2½†6WNhHu¦Ó1Ø6ÌiÔþÕ5˜høÁ|mè‘A¸iÖö}ú€0øfâs¯Ù|‡{ç`Ó’ ÑN»ŸƒpüøyŒ4×ËüJ[]cßIOƾCP¯4ø†\ƒÔë,ËrI'ˆ]f'ÈpƬ×À%²ó]&£!ÈÀÌܯçš`9É ÿɨ6ä=xóñÞ¯M®®ñ—…ÈUʧaX¦±—Çs]õO˜tÙò1]ï`,³&û}K`Ä1Ø=÷eÒk{ßNÿuÈø~zï7H”Ï1ÜÉt&˜ìD…9×6ÕÎáÃ%jÊ€ô%µ?–Î*äÏ^xKӞȆG2N‡?в0fnràÈ7Z&8»X‡zé)ñ·–ô‘ȳÃEÔ0‹¼ª %±Fô0XuJè xzšâ¡Öºc[ÖxݾÙù¨aG³hù„§·Õ[¡\Âg‡zäÞÅÁ´üÆùÁ8ëSÏ6Äq±[üÓ̹3oº¿î4.Æï·«Äƒ#yw8sF4ö÷§Ð1(ÊxІJˆ>¼½6ù2¤sÃì`03†“âƒOœ¹z[þ-¬·8°Þâ0½ôR2u>´9†•ZV\ÏÅõÖ[&©wè[Üлa!’™«íçhé$ÆRbh `(Žà¤6p‡/OÈ¿¨›‰xß°k4~Ô‹ÁU ëX×€’ßìÇg:¢àt]棫…a&çKÈô0kŒz'üÔ5î?¶©Äð[I9Dâd0xœ†Jˆ-ãPzM3#™ÐýÌý:ʹ¨ÆMïÇ#×õ£ÚÏ6¹ s=DÍ=á¯OcyD¹ ¯!ù\*A¢u6<­ùÔKù÷Ê¢×ô¾ÙÞϰzŽò9^<ôºu{ɶ—¶¤ÉnUdç£ ñÔ^‘þ(°æ˜ux…ÕÉðL›ú¡ˆ7¾(~4Ö£&Þ$þº“·ÈШ²/2=ÓdDÆ×Kƒt:ÔU‰Gaç»ËÃ(Y¼N²ò&kça†:¥Çhÿ¸Þ·§Jdâ‚olD†Œ| ‰ñN•q6‰ÑX™>p‘égÏÓGÈk¤®ÜdôʧçÀWøqm ¨& 9È뤒<óÉ”Ç>ßú¹¼v *ª`qlGl¿ä÷ ÏcûþË(}ª»Â©†ù“‰d(¤gaf* Ž•ñoëê¶bs#𥞓mç'ºsr’´4—ø ÚH¡È9W‚Þ¼½èwS¯0“]¢>ï|¢ÆI—sy%œ l6y.ÄuO,’ûu]Ñ'Bˆªþ-ùt·«ã ʳ! ½E埣?»Ó­à%§ÝÚü$*Щ;ÍwãÚÁNa´=©Î°:>lßý£rùƒ‘êÈx2Èc) íUbâTúp IÕð³uMˆ§ºŒ åHeŒô’GQÒŸ”û44¢ §qíÀi`žµÒ–>*¹Mˆ¯¨ÅÄõ¯ô±ƒGžú·c Ý5)2`<»·¾d‘HŸ÷¯pk¼6ÛÛCå©n±ÎÌé@ùÈឨ ò ѽÐ~!ùÈòØÒçU?w a¨wÇij e¨øúÔG¼èó…L&PÃŒ}kõ$ÓïÊÌP ½¹ÊóûýâŸs•}‚·ü™*ÃdK¤ïî®~Áz Ö5¬— ñÒ«Gú3aÊúÝÃL¬Ž¤,\ÏëÖKüÐô++fàÆÈwú(Œ4¾‘snÑäðÁH†2̫ӻ¹†ÒÃÊÝ›MôÞÀXMy¦ç–zÕçß´Ç]´ôöb°¦J®öE!z'É•·’2´þ¹=•Z2 ,å¹YŠŸäXi¢¼yŽ¿g]“-/A¥©ŽŒLàŠ.KK³Ûµl,õ´Oªþå ËGCΆÃ]ˆÍõ–Áp_&½÷jƒ±‡^–÷Ó©g¢Vqˆò9ªè"ÿÊ.Ò¾&ƒ¡ÚÁµ[YùP'»·S"û ö/:\¢¦ÌøqÌ6!=–B.#th”T~ï|¬Õ2o”ƒ™Ý¸&N;áú¯Jó{j¢Géa¡4èØJŽKz[>Ëi<Š’Õ·©0奱ÉE>¿B|«>·žùœôÞ9hØÅQÜ^üRæœr8PyèÝk”Ü–Bó±¥·Õ oGÏ|=jðˆzþù,_jáä×3°~Õ tÿ· ë£ø¾¤µªå%›¸Ïü$[€¸:OáÇ"ýÉA}Lã O©| Å¢7¬<ähè~ø¡LƒÀXNuÊÊ‘$Ðô”ºÎ»§ s|¥sÔq¬—õa½Š©ÐK ¦I‰“_‹ôÍnù®gÖ„õ*X¯(õÂ`sÚc6zâ³”nÿ½!Ú=ÞH†ø´Xú+e„¯:4¬­ÆøÙ<{‡ÿsÿì*É„¯Ëqñ9z–?>>!Ãée¸»Øœ% Ò+ ±ðt}&|>OÇ ©89T-®UË)š­œ§_9‡ñ%‹³î˦A²MuCYÃ7²åÀg]pë(léFQÿŸ÷9ùˆ¥×d°•g2õo*-§>i£÷Á;ôÀ”Q¯ï¾êpÁ¡aÓk¢…¿ŸQ>Ç,jiUˆkÌØxõmï‰Ú›©ßáÊùr7Œý›MøÄ ÃÜ[È•3f¢Û™Ø²X°ÞâÀz‹ÃTé ¢JÏ2§Kðó-¬·8°Þ¢@ŽÑÕ#fÇè]ay%¶ü8Tìô©Ò[(“(§m²Ã°I‹?÷œL‹çh€ 3Mc¹ç«¥§ŠÝÐ`½wÖ[¦Jo>D9»¼?ßâÀz‹ë-´ªDÈÊ\Ìô`º§Œv20 Ã0 Ã0 Ã0 óƒ&| K†a†a†a†a˜ìÕbµo¢jk#Jh¼ÓºÎf[úHä›ù%TÄH¨=ñ8®µÉÕþ}ÿŽZZwT0qžÖ…?}>÷e$ 7+ç| =í۵̭º³0Ž«±”{i~õ‹’è9°[fßWÈš©„|'€O_û3þ¥E“…Öy^ ÷¢ŸQÙ:{;®«€ŽˆgvwˆrÖxãRw–?ÊòŸ¯ESœŽÇî¸>m夷ï|i-ú½Z‚ä$ÞÙò_'ðzpè­[EÝ׫7r<œ4ÎFLkrHîO¢´5¤ü!ù„‘w9 dªgRÞWTåÉ•µ>éù4 ‡¯=] oКZµï|ï¼2‡ßTï·K$¶ó†a†¹käɰõ˜W6ŠtÕã™2&w`çkÁ—h«[꺭xU6Ø; “åD¾[bìàãÂH~©ÔBÔoÛ¡Ï™°¥JNPƒDÉis 6¹lÌÌ>‘+Cü0Æþ䦸ވª›ÔžÏ}·¡âz £úØ 9TÚo?š;­Æ|_98õgñ>ܹƒaåʹ¨HöcËþ”ØŠ¸dSwZè‹Æè¾›Pý4_Î^KŠÊoz^íÐ~ uš»Áû)\M<„¶r-(2?]2Ï#6 ó!o½õbŸÁh%c5Öu^œ;'¶$NÿGŸ°aÉ'SU?w›{þ¹O[6!¾ú›³W¡¿wg·g¾Cr;‘®£¯1 ¾w•‹‡=é‡QñhX»‚a†a˜»5’aÁ–hmœ¥\ú>ÑÑ J·ñ‡C=ZB½ƒÿ…t®H†@Ïyɶc¨ªN‰FP¿¦<Óû z%D£ä¶ôQÈÛÿøËÛ€#Á^Š0Éwà!a¼_ "PÑüÛqLø£2ÞÈ PY‚yò‘éË0ÜžDÌ[†Ð|¨¼-¸åíÕñ¥ôõ‚<îËK=ži{KeÏõ%O$ƒŠ‚I§›°TvDåÝB=Ô‹»ý½ô¾Þlê±×k7ïÚÃÈx9šd·¬?êzûUo:pCÁ'K«±^uߺ ^–ù¸i=ï2:@gâ9¦7ˆZãZ8½˜›uŸ™{‡Y/Ѷy.p̯ÏV~S9šÍå™ìZئçå…`©O)9,õæ-'98žÃµœÑ …µ6ÓªÞQødšTv¾-~ r`êÍé*Ab ý'©èi¼Ë(€R™‘ÑCtí¬  ¦ñ¾~$çT#ñ :×½¼[Z«¡jHËä¾"¨·eOèz•ޱ®óò\Ëže"O’dçÑж ‹ºÏáã´@àíE—×¢û^¹bÍgÞJŒ ‰r×Бç~5¦^w7/·œ¶|Lò+k—aµ<öÐ×#{ðmõ@÷ê^ã¿SyP@ýûóÖèòXóñ½¹ïË!«>}ù Bô†¾o†÷Ó{¿&îô9:ï‰j_¤­‘oôݯĀƒ;û[œ}çÖ:Ú¾ïå×óˆÄc†a&R¬‘ ½Žï~!>æ¢á-£¨qîw0¹¢~FCþÜ7·׿{Ôp †@§êi§ž‹LïÔÊ^‰íÖôQÉ1Tl=†ú6±½ü¦–yí<ÌO#.1RÞöwÄ3F 5tHö*®ÍD>”l{ ñÔ^ÕÃ"6Õð¡ÈŠ2 dz^D­ft›òåÞØˆÛ'¶‹f›—°|LPz7¢Cö¡÷e¢¶ST‹!‚%Þ„ºô_ĹØß ,]·FŸÈ† ÆC­Ê°\°ªVîçw/GÈêEÿchjVéÉhmøÚé]OI#”z¾Uï:±jß1X=ïå1¼'‡è´â½_/Œö•t΢ׄMo÷ˆ6HÌ÷G¨ŠÝÂï‡BôŠ´ ¸“‡†5rÀPΰz(„‚ŸWH}:eÊÈE>ù”óÌÕÛ˜ùðUÝ„ð¯·éýÔÛ'ÀÚÖh™û»&Ïåá` uÀ×g½e)EbÖ€Œ8Ü%ί­ >9Ì€"ÄvX-Œ.".Œ®Ï÷÷c¬¦¥gÏ#y³•Ë…QÖ:ŸÒ;dë-z‡Uz±/©’s'œ~EªD2TŸÛ« Úù-“ûϵ‰rz Ã91$ ÖW®ÈcS>’«ñ𨊆Èܯ‡`9)߇{è‰s®±ìÉgßñ $~©ï× ïnwÊBưÚw qs=8×ÈêS߯­<…ÔXylù´4—C[ËuIø})‚õ¹¡u¾vò9>¢¥v½¦÷M:´,ï§(žc~lÂkc¸}9àh~×OœýAvãZû^ÜZ¡¾_± yFâ1 Ã0 )áÃ%jÊ€ô%µ?–ÎêqøÙ /biº#àx(„…ÂÐW=ôGŒŠŽQO žîCÓˆU§„¾`Ã)Y¨Õ½Ø|ãO³óQÃ$\ÇE†Ð| Pú~Cƒ-ô¾ å>Ó.Óò¯3g.KC²CX4O™ÊlÆpR÷b“Áé°x¾+Ÿ4sîÇÌÁ›n/|wã÷ë ðÌz ¥¢âGXùÿÝ/öœ|E^¡z %šrš(øy…Ü—ã°[&Ò!†¾Å ½õv¾þÆnµ9sÉL–§ë„!>àë¹—½ÀÚ0¾¬]u3ïv{†?êÅàƒ3åîX×€’ßìÇg:¢àt]æ£«Éø§-Óì1ê@òS× ý8¤'zø­¤4öNöƒÇ•‘è³Jïjø{ÐíŒdB÷3÷ë`(ç¢7½\×zÏmræzp+Áú4–G”»ðú7’Ï•¡$Z—aÃÓZA½”Ÿ¡!zMï›íý «ç(Ÿ£ìİD13>û· ñµ½ "²ó¡ÎÇ™Xsl’Æa†¹¬N†gÚTƒ<Þø¢hœ¯GM¼É·z€ A.û"ïÞ¿ ß]FÉâuÒЗ êAW§4ãí×ûöôQɃL\0w—däiLŒwª<ˆ³IŒÆÊô‹L?{ž>²@ô3An†I­(Ÿž_ÉT“†œ 4™£r,HòÈ''yÞsgÈ^øÒûÐ l“ç€Å ÷ á[}ö‡Ãûå1<—¾ø›vTœÓgò€ãßâŒ>4R·›/ßu"´Ót’ÈÞhA)öNÆ¿³éˆâ®êÆ`²kBÔç¯Ø1ér.¯„3UŸ›<âº'–Àðôø[‰ªþ-ù8Q ”# gCz‹Ê>G9|qvgf8cŠb€ç»dC§SÎüݸv°Sü¸>©Î1 Ã0 S4¬N ßß9*ç` s9lBõPÏ t0dV•˜d¼"•i4”<Úˆ’þ¤Ü§ñ˜U8kNkt¸¾-}Trª×D «ðâ‘§¾ÁíØBw(EŒg÷Ö—,éSâ>ðn×f÷ªPyª[r IPùTm„/ª‚zjè^h?ß|23¡ZXw’xȉ|Èó¾ŠË Té þvåÑÓ=2Çê•?ÒGAܼB¡^òùf–„¤ÞöcßZ{ŠsÐKùÇÄâ‘›x_ìW,+c-·égbu$“šëAEÌ~BEË})ncÀ™KcYðùÚŸ×ÊŠ¸1ò> #oä\ 4im0’¡ óêôn.„¡ô0†r÷f½70VSžé¹¥^õù7íq-½½¬©BËr-ðRˆÞIrå­$÷ÎÁs9BæC±”säf)~òëpFyóϺ&[®†–äǮ貴4»=ûÆòPOû¤ê?Pž°|4äl8Ü5Ø\o ÷eÒx¯68 yèõay?zVQ _~Q>G]ä_òX~÷ÉÁå×Q ÿÎvšgåCß5¯Sœ"ó¦üûÅ0 Ã0?DAršÿ€"î²6¹À8ÑbHzÛ9oyJ¾0$ÊÏ3IcùHô=«I/IÂÀ Ñs&ÄÌ9¥gÙRò¹ø—¤%.wåŒ|1M$HéoÔ¸½…/’·ÑTª'~´LI¸Ë*ª‰Éݼz"AoZÉØ0þøÁ¸HìI+çPºÂôڰ饕¨lròÆq%÷¥õè%ä¹ÒŸ>[ù7†”ÓT‰žœ‘†@˜&s bz^…Ö§;ñ£ÿùÖr vµÎÅHKœº?ŽâËÎ4––¹ï¡š8Nÿwä˜ø‘Ƥ'M$|Ë>í.)H†Ú&9$¡&Ö“ñÑ$~ggcË/¾r‰=õÀ_“rÈ„?ÌÝÀФWNxH׌_w‰JÍM5‘#aÊË[~:¿ºF•u‘%ÛýÚÊIxË”™0Л¿§Œ69á«S=á¡­Ô½ÐÞ„ŒÔó6èá ¦òÈ{) þ SylùøŸ‹ªcç߯”M¯{_"šKÁYÂÒ ÷Ÿ¨·¾oÉæì÷Ó_Šñ×kTÏQýßy&l NB,Ñßú®­H»ßoYùÔdÑNÜD®ïÃ0 Ã0wƒP'Ã0÷ä(=—Ÿ `h‚Ƨf¢;ã}*!ÇÄê×yt×Y^)´CC©(L•ÞB™D9}Î 6ù”ÀÏ='Óâ92 Ã0 STØÉÀ0Õ{ïè}Ьå!#@E„/ÉyOgôsoÃÆé÷~Ž Ã0 Ãa'Ã0 Ã0 Ã0 Ã0‘¾„%Ã0 Ã0 Ã0 Ã0Lž°“a†a†a†a˜H`'Ã0 Ã0 Ã0 Ã0‘ÀN&h‰ÊÀºçÓ šL°µ ÏêÃ{šøÑWÖ(ÊOy¬w–˜dŒÐ2{ëР'MTù0 Ã0 Ã0Ì=Fމ×àù¶*¼Ó~P®5Ÿ8¿ïœb2(þˆJ2övŠó›Ü†wmìñNã:Ø>lé#‘oBüå—Pá,¯-=ñ8®µÉÕ¾wMî‰ó{1pþôùÜ—‘€Þ¬|œóù¬¾µ­Ã,ŒãêÁ_`,¥Åça:Ÿ'òÙ÷^ùdqWjˆ~¥ëª d`¯:"^±án`\²ÀòÛê!š¥!ÅïÆKœÄ»@ëóËuýåúûśޫ7r<œ4Φ¿ óåÞ’û“(m )H>aä]Ιꕂ÷Uyråc­Oz> ÃØ×~] ¦+aß‘°ï‚ Cúæ÷P¿¦Vv˜ô7a†a˜ï yE2,@=æ•"ÝGGõx¦…ŒÉØùÚ_ð%šðê–ú¹ Ñ`ÙºcFòãH¥¢~Û}΄-}Tr‚OJN›ãH°É¥ƒaöéŒ|àÈa!=Œ±?¹i®7¢jã&uAA„çs߯m¨¸žÂ¨>¶B•¶ÆÛÓÞ·±3ÎïÕz&é` NýY<÷;w0¬\9É~lÙŸ[—RìN }÷¾ƒê§iü²ßÁ@DTþ÷;R¸šxmåZPd~ºdŸGl@çCÞz?êÅ>ƒÑJÆj¬ë¼8wNlIœþ>aÃ’O.¦ª~î6÷üsŸ¶X¾#9¾ YØÒŸÝžÉ[n'Ääú7ú$Ã0 Ã0?T¬‘ ¶ìDk£êßöÒ÷‰ŽfÐPºÍ8ˆ?êÑ…Mžz@%Ñs`·<,Ùv UÕ)ÑHê×”gzJT¤À°’ÛÒG!oÿâ/oŽ mê 2Éwà!ÑèºE z‘ø·ã˜ð÷*©È‡ÃþÈ PY‚yò‘éË0ÜžDÌ[†Ð|¨¼-¸è±¢û]ð:Tœ:ÑÆòx©Ç3m/biœö/y"TL:Ý„¥²£k_¾û:>ì•'­POúân/½¯×zìnbKÇ-ìÚÃÈx9šd÷±?êzû›d™€É~œ,­ÆzÕÍì2xYæã¦óGÈè‰ç\˜^#…æSÃ{O•c&‰ejÛ<8æ×g+¿)ÿæZk=8#ã9\ËÍ@ÑMk3˜êÙÃ'Ó¤:°ómñ—SoöHW KèVÑÒx—Q¥2 0"£†èÚY@M)â}ýHΩFâAu®{y%¶´VC=-“ûŠ Þ–=  wBèU:ƺÎËs-{–‰‡?Ò·]^‹~ì{åŠ5Ÿ {*12$Ê]CGžûÕ˜zÝݼÜrÚò1ɯ¬]†ÕòØC_ìÁ·ÕÝ«{ÿLåAõïÏ[£ËcÍÇ÷>ä¾/‡¬úôå/Ñú¾ÞOïýš¸Óçè¼'ê»¶D”¿G„ù»`ÏÇœ^aûV2 Ã0 óCÃÉpñÐëØù „×:Ð'¿ƒ Ìõ³02t$Øä.÷Í-ÇÄõ¯Ä5LŽ¡üz§ê!¡ž‘èxTE”’ƒA¹¶ôQÉ1Tl=†ú6±½ü¦–yí<ÌO#. r)oû;âãŠb${ ×ft%Û^B<åD 8‘"íÖ2 dz‚Díet›òåÞØˆÛ'¶‹æ¥—°|²!ç •›œ ³Öè{ÐtN>™H=ø°¢Wè oB]ú/âÜìï–®[£OdC†í¡Ve/XU+÷sÎ/GÈŠzøchjVéÉQÑðµ ‘’Æ2õÐÓþEiˆ«}ǰnÿÀ9ç }9ü@§ïýza´¯¤s½F&‘Ï®§îÇISz‘WnàdÀ¡a,?aÈ?¬Î\½™ÿXé á_oÓs×Û'ÀÚÖh™û{!Ïåá` uÀ×g½e)EbÖ€Œ8Ü%ί­ >9Ì€"ÄvX-Œ.".Œ®Ï÷÷c¬¦¥gÏ#y³•Ë…QÖ:ŸÒ;dë-z‡Uz±/©’s'œ~EªD2TŸÛ« Úù-“ûϵ‰rz Ã91$ ÖW®ÈcS>’«ñ𨊆Èܯ‡`9)߇{è‰s®±ìÉgßñ $~©ï× ïnwÊBưÚw qs=8×ÈêS߯­<…ÔXylù´4—C[ËuIø})‚õ¹¡u¾vò9>¢¥v½¦÷M:´,ï§(žc8æïQä4?)ò?͆a†ar —¨)Ò—ÔþX:«gäg/¼ˆ¥éŽ€ãÁ.Ïf¡0ôUÏÊÀOˆåÙíÀ:Ñ(ZcÚÅ’>yvX©2®-òª2”ÄñÀ}Ž#£~7®9éé>d>;«N }äXðМÀ,Ô*#Ÿ6ßøÖì|Ô0 CC14Ÿl¾;ò •o¿šcBÞƒŽîpùäÎ%|¦#Y.¦å_gÎ\–o‡°¼.ž2¿ÙŒá¤îm'ÃØañ|W>iæÜ™ƒ7ݨ†î4.Æï×õ™õ)4Ÿ†2,@ëÉÉB[&b2PN/Cßâ†Þ ƒz;_c·Úœ¹X&ËÓuÂðõÜË^`m_Ö.µº™ˆ÷ »=ÃõbðAóÑ5 ä7ûñ™Ž(8]W‡ù(Åj2þiËô0kŒz'üÔ5H?é‰~+)½“}Ààqaô‘á—1f•ÞÕð÷ ÛÉ„îgî×ÁPÎE5nz?¹®õ¾Ùä6Ìõà8V‚õi,(wáõo $Ÿ+C%H´.Ƨµ ‚z)ÿ>C9BôšÞ7ÛûVÏQ>GÙ9}`ú…cÎ'ŒMˆ¯¨Åèõ a†aæ‡ÕÉðL›2â/ #b=jâM¾Õd¨tÙY½”6yï.£dñ:ièKƒ™"Ô)Í8FûÇõ¾=}Tò ÌÝ1ù@ã*âl£±2}à"ÓÏž§,ôUF¾³&Í¢|z|…ׯ€jrÀ€&áRŽIùä¤öMT.†;±eŠ _p¢ hû`gô©¢@Ž‘ñoÃuÖmÅæFàËwH팜$²7ÚcÐF …½“ñïl:¢€¸«z…1˜ìšõyç+vLºœË+áÌùçÃ&Ï…¸î‰%p#<=þV¢ªK>NÔÂ…åÈÇÙ…Þ¢ÑsÌë{4(Šžï!Ã0 Ã0?h¬N ßß9*ç`„6¡z6¨S:«GØäFÈ(G*Ó()y´%ýI¹O=éU8kNkô0[ú¨ä>T¯ŒVáÅ#O}ƒÛ±…î Š$Ïî­/Y$Ò§Ä}à+ܯ͞’ÊSÝâja†ò©Ú_TEœ„‹æLx<ï|òcX‡¼ªûzf JOD¸+žý‘±8V¯ü‘> âæ õæÏ0³$$ çX0ö­µGÛJ¡ùP¤ÃüRóÄ‹Ãã⺙XɤŒözXY17F¾ÓGa¤ñœcƒ&} F2”a^ÞÍ…0”ÆPîÞl¢÷ÆjÊ3=·Ô«>ÿ¦=·ƒ5UhY®^ Ñ;I®¼•ÄáÞ9x.GÈ|(–rŽÜ,ÅO~îÀ(ožãïY×dËÕÐ’ü˜À]––f·gßXêiŸTýÊ–†œ ‡»&›ë-ƒá¾LzïÕ'b!½>,ï§SÏ* $áË/Ê稢‹ìK »ß£pråãGG1ü›W”`†aFº„efÙJü¯ÎïP“8ÖmÅ«¿jr'Ç’\ÂÎwÓFyè’†Þ寜e¯¤ ÂpÖÃ$䱞²Êž0åC$§q«ÎRŽ¢íLÊh• Œ-†¤·ó–G ä Còq ü<?æ‘ÄSá?ŽãêùaTÌv'Ê̆–+\9™ƒ\¾tÀ¿Œ!-q¹¸+g„‹iâG2Ì› Fø-|‘¼¦R=ñ£eBHÂ]SMüèLbèæ%ÐzÓJƆñÇÆEbOZ9‡ž°1D¯ ŸÎ|òñMi(éˆOŸ­üCÊiª‡]­s1’ÇÒ¡îÄ£ø²3¥eîó%#%3qlމiLzpÒD·üàÓî’‚d¨m’“Ajb=9Mâwv6¶üøç+בØSü5)‡LøÃÜÝ Mz儇t]Àøu—¨ÔÜT9¦¼¼å§ó«kTYYò±Ý¯­œ„·L™ ½ù{Êh“¾:ÕÚêAÝ íMÈH 9oƒb*¼—êŸ0•Ç–ÿ¹¨:v~>LùØôº÷%ò ¹œ%, zÿ‰zëû–lÎ~?ýå¡H½FõÕÿwÂÆü¾kÏw!W>'=}wV¤Ýï+Ã0 Ã0?xB ÃÜ{Ðj¥çüŽ˜¨ 'Ïêשq×Y^)´CC©(L•ÞB™D9}Î 6ù”ÀÏ='Óâ92 Ã0 Ã`'ÃD@V4C J *|QQ‘GTsïÃÆé÷~Ž Ã0 ÃLWØÉÀ0 Ã0 Ã0 Ã0L$„/aÉ0 Ã0 Ã0 Ã0 “'ìd`†a†a†a&ØÉÀ0 Ã0 Ã0 Ã0L$°“‰Z¢2ïuÕ#„&+l-óúÉZ–oo]f=ÿIU> Ã0 Ã0 Ã|/È1ñã<ßV…wÚÊ5ñçwàSBLåÏQIƾÀNq¨Ç3m/b©³¼FB훨ÚÚ¹¢¸gn+¶ô‘È7!þòK¨ˆ‘P1zâq\;k“«ýû6þµ‹ÕIµ9üéó¹/#½Yù8çSèiß®ehó5µjß—³öù8®üÆRZ<ä;ϺêE‚œ «€Ž¨WZˆ÷ÿ%HNâžh=|¹Ž¾\ï¾x³Á{õ†"×þ‡o wþ ¹žikHùCò #ïrÈTϼ¼¯¨Ê“+k}ÒóiƾöëZ0]±üzÛrþæ~÷&•Ã0 Ã0 c"/'çíŸaEÛVàÿ¾Ž{É™°ß´“ᥠu=Áõèk䔸i:[îB ½ÜÒž»õù ¶ôˆHþ•hÄnŽ(¹ 5nMò|Ê ”l;†òëä|8¬%“#˜Ò=ŒÑjàZ¨“Êÿ$ÆþDiTC=žùœkAÕÖ2 ·'óÔãeòN¯QXLÃ÷Nu™ŒÕ»Qþ»U'ŬkAýQ•'W>Öóß'ƒëï*9 %í¿ÉÒÁœÇï^®|†a†a+V'Â-;ÑÚ8K¹ô}¢£4”n3œ ÊùPvÚŸÖG GƪêzNˆ«×”gz˜¤1½xXÉm飷ÿ£@'9=yø/á;‘êØéS‘¢‘쬕%˜g _CÙS†œù¨ú¬ÄOãÜ1䑋7‚å’'’A9¨Òé&,•„£øò]rRÉ“Y¬\9Ï•ÞæÇ1sp_ÄÊÑ“ X_‹õªk] dµÐ¶¹V¤ñˈ]›c×Ëkná‹¿]Fû°¢ÜqûÄvÑ,÷–Ã&ü¸6†Û—½>òÉÇK>lߡߑñ&Ô¥ÿ"ÎíÀþN`éº5ú„™™ó“Æùå(=×/Æf ªx¿#…-ûõ&Þ©õëéÛ?P²‹òÈC¼ #ýòÜ“@S³Joã_oSùõö °ö…5Zæ¾÷ò\"Q|}Ö5 R$f `ßKçp¸Kœ_;[|r˜Á9)ßwX-Œ.".Œ®Ï÷÷c¬¦¥gÏ#y³•Ë…QÖ:ŸÒ;dë-z‡Uz±/©’s'œ~EªD2TŸÛ« Úù-“ûϵ‰rz Ã91$ ÖW®ÈcS>’«ñðèyy.s¿‚å¤|îUéiËËž|öŸ@â—ú~ òîv§,d «}×7׃sܨ>õýÚÊSHý‡•Ç–OKs‰0´µü%ep‡ß—"XŸZçàk'Ÿã#Zj×kzߤCËò~Úˆâ9†cþ]ÍÐü¤Ÿ¾sÇ@Tù0 Ã0 Ãü@ Ÿø±¦ H_Rûc鬥Ÿ½t&œÀ;Ú ûëñº0ÖÂY( }Õ#5pä-œÝެÉu"Ko/º%}$òÃû“6¬Å6p½õÛÈÑa‘W•¡$Öˆ.èsäÙèã»qÍIO÷!óÙXuJè †÷&0 µâ^µ³Â7.8;Ÿû6n³4°ÃòQ“Ãx­—<òÉŸKøLG¸\LË¿aÜHŽªh„±añt™R”áÖZµ­Êôφ0†“gþWî¹z[þ ƒz;_—Î1±9sL–§ë„!>àë¹—½ÀÚ0¾¬]Cu3ïv{†?êÅàƒ3åîX×€’ßìÇg:¢àt]æ£«Éø§-Óì1ê@òS× ý8¤'zø­¤4öNöƒÇÏ)Ã/cÌ*½«áïA·3‚ϵ™¹_C9Õ¸éýxäº~Ô“6¹ s=8Ž•`}Ë#Ê]xýÉçÊP ­Ë°ái-ȇ ^Ê¿ÏP޽¦÷Íö~†Õs”ÏQ:¹³¢L¿«›_Q‹Ñ *BÍÁœOæ|†a†a˜ü±:žiSW¼ñEa|­GM¼É·z€ 1/ûÂÚ»û¯ó—€²*}”Íw—‡Q²x4ô¥ÑK‘ê”f£ýãzßž>*y‰ æn¬Œ| ‰ñN•q6‰ÑX™>p‘égÏÓGú*G…³&£|hÞŠD@59`È @“—)‡€$$9,dvgf˜H(y”§X´#†çÀs"Æô™ˆ¨ÛŠÍÀ—ïêh…O´Sm’ÈÞhA)öNÆ¿³éˆâ®êÆ`²k˜’“.çòJñ&°És!®{b ÜÈO¿•¨êß’µp¡A9òq6D¡·¨Dô³~W)úžßãÉU> Ã0 Ã0?`¬N ßß9*ç`†—6¡z„¨çW:BVøÙâG0Öó™>2@F9R™Æ\É£(éOÊ}š7  §qíÀi`†`K•܇êÍRÃ*¼xä©op;¶Ð"AãÙ½õ%‹Dú”¸|…[㵨ڸIp òT·x†Z˜¡|ª6ÂUÑÓ~TÞ í‡å#ë“ ù8 ò,Oq¹=§BÛ²|" %oä\õx¦%ÉP†yuz7ÂPzC¹{³‰Þ«)ÏôÜR¯úü›7ôQ6-½½¬©BËr-ðRˆÞIrå­$÷ÎÁs9BæC±”säf)~òëpFyóϺ&[®†–äǮ貴4»=ûÆòPOû¤ê?Pž°|4äl8Ü5Ø\o ÷eÒx¯68 yèõay?zVQ _~Q>G]d_×ý]%tôÁ¿³ßråãÇžÃ0 Ã0 “?¡«Kd–­Äoðêü5¹cÝV¼ú«&wR1É%ì|­ ÏSă–Œu†­,¡ñ.æ[’ ÂpÖÃ$䱞²Êž0åC$§ñ¾ ÈLÊh• Œ$†¤·ó–G ä Còq ü<?šò‘«H8et ²&}ùK|Ï Xž nš‡Ã}î¹|é€eZârq—5òENüˆkØþß?Æ{û>ø¬Þüpì2š‰oá‹äm4•ÞÄ–Ž[ØÕZ‹òjÍØ0þøÁ86¶–á¼3$-qÙ ÒÛp'~Å—i,-sË©&ŽÓµ”câG“œ4‘ðÍüÿ´;Û?j›äd„šXONÄG“ø-¿þùÊu$öÔMÊ!þ0wwC“^9á!]0~Ý%*57ÕDŽ„)/oùéüêUÖE–|l÷k+'á-SfÂ@oþž2Ú䄯Nõ„‡¶zP÷B{2RCÎÛ ‡ƒ˜Ê#鹿ú'Lå±åã.ªŽ£Û”M¯{_"šKÁY] ÷Ÿ¨·¾oÉæì÷Ó_Šñ×kTÏQýßy'l ù]¥ß«i÷÷ÝC®|$Þß=K> Ã0 Ã0LþäXÂ’a˜¼X^)´CC©(L•ÞB™D9}Î 6ù”ÀÏ='Óâ92 Ã0 Ã0‘>ñ#Ã0ùñŸ+ScðM•ÞB™.å,~î Ã0 Ã0 ãƒ#†a†a†a†‰Žd`†a†a†a&ØÉÀ0 Ã0 Ã0 Ã0L$°“a†a†a†a˜H`' ´DeÞëÑZº²µ ÏêC&À÷µ~h™Æ½uhЇSνVžé×Û~î Ã0 ó½ ÇÄkð|[Þi?ˆŸ½°‰ó;ðÎ)!&ƒòç¨$c_`§8ïRgÚ^ÄÒø%ÏÚäjßDÕÖFȕصÊð¥BNk¤¯©•§3øÊä]_}Wþc)y€Ò%p­}»>ös߯¿£vqLî«µÞøË/¡B‰$£'ǵ³úÀŠ­<›t~)ô8e°Ôƒ·,¾<¼u᫃ä;ÜÏL1Qé¸ëe5ÔOtˆß7– 9‰w¡ü× <zëÖä–8$#å1à¤çº–=ËxöF|rt]Ã0öµ_×…·<6Bó7”'ª¥7ì]†ùz?£ÛpŽ>´-âîsøø#}Bã–_p³ûôªÞrR=lª¾³3Åý” ¹?‰ÓÿÑò%Y÷}/כʻTí{î׆­~õ &2õ$Ÿz¸¢Ê¿˜åœ²çBtÿ“w«¬Îw]æjϬßw+N›";mv»å°Üg†ayE2,@=æ•"ÝGGõx¦…ŒÉØùÚ_ð%šðê–z™ŽX°e+–¦/A& E|ж.ÄØÁÇ…1ü8R©…¨ß¶CŸ3aK‘üìvyœÙNˆ/ìõoH±ä¾-˜!>®ê|®µù¡ž}:£ÃýPÓGÝÕÛÁ`/Ï}·¡âz £êP`¹_Ñ0©\<œÑÙsbRýˆÆÌ hù^\E#ª6nRY…qêÏâ}(žƒhÿ …-ûS¸¨}t§Å¹»ëP˜ÖÜ£õóSa°~~'†ÎG½ØhäŸ~åœÃ >.„|Êš¿¡<ÑAFšÒ}¸««Ûfkya8å§íäP5¶üú}F³¼OƒA×c7Ò:¹‹Ä¬ Šã ÷r½µ4“«t'a¸ßÆúu²E”=¡ïÕ¿Ï9ˆ*ÿb–sêþ_¦3‡1ö'ý­ÛÀuç»\è÷Ý9*Úo?êi;(ìí†a†±cdX°e'ZU?¹—¾Ot4ƒ†ÒmÆAüáPP·¯þª Ÿ½Ö…D®žLê-_”DÏÝò°dÛ1TU§ÄǨ_Sžñ¤+ºøX’Ü–> ¹/ò€z ¶G\ãÒÅ.þÞåá_(#n/G…ôü;½¢1`ŒpÈÖå Ê¦à/£±<²G£ ÃíIÄ]¶zi¨×:j‚äå׳{(¨þ+q ¤aáD®Ð¾7zEEÁ¤ÓMX*ƒ"Fñ廯ãÃ^y2R‚½þ¶J7’œ¦·ðÅß.£}Xs¯n$û±ýÌÿª2 @'Öù 4×b½Û­¬¼Œ-·ðìzï9·Lm›ç_ßåQy‘Þ“¥ÕÖ|L¬\9Ï•ÞæÇ1sp_ÄÊÅ}(°êµÔÏæFÆÅõòý˜ è¦µ™àõìá“iRØù¶øÇËA°7•zG„ñœXB}tno±¯·ÐÓsÚC*Èêe$C±µΓD_/ «w×Û.ñ÷`ó·•Çv_„·×|¬ë|hϲW_¦¬—« ŽdðBúKϪ4êºTî©þêF.iDø d`®¨Ÿ…‘Á¹ÿÌÿiÂÈ'ùõfß7·׿{ôÁ¤`§ò SDAÆë¾¥Ò¿Ýš>*¹æ'Qqý´üÈ’‘]ߦŒýYkŽÉ}Aî—O9Ñ^­0\(9ÝËÆ7…hfŒ§{™GÛßÏc1TlÕò—EZÍÄÝ !ó5,ôÚË#îgc#nŸØ.šO.öûÝ-{qk…ʃÙŽ„Møqm ·/Û D>l§¨zGÄ›P—þ‹8·û;¥ëÖèw{„C ¢YFç¶œÆýã1¬Rr4|ݯäb u0ږ͸J»e¿2Äßïpt’á®ÏiÇ€sNnâÿgýz§çô4•ÞÌœ»š˜„äccæ|àä߆qc~9JÏõ㋱¨*·ëµÖO¼ #ªþ˜ššÃ{xÿõ6=w½}¬}a–¹¿ò\"Q|}Ö{¯¥xX¼_²ù¸hÔÿ²RËÍ„öØÐ:_ïW×ì;>¢¥.ÁòlÃTú“âeëêÉ2,½ØËc¾/2jî=¯äb 5˜$¥X½wž eȤ¯©—2g ú¬LPØ?¥M õø èÊ_× £Úo@_¾‚ (GKóLŒœ½Ž+Cú„æÞ¯7—D]‰¸ŸÜémõF°lålivëgßKÙÎ Ùÿ/“ã^)çÝ~î¦òw·;:ɉ ÏiÇ€sNnÇá‰*AbÖpæÜø’*ñ£ªöMùØ(ü½%ƒŸ¾×/¡âúQé<¸³ï{„¶[†aÆNøp‰š2 }Ií¥³œ?{áE,MwHǃ&¡öóg¡ø0*üÀwh9°N|ÐÈïõ [ÒG&'ÄÇzE-F/¨žïŽüB9úÕ\ Òà—½;«N‰ëMn\4ÐGc¢«ÊPkÄâcïs> '†ãP›ž#ÁV5LB5<²1Ý/5J'ÉQ`ͱ¬aäD±ç™—ðE¸.¦åß©e 'Bwã÷Ë ÆÏ÷Èó``ü4=U‹]yÎNFчZkÕ–‰€ ná Oãû÷ž¨‚B¸‘U× ãˆÓý'°ëµáÖÙ«·åß0¨7ìõ7v«Í™£e²<]' ¸€á2â†pÔ‹ÁgF7!œÐ7¿/¨Ïƒ±3bIfކéRoä` ²äêmâ¯;9ßgNù…UGùÁ…äÆt)g†;xî•_èyb ÜÈC¤ÓT"Û ³çMúûž7y¶[†a&ˆÕÉ@!ðû;Gå Ò€Ã&”ç›z0¥ƒ!³ª„2ïl¶áèc…TæãUòh#Jú“rŸzð«pלÖèð<[ú¨äÅðï„)ŸB™ŒÞBHã9ÇMŒd(æõVÕ#æú+¿Š³/ ]²H´R¢}4‰ï;‘+ÿ yµ[†a&›Ð%,3ËVâ7xu~‡grÇ&w²4‰wÂ?"Ï%ìLK%J­n ‡IÈc=d•!=aʇ˜Œ|EÚ=ö@=ûÙ?ÒIgIÑ$ÉLüèÊ|ùˉE#@89šòPC-Ô$N´7Ž«ç‡Q1Û?ÁSvy“5Yî—œ îWº<¾2:8e5AÏš"W<ÈeMüï-q¹¸+ïñùù°«µ ô¾dlü`ó‘k9o^¹&~ôëõOžHC~+'–dMüx _$o«y„\NüxÌ<¹¢)râG\Ãöÿþ1Þ{Ø÷Áÿ`õæ‡d¾îd”~½özóL I“[6¨ô6܉GñegKËÜç«&Óouމ©WÙ4!¡o‰FÏÒÔ`ßäL'Œ9>ºýº?=¡¯±Éeo¶|qGp’Æ\ë ëlåñNÒ&É‘^r-sðžË5‘1Ÿ§³'Þs&Ø£‰}“ÓÊãÕé˜OÕùNö•fÕS_ÔC|Ï×›0|}“}J(bÃ{ŸÔƒíÎ9`¯s9mïaÊËŸO°,ÙïyXþ¦ò&ùT•ÓVoyÉ'ûܶòî=²&~ôß—œøÑ3Ñ©S>„í¹ØÊ¯~?½3ÚÛ }ß5¹ò—8yÛ- Ã0 N¨“aæ®BFß/CÃaJ±•Çh¼›WøAÂõ¦¸ÛïsTùO—r2 Ã0 3-a'sOÕ»®¡Lò°Þý(°•çnëµq¯•ç‡H°§u𸹘ñÃõÆ0 Ã0 ó Ã0 Ã0 Ã0 ÃDBø– Ã0 Ã0 Ã0 Ã0yÂN†a†a†a†a" Ã0 Ã0 Ã0 ÃD;˜4Ûý^µæ?Ã0 Ã0 Ã0 -9&~\ƒçÛªðNûA¹&~âü¼sJˆWý¯ÿü•dì ìçz<Óö"–z ïûD§·á]Ù³¾³[úHä›ù%T8ËJ FO<Žkgmrµï]‹Z­] ú|îËH@oV>Îù<Ö­¶Õƒe}m”fQ=vkA!ˆ÷ç%HfÖâ¾sh½ñ'У×mw×ñÏr2<ßéî:þþµÓ}–â#¼åñaIÏ0 Ã0 Ã0 ó}&¯H†¨Ç¼²Q¤ûè¨Ï´Ÿ¾¶;_û ¾D^ÝR/Ó ó_¾Krµ…:°m]ˆ±ƒ #ùq¤R Q¿m‡>g–>*91Ž«ZN›ãH°É¥ƒaöéŒ|àÈa!=Œ±?¹i®7¢jã&uAA„çs߯m¨¸ž5ž ÛýnB|tþ{q“-gqùé’ |4è á£^ì 8N¿rNÈÎaP—‡a†a†aæ{„Õɰ`ËN¼þÆzÔÄ›ÐúE(ÌÂÒ_íÆó«zða»Ó3݃÷ä6s4'0«ÿ4ÆRêð¾Ù1 :¡zÎÛþޏî`'C¾¾í½ôÉ fJçèáß„ûg·/“óAÇ_>&îGm£ž" ´LÞk|DúJ¡{à@R;˜ò±Þ/91œˆÃøŸÔ¸Þxóq"B (—×ßp¶ßàg™xðÖ:ç^X£.˜$5ëð9*…ì¹½ËÄ–@Ër%£‡ÌpŠ(h•' ˆ•6ÏáË+±E¦Ûc¥Zè’U[z¯\ë¦k·´Õ)y[¥.›.—ÒáOÏ0 Ã0 Ã0 3°:.z;ßýB§ØùZúpIF/ø£ê±¢~F{ô±rDHƒ²m«–™¹on9&®%ö”á]~½SõÊŸÝŽžèxTEHC¾}»5}TrE [µqýò›ZFäµó0c<ø6-÷8Fdô€”½„ŠëG3‘%Û^B<µWG8‘iP†-ë9!j5£Û”(÷ÆFÜ>±2ƒ9ŸðûuØ„ׯ´ÃùÐsB{'Bø×ÛnôÊÎO€µ/¬Ñ2÷½‘çÞ…ºuÀ×g½Q¥x=2 aßñ $~Y©åf XØÐ:_ïW×ì;>¢¥.ÁòØÒoh‰ÏI&åÀê=ªœñàóýý«©FéÙóHÞ,A79$äpŽìô Ã0 Ã0 Ã0÷:áÃ%jÊ€ô%µ?–ÎWÿ³^ÄÒt‡vЃDÑTȨÊX¾6xÀ¸ ×Í{tt$A Û/ÞÛkï'3ÏÙ;Ï ²ìõ‹H”¹rçÚ+wæIr­½rïÿâÿ“úÿÃ0 Ã0 Ã0 Ã0Ì-òô_†a†a†a†a˜[‚ƒ Ã0 Ã0 Ã0 ĘÛËC¿ÆŽW~_êM†a†a†aæÇË-Épïÿý Ì™þ%Îÿ·ÏjIü+ÌxóŸážöWñÍïÿ­–3 žÆßÔ ãoßnÑb)žú›_¢×qöÿý¿à/WH6µy=M“€áÿSCǯœ¥…gŸÄ9®ÿíБ±üaî†íXøíÁðÌ+ {ù5|¦÷e>IW…uMÏaQ„Ö/zä)Ðq Ú±ýÍcZ píéÔ¥<Ã0 Ã0 Ã0 s縵L†ú&¦—CŸ‡`˜ ÌX…Íó_¢ão?C)¾Á_Þþ7øÛ¿UËÇ#ÿ›ÿjÐñ‡¤L.÷#2*!™ñW«±h¤ßÕ•¡ü¡+jâø<Õ¡Ï…¯a»/Љ÷›· YKJ»eCö0 Ã0 Ã0 Ã0·[ÊdHÏbøkLû7ÿJ'ëÍžó—aΖù¸)<Ê¢rÙ}¡¿,Å•®7òÑùBg¹Üt¹Auô©ì†ž @ù\ÜKrÒÿ¯…ƒ.äE² ‘Àå}`ø?ÒºÁžý?ÊàÈœ-K”_ù(¨ðÏ€·Ó2 (›ágˆ'3¼¨ì„âSÿth‘„䫯!Ýÿ—i8õ·ÿ,‹?ÀW<µ|ÿ·?ýÃoý=ý;-¸üY 2“!ÞVŒE5SÅöÎîÝ÷»”<™áÌ(¸˜!c!å8¢r#^úU-ÔÝ ènñe&¤e1ØÊûäªî:¶8T<€H÷œ-®¶i»d¶Å²t`vÃ0 Ã0 Ã0 H@&}²ðªß<Šiÿ”¶Éaÿ3ÖÉ‚…B_ƒvèñ%Î?»T-äÐK"(š~NʺÚ(ú«÷­òÿü¯éØ!±÷?·¿ªôx>ŸWÔù ÉþBÁ†‡…mÿ#®Ð¶^¾é‰ ô_¼)JÚìçµe nþEɺÚÒ-Ž=aA‡ÿó7ë±hä³”ƒ`Á/…ü+÷óŠö1òqj`Áƒ¯|0Gÿ°ð™‚ez;ª°´ èú{oÖÀ¨Nþö—·aûGq,úëZn"׌…<ó«*tí¥cHÿE-wHµÇVžäÅøœdR¬hRvF*€Ï÷žA¢¢Å­oàl¢ŸQ@B~Α^ža†a†a†É ká®FõüÿåštÂïÅ|LšœÀ·]žý7¾ÄÇ?ÿ§#2Ùþé„#ÿþÒ5ñït¹N¤ÊU@ÃÎnÿßÔÊûëpþY•p׋GQ-"ž,›=ëþ+™õPôWª|åÂd?xf²þ\á | ?sø¿Äß<µTˉ9ø«ºYèéøBn©Ï$ ˆ$þò™¸Øô÷h¿zã>-É“‡VaQüïð¾¾ÞŠ‹î§ 'ÚÑ)VëaðÐB”w§Öç!Õ[y’ã¬xevÐ’ÌPm-*C!qŸœ" ¼‘îv7sAŸTÉ0 Ã0 Ã0 “;Ö ÃÝS#øÏ#ç¤3ßÕ>sè“„žÿ ?+øW(Îùîú²ì¸²î}( O-T&ƒ–BŸHèì¹$Ó3ì\iÁÛdçè{éèè¦ÎÐ[ÊJÀÿ©ƒ s°è)ÂÑý%þFf>Ð ’³°R¬?µ@–N)Ÿå÷ßB6CÖÕãìǃ(V–¡(×·›,ìñBŸM8™ ´4Ð;†a†a†a˜Û‰5È@Ÿ-83;|ÿûG”3®?Y ¸àŸùá?þ$nEÒ§ 6þÓœ Ü8§·\yƱŒè =¥ÄfO×5ügDPºò_iA–Ðló4ß?[,˜…á‹gõ–ÎJ8åŒëà$òoå ’4»„3†CjùÌ<Ðüs,,¼‚¿<ñखAãìxe;ÖUjQù *és[V`îÏ«‰÷é­bÜ/oÀ3žÌœè‰#Q±Pg¤è1Ùc+O™¿ðŸO>=Ô¿@y"Îc20 Ã0 Ã0 ÃäA?ª±"]¦é%ÕÔ“ÉA“?:ƒ,Tæ€oðEBË5ô DòSïÀiÓZê±äàŽp¹}:J+Ïéq öP dÝû¨þ«¹Z(úÍŸy$‘SJBM/)Îô•ä“W|òá³ïáí¿|£6ŒÓ`z!ž3–O'×)ÈÐX=ˆ£’ýòÙ]ˆvlÃ[Î'áÈ¿²Ê=ßÄ™d†€Ò¡ƒl‹cQ1 üyBÓg‘õ®¨ ÁE|Bc#è))Íö(;MåÕ´™ÞÏ$ÞÀ{بüûñÒ_ï5Ž¥4ö¿Ý/~îØOðÀ Ã0 Ã0 Ã0ù’{A:èÈîBh Åäø „MÎŒ/4¢pÂûCùÄà‡fÃ0 Ã0 Ã0 cå–¦°Ì 20 Ã0 Ã0 Ã0ÌO‚Ûd`†a†a†aæ'uàG†a†a†a†a˜\à Ã0 Ã0 Ã0 Ã0¡ÀA†a†a†a†aBaüƒ 4Ýà+¿Æ/õ&ÃäÄD¹ø>gn|_1 Ã0 Ã0?0¬A†¹¶‹—×])Ëõe¶ÏÜqÛª°®ÉÔ.6y–ÓðlƒÞÈj §þíXWéÈngû˜ôÇ5ù±3‘ÚÔtÞn&BûÜâóá'Ï­ÞWü\b†a†¹ÝXƒ îÀö—·aûGĵþòkøLï¯Ý½·N¼ßLmÒ‚-QØäw†¹~¢¶7ôõÚ÷»ôŽ+åþ™°÷y~üäîì ùùÀ÷Ã0 Ã0 ó#ó–Ô³^ÇöæZ ¨Üˆ—~U‹ˆÜ¸ˆOäK.õÐmïÐq,ªy@îIˆ—Á÷°ë‹ã@ňtŸÁÙâZ,Š\”/Ùëšžë^Dži*C<.ÊUÐöÎîU/“¿|vVH¡ŽO¦énÁö7Yì ‚ÎÁ±IÙÿÛƒÆz]=Ô3¶±4ݹ͟\àØo!ÈÚíØ†·N¸Û¹µù:R;˜°ë‡<ÿx[±Ð3Uë ŸîµÇ•̱‡®¯÷|ªó³7ßÈùþñë'<û ˜Û9@¿ï=•J~i¥ý:æn§ù>Ïù÷b¸^¶û0“=Xì7Ÿ—½}.Zï7s»¹·e~5ÖÄÓÚÌ&7>líi¸o·¿|Àr_ûmäô€µ=ÍöÛô({ìícÆx_eÕnŽ\µ{úsÉö{¶Ÿa†a†IçÞWZö?èu3üTŒáØßý'-/i/UàôËÿÿë_>űï~Ž_ý‹j±ÿ,üå/°'±}ç[8ö—ï±pc=¾¸uÿh Ç÷^@é#µø/޽‹3Ž…½‡ñþ¿Ç‹rÕUŠ«ù{tKý•X²ê—˜uá ü¿þç÷Ðußrü×ÿäâØ¼€îÿHåõBõþ×S𿾩êªþ«Iøß›DY»Îy¤óËg_ÆÕ¯ê¥:ZÛãRnªWÕA{}ö;¤ÊmöÜ'ä5èßûÿÀkÿÉÿ™qÅ£?“=]óþ¯øï6þ3Ì›Lû/Wà‘¿Kõ÷y´ù:NN;?…²Å¤ŸÎ)æû©Ç½ŽÃFý_vV㥠÷âxŠ=ÿ¡2æžÀ—ÿ@8BÏ-Bïß]Ç?zìåqÿTâ™ÿæá¢nçï«W ôÂÛØûwê›0_wXïO3qtü].vþ¬û?ÇÀ»ú~øË t µsîvšïó/䘮W×ì'÷¡½}l¿;ûûnž^Eö$ŒòÿõzÕÒ>ÅÖûù3c»å×>Eÿd9~vÿ¾NùØäê÷á¹,í)Ÿ†û¶µ½Ór_ÙŸcfr{|ù—ó9ý~3=Oìí㇂ÆûêZ½¹ÞÞE{Äý#žK³ÞN¹¯þ“åwd~^Ùž‡ Ã0 Ã0L>?>´åx+œïbSzØÎ~ìôÀÃ[/¿&×m-ªg)qŸdÕ³yŸëž¢ ýî ²oœ_½í4Ñ€h…[¯—œêµa³‡äÝОŽÉçó–OÄ›oÏG”J,–€lˆ\¯c6½Œé\Ä'Úïu4Þ'åňt·»õœhGO¤X®ͪÂÜŸÓz±°™ˆãR`{™ïŸ\±_÷pô›õtâR|*ýjžyH‹2›–û<×ß‹åz•çzüî¼ö{ï“<ø{óýln·üÚGýþÒ'6y÷nµ§ŸŠÜ~¿AÏ“lÛÇú|³Õhùþ±ÿŽìö3 Ã0 Ã0éä7»¥!ÓKž³x?¥¸]TnÄúàì^]'‘‰0ì̧^?F{n3äGŠ ŸŸ·ч@1ây¼ä_D<¡œwr"VŸÉ˜BZ;çÈgoª:c ”­Á†ŸÊýPY†"½êÃ&ÏÄx=Or&Çû6&Àsà¶A÷OB7löXîëý`¹†a†a+™g—`˜4Òü‚vÏøäìýñäŒ Ì º>YÎÚòc†ï[†a†af‚ÁAæ'9g«ôÀ‘ @.»)™;aÜ·é:’Ðx s2 Ã0 Ã0Lnpa†a†a†a˜PÈov †a†a†a†a˜8ÈÀ0 Ã0 Ã0 Ã0L(pa†a†a†a˜Pà C(ÐÀj·0×;3þÐ(þ·ë’îgôÃ0 Ã0 Ã0Ì—x_iÙÿ ×Ó!çè¿qçO¯þΙŸœœê'09˹ÍÍä£#Œzæ ëšþî=ö¯ñGgîöÐø!ž¯‰Ûi'µïËxz•š¿þûd6y&ÈÖ𤼧ëp_§žÿþŸã‘ÿYÏ×oÆÒÿßÿÓð×ó¾Fk{\ †a†a†a~|Ø3*7â¥ÇO^Þ†ízáéþÒ™»a#Å[¸mnx¿™î¿ôh‰Â&fî†_ ¨í }OïÀû]zlj×Äöí›ñ³7[0T³ë*µ€a†a†aæGˆ} K 2üªŸ§8^¿|vVTè =׺ßE|’<–zŸÃ¢ˆÜ@B8y¿=øÜùîõ~œÁöæªP öz/Šc7 Ÿ3ŽE5H±Òß™£=úœk¡ÄÞò6H¨ûߺÎêÜ Û±¾8T<€H÷œ-®õh]”ò¸²Ñ§ß'ÁÙ½;pi¥½Íh[ í`?¯\Ú¡Ï4•!ç#íÊ×NÂ\¯ýz”%àÜ/^lr3TG´Ã0óÚ’Ú6–öÌózÉ{” Ékâ)çÊ0 Ã0 Ã0 31±I§/Íi̱#GlA»<Žô<×ZW¨drL S½ÚIŒ;Ç[lËÁy•¯[ƒrÆÿø­§LÒiÜǃÂQúè Äë7âýÛéÚ$õ®œYO Â%‹vNbn‡·^n·žWníÐ'ä«d•—çYüwÁmnÁ\o žë¥°Õ‘]ÝêºLÕ[ß}ªÇÖž-ù_/Ã=ÅA†a†a†a~LüøÙ›ê3‰O° ;^Ù…gÒ;,ÃDåä’ìém@´â">7:”`…(›]€!ˆœýØ9þ˜p•Ö“=-D¹¶Ç_>wm-ÊaLœÁ'Nyy1"Ýí®#y¢=‘bü¸ŸŠE¿Êܾ™IoûyåÓnù ýùŽ-`¿Ì×+.Ü¡îån ç#u_g¾ç íy+׫'Ž„^uPvq€a†a†a˜YÍ.!ƒ ]DyýF-1P¹ëk€³{µ'Êgæ"ζÅez;Dò±‡z¶©¬³e1„ˆ̉-Pö­RÈõ¼Æ£òºƇ¼¯šâ}zƒa†a†aæÇGÖSXÎU¬×ŠqÚ vq\’)äUXWïôD_D<ñÜP¥·ý\:¸û;«°ƒRгÂT¯ì¡Ì‚Š_ä60_×çè"ÝÙC½Ø “S$҄剸¯›œ×ým#(šåµ-—ó5`=¯ÛA’­A÷ƒézýpÉõzÑo(÷OTöOÊ0 Ã0 Ã0Ìû˜ ôýxJª¼÷»qõ-¹þÆ]ÛžÃÒÌÛâXTì|SOߪ¯B9í¨ô¼?:ã?¤ö—Nz½zàGÃ7ò¹ÙÓ™vÎIyÒžäØj[î÷÷ÊñÞkþKµ}_üÜ;.€g@E-Ù´³CÀ2V€õ¼riÿõ’e‚ ªÆˆH—g]PþSÛ_ õØÆÊÈ÷zÑq¿@|¯_Ÿ*Ïc20 Ã0 Ã0 óã pàG&ȹÅ­<Å'c‡‚»d†a†a†™àdý¹c£ï7·sÊ;cá¡_ËÁM9ÀÀ0 Ã0 Ã0ÌÎd`†a†a†a&8“a†a†a†a˜Pà Ã0 Ã0 Ã0 Ã0¡ÀA†a†a†a†aBƒ L8Д–¯LüÁ/w6ã_êuTãàª{ôcä©JlÙS‰j½™7aéa†a†af\É0ðcži*Ã[Íä|Ñ=McåF¼ô«ZD¨Hâ ¶‹ýyQñ*Ê6Ö €ÖGÛÐùûÿNŠ­ØÊ‡"_‹È‹ÏcF! #ÇÅÕS69pךQ±ÀÙ1Š+žD¢[obîkZ‰©rÝÝç=f¬cúÊÐ=eo£ª¡BJ’åë<2‡¤.GGª„s~ÝèlÞìÙV{ ç|¡ ÃãÀ'/¿†Ï´(ÈÉïØÇ;zûNÒ´¾E§»ñߟ×Á¿\UåC½Ø|òÿ«%ù ~G¯,D,¶)y!ŠÇЉƒ¯ßÄjᄟ{¾ ónÞz¡àÀ#Àqƒ]«÷,Æ,¹6†ØþŠìÐDÖvæÈlk©ç–=™ôXÛ“®Oõ ö5kÁDÆò< |ÛH}~AÏ[†a†a˜?Ye2ÌEî/A¼‡¶„Ãô«*tí݆í/oÃþÎ*ìx¶A–Ë ñ"¶q/g¢»{ª6mÓûLØÊ‡%'è¥PÉiqkƒ\¼–.LÊ: bÆÃ®ýw­©Ç¤Ž=z¿'À0­5yŒ 0dh‡t=â¥v)ôö\A ÊÖ¬NmÖ2½_R/ÎMQŒ6Œ­ÓË]k6aÆpwÊ>[;pâ5q?ÜZ€a¬N.e x{‡,åC‘Bâ÷î'}Ã5Ú©·ÉéeôyDºéS††#ò³…ï?©Êõª± äqÔ;VVŒ‚ÂÜsNË(²ÆqàÓí±êñP°éyÌþ8%ËAص´#çüeSQi¾©Ç¶ó͇‹øü`§\»Ð—óã&ÎxœŸÿ^g P†ÁÁÆ µ<”ìoÅ‚Y OÍ:¨.Æ\D°ÊP>#×¾Ãu½õvîxe—Z@Kóä©Jáˆ÷ùzîe/°v ´kS9‘žA·gøƒ.ôOÖ¹í}J~£ŸëŒ‚ÖÊJÌB–“óOK²‡Yc¬w ±O\‡ôÏ=у¯Ç¤³w¼è?ªœD×™Uõ.‡¿ÝÎP2u?y¾;ç—»åýxäº}Ô“6¹ s;8•Ôö4Ú#ìνý è¹|­ÑÆÅXý”dCj½¤¿Ç`G@½¦ûÍvµs˜×Q˳β³ý‘®Çþüd†a†a¬AÊR G)RóœpšV¡T6€JŸõâ‘SР°XË”©0Ð[ß} ßÎSçBÔE1•ÊgeÙ>`Hý\g1|‘iTt¶eb ´îØ¡°µÃídʲdñ[ôéOš»™ C‰–/û‡zKs>Ž ³ŠÌ8Žâ<¦`¹¥Þe3&áúÐ÷z+ˆ8.É1'ª°®>5“¡÷WêÕLGi®eîÍ&º®#Q^’ì¹¥^õY7ìyõ]]è//Cý-ð’K½yrùõuMÇ– )óXìºQ„Ÿ½À(©›îïYפËÕ§%Ù1†ËÚ–ú:7äj´‡zÚójÿ{‚ôh(Øp¨} …3½6ÎËToÊ}µÚÉXÈ¢^–ûÓig•õé ó:ªì¢,¦ÖÍðöëÉöùÉ0 Ã0 óÓ$p Ëä´•ø5^šÕ‚ßêÔw5]¡v¢ne Kï´‹¾©iöý™„ÜÖAš¦n$Lzˆœäj Gg¨Ëäô’V¹röÝ),ÝAÂã€r 0ñò*7<åmv Òôøt8h]¤giÜw|ªý’”:Tgà2ûùš¡éWA޹æ ï‰>ÿ´tÏx µAŸ@ü&ª ïÀ†AÝoâLì[5nƒÇagcæÊ5ÏÀÕžÏ$’r¬·hÈ3„ËÎÆ™ú£yPH/îÀ#8ÛÇ¢b÷|ÕÀqºU3 üHߤ§šHø¦|ÊRµµr0HB ¬'â£AüNMƧO·#º» øCL~2áOsw04Õ+<¤ãRœ_wŠJÍ 5#aÒ嵟ö//W¶Î·è±¯ÍNÂkSrÀ@¯~69ákS=à¡­Ô¹ÐÚ˜ÌÔã6èÏALöÈsÉ¡ý “=6=þë¢ÚØqºMzlõºç%tÐX Ζ†z?E•õ~‹Õ¥ßŸ~{(Äß®a]Çôž‡Ïát=^ìÏOIÚó–a†aæÇM`a~JÐŒE§»}ÓXR cùŒ¸í,)•NÚA£tG¯zs%;}Á 6ù¸À×=#â:2 Ã0 Ã0~d˜ŸÍïuË™+$ÕÅX…;` ¾¼<>ßxÕ›+ÅÎ\áëÎ0 Ã0 ÃüHàL†a†a†a†aB3†a†a†a† 20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀ„MQ™Í|ôw˜ž šÆ2U–+¤cUð<þ?yhšÃ=•¨Ö›y–†a†a†aî~lÀ3Mex«ù~ùì.D;¶á­ZþÊ*”cg÷îÀû]²pîT¼Š²53¨g3—¸­|(òµˆ¼ø·›üuŸ¨>'mÿ/Èð¼½kÍ&ÌîÎòYÌ0 Ã0 31±f2Ìݰ5ªÿÜKÏGÞl†_ žo&õ¼Ï¡óÝ]r³`Ó”ÍîÎ1PÕP’ìR=ÿ™'¹­|òæ?!òâ&à°©çÉ$ß&{« {µHOɰÎLÐvá9ï )•7dØÚA—K×ã‡Ú§ï&ëU˜l¦úëq3%“áfG fÈÌ {¯Û=_½XWa]ÓsX¡õ‹žL•×b‘L°È.ë…2œ7eÈ  7 Á&4­Ÿ ñ)(³¡V÷—ݹ¾C£%¨•Ýë*{¢¯®«Tw»Kÿ6´¸Ž2¶àjÆlÊZ‘L2QmŸLÓÝ‚íoŠ4¦Þì¡öDR.ŒÊλÌ(’e€!™ pŽ:”!ÒÓ‹ØôÙˆNVûÎ/)ņÆÙP-ªer]‘Zoýî(Ð5&êUu$Ú;ä¾úÝ‹…N’¤ë¨nZŒùçOãÏhÀÛ‹.E/öm½lÕ³zw)†® »ËiËs¾S¯»«ËµÓ¦Ç$¿¼b1–Ëm=²ßÖt®î1þs0ÙƒÚ߯[£í±êñÝ™ÏË!­=}úõÞo†ûÓ{¾&nõ::÷‰úÿ-nȼJ}NÚžÿŠìõdÆZ1›c(4ý_À0 Ã0 ó#ÁšÉpáàlß{F¼ ÇGf-sän»f–`løk±F/qä ·©Þê‘OflC‘ÌØl-–\Qˆ ªI,/¾ªe„I¾K¼$îÁÍ¥JNrôÉé§í²ÙÀÔ} e&TÜI£qD6iYÓ‡ˆ'Ój§Mµ¸·¢ßx ‚º'0c¸ÕøRì§B8ïª:jó5Î¹Ñ 2Õù¼ÐóqpÏp>ÞoÞ¦ï‘"µ¨Œ¿!ömÃþ6`Ñʽ#rØ6*Ç~îCr]Ž{PRˆ·åç ”Ù qÿ­Z_h•/#eb_5®ãxJDó{ªì½$R‚ê!•=ñ»P[wÌTPe) ¡ëðˆ“W¾Å”9÷ª:øìMj½|¬x¶AËÜߕܗE€ˆVßœòÚR„èÔ>™p¨]ì_1M9|ò3ÊËQ`¹pºˆˆpº¾Úß‹Dùlê@ìFJ—§¬q ¾2”wH¯·@Ô;¨Ê‹eta™;¡u«ÚîW…$ä¨nÙ£ÚY,–ë[š„ÈqŽ^ëÖËrÛ¤G2y6挨lˆäùzHµ“ôÎér²'N»Î²GϾ£cˆ>­Ï× ?ßìØBΰZwqs;8ÇÈ…ÚSŸ¯Íž\Ú?È›žúºáhk¹Î" >/Ej{®nœŽo=G‡´Ô^¯é~“-Ëýi#Œë˜¶ÿrA<ß×ÔàÛc›1¦% Ã0 Ã0?V‚?—(/âÕz"~Ë©ðéÌ޵ê)ï;|I˧6£+ÅKe x{{,åC‘§& œz›œ‚Ï#ÒMŸ2| 4‘Ÿ|øIU®W}»+£,…²bÖàžsZæsêÓí±êñP°Év-­ÀÈ9Y3Ý¢> 8ÃH¡¸Þ  ¨:åuH ndËE|~°S®]èË¿6NžŽ|‹ð(/œð8õÓïÆ”þîç ç㸹Û*Ïo€ÀŽël d͵ïp]¯A½;^Ù¥–ÇÐÒ[{R‘ŸULkK <8v%ëɪw4= ÏwÚýz‹ñAŽÑïpRo©Üˆõ5ÀÙ½N&ƒÚå‰ìö8´¡Biïäü;‹Î( nk½ÂŒµ‰ö¼õ;ò¶sI)`H›õAg1|‘Ål)ܵx h È æ‹óí×a¼ lY““SNÒgsßYå²gtpTü‚å¡ Ê8 e=ËfLÂõ¡ïõVq\’cQTa]}j&C1î¯Ô«™ŽÒ\ËÜ›Mt]G¢¼$ÙsK½ê³nØó.껺Ð_^†ú%Zà%—zóäòë1Ꚏ-Ræ±Ø9t£?{!8€QR7Ýß³®I—«OK²c —µ-õunϾÑêiÏ«ýSì Ò£¡`áö1ÎôÚ`8/S½)÷Õj'c!‹z}XîO§UHÔ§/Ì먲‹r š§ÿ¿6e¾Ñ3ŸÖç>Ã0 Ã0̉À),“ÓVâ×xiV ~+Sßé+=$Î`{ó½‘Þi}S1Òì ú3 ¹­‚4MÝH˜ô9ÉigªHá8ÓKZåÊÙw§°tk$Œ6Ê¿jÄë<á)o³S¦Ç§ÃAë"=Kã¾ãSí—È:.ùåÛÁ†í~èóOÏHS\.hÏ8î€iàG ü&ê8î 69!÷ ùÆQp§ÆÔ$ñ»÷F±Æ;M% &Y}#yœ¯Ž”w6ÎÄPSlº?Žàl[‹ŠÝvPÇéÖÎ0ð#}“ž:h"á›~ð)wJArÔÖÊÁ 5°žˆñ;5 ž>Ý:Œèî*à1ùÉ„?ÍÝÀÐT¯ðŽKq~Ý)*57Ô@Ž„I—×~Ú¿¼\Ù:ߢÇv¾6; ¯Mɽú=6Ú䄯Mõ€‡¶vPçBkc2SCŽÛ ?1Ù#Ï%‡ö'LöØôø¯‹jcçgfÒc«×=/¡ƒÆRp¦°4Ôû)ª¬÷[¬.ýþôÛC™ þv ë:¦ؘÝs2õy˜½žÔg2üÈ0 Ã0Ì—À ÃLdh6‰¢Ó)3U„C–õfœY"4–”J'í ÇQº#ŒW½¹’‡¾`…›|\àëž‘ q†a†a~BüÈ0šM'?©êb¬ÂÀ 0_^‡o¼êÍ•‰bg®ðug†a†a&œÉÀ0 Ã0 Ã0 Ã0L(p&Ã0 Ã0 Ã0 Ã0¡ÀA†a†a†a†aBƒ Ã0 Ã0 Ã0 Ęp )*sžwþö³³1eàGš¢2U–+¤cUð|ý?yh:Ã=•¨Ö›y–†a†a†aî~lÀ3Mex«ù€œë?Ú± obr(@IœÁv±?/*^EÙÆÈ™ÒÓæ7`+Š|-"/>…$TŒ{WOÙäÀ]k>DÅgÇ(®x‰n½é›/ÝÝç=&9çz`;ô”½ª† )I–¯óÈ’º©6ZðêJ³Ç‚¼'à™/>v6V`®\»‰3@ó ÜÈã– xhÙÇ;Z): eÙÂRü¾^YˆXmVòB¡_¿yG§êóÖŽ§\ŒYrm ±ý15Ø 'ˆ¬íÌ‘ñž1õ¼Â²'“k{Òõ©ľæa-˜Èž“ÏU–çm>ÏU†a†a˜ HV™ sQ…û‹Gï¡­*¬«'gr¶¿ü΢/m¨’årC¼ˆmœ‡ÄGÑÙü(º»ç¡jÓ6½Ï„­|Xr‚^ •œ $( òŠWQº`0)ë<6ˆ»ößµ¦“:öèýžÃ´Öä12À¡Òõ¬Ed)ôö\A ÊÖ¬NmÖ2½_RŒ¦(F›?ƈÖŒE&N¼&î‡[0,[63b½Ø°¿[,ùHOíè€?À@œ ½Ù‚x§¥W¢÷¡©D î0.ÃW!;ÐÙu½taŸÁi%gµ°½Cì;-–Z¿Ô;lXôdb¼Úçvóƒ¿îÛsÒö\µa}Þæù\e†a†™€X3ænØŽÆÕî¥ç#Í ¡rëq¿=Ø©%YB½:ócè|w—Ü,Øte³»ÅKPÕP’ìR=ÿ™'¹­|òæ?!òâ&à°§çIB™ &ù6Ù[Õ@zJ†uf‚€¶ ÏyT>Š«Í›õ¶ÆÖº\º?Ô>¥x7Y¯Âd3Õ_›¾L*çfi$3+<˜õ{©Âº¦ç°(Bë=™ * &¯Å"Ùy7‚³{wàý.¹Ó e,8oÊ>n‚M.hZ?8âRPfC­,î/»s}!†FKP+»×UöD_]V©îv—þlhq- dlÁՌ٠”´"ÙªÚ>™¦»Ûß7hL½ÙCíˆ.¤\• w™P$ËC2à;u (/B¤§±é³¬ö_RŠ ³¡ZTËäº"µÞúÝQ kLÔ«êH´wÈ}õ» $I×QÝ´óÏŸÆŸ?з]‹^ìÛzÙªgõîR ]v—Ó–ç|5¦^wW—k§MI~yÅb,—Ûz:e¾­è\Ýcüç`²9´¿_·FÛcÕã»2Ÿ—CZ{úô ê ¼ß ÷§÷|MÜêutîõÿ[ÜyezN:¤?WóÓ“Ís•a†afâbÍd¸pp¶ï=#^„ãór z¤sä0ƒ¹´j*†ús 0îšY‚±á¯Å½¸‘ƒÞ¦z~¨ç(™° E2[`³µ|XrE!fl<‚ª&±¼øª–&ù.\mÞƒ›K•œôÂH/´]6˜Ú ¡Ì„Šû1i4ŽÈ&-kúádZí´éñ±÷VâÛ”Õº'0c¸Õørë¥`Óóˆt;YNf…‹~x¿™²ZèI!R‹Êøbß6ìo­lÐ;Ò!‡ý`£rìç>T!×å¸%…x[~Þ@™ b÷ߪõ…Vù2R&öUã:ާdA4¿§Ê^ÐÛI"%¨RÙ¿‹µu÷ÈLU–ºO€8yå[L™s¯ª3€ÏÞ¤öÑËGÀŠg´Ìý]É}Yˆh%ðÍ)¯-EˆNí“Ù‡ÚÅþÓ”Ã'?3 Œ±– §‹ˆ§ë«ý½H”ÏFÑ©Än t‰pʧà+Cy‡ôz D½ƒª¼XF–ɱZ·ªí~UHBŽê–=Ê¡õÈb¹¾¥IØéçè5á°n½,·Mz$“gcΈʆHž¯‡T;Iïœ.'{â´ë,{ôì;:†èÓú| òóÍŽ-ä «u×7·ƒsŒ\¨=õùÚìÉ¥ýƒì±é©¯+޶–ë,’àóR¤¶çêÆéøÆÑstHKíõšî7вܟ6Â¸Ž·D–ÏÕÌdó\e†a†™¸.Q^ Ä/ªõD<-þ—Ï>‡Eñ–”ÀC.ÌŽõó¸ç rp=)¨§6£+…cM™ÞžKùP䇸½›Û7\£z›œ‚Ž“þ1ÐpD¦¿~øIU®WÝ £,…²bÖàžsZF”5NÀ"Ý«$˜1üqJ–ƒ°kiFÎù˦³ …³»E}ö]³þ\¸ˆÏu†Ë…þ¸ükãäÉéÈ·ò S?ýnLé¿á~Þp>Ž ‘»­òüLà¸ÎF ÀAÖ\û×õjÔÛ¹ã•]jqÆ2É—§*…#Þçë¹—½ÀÚ1“Q9‘žA·gøƒ.ôOž"Wí}J~£ŸëŒ‚ÖÊJÌB–“óOK²‡Yc¬w ±O\‡ôÏ=у¯Ç¤³w¼è?*œ>rü’άªw9ü=èv†’©ûÉóu0Ø9¿Ü-ïÇ#×í£î›Ü†¹œÀJj{ívçÞþô\¾V€hãb¬~J ²!µ^Òßc°# ^Óýf»?ƒÚ9Ìë(ƒè9}Þe~®æ®'Œç*Ã0 Ã0Ìka]“rˆ"5Ï çhÊ#µ¾Ùd xñ™¬{_Sù~` VJÇZ¾lQO¿Ú¥ÅHï¨^·—KžÊØ9swURN½ZhÓNú.\=Ð,xBí3ÑÇØh›ª“8ÃHaqÖö¤"?«˜Ö–xpìJÖ“'VýŒ :F¿ÃI½i¤r#Ö×g÷:™ :h—'²7ÚãІ ¥½“óï,:£€¸­õ g0Ö>&ÚóÖgìÈÛÎ%¥ðŒíêb“gB÷ØB¸™ž+aµ¿E“µp®Z² 6„Qï%ìëèÀÏU†a†a˜¬±(~ÛˆƒA:Fò³ ÕcC=³2Àï¬9ÙèN¾´<\ƒ‚Þ˜\§OÊЊ«ï¶ ê³kù°ä>T¯•úŒÁ‹GNAƒÂb-P¦Âh@o}÷%|[8O QÅT*Ÿ•=~dûЋjÚè亷í‹lF-ÿ7G+ŒƒÙõ”-0krrÊIú¬bnâ;«\öŒŽŠ¿S°<”A'¡Ì¢gÙŒI¸>ô½Þ "ŽKr, 455“¡÷WêÕLGi®eîÍ&º®#Q^’ì¹¥^õY7ìyõ]]è//Cý-ð’K½yrùõuMÇ– )óXìºQ„Ÿ½À(©›îïYפËÕ§%Ù1†ËÚ–ú:·gßhõ´çÕþ)öéÑP°áPû gzm0œ—©Þ”ûjµ“±E½>,÷§ÓÎ* $êÓæuTÙEÙN¹k®æ¢çõ\e†a†¹Na™œ¶¿ÆK³ZÔàŽ•ñÒ¯jÝA¿$ÞÿrÀ4¥—”Ñ(Üú3 ¹­‚4MÝH˜ô9ÉÕ@ŽÎP—î ˆ6¹zit§°tk$Œ6z§ªô–·Ù)HÓãÓá u‘ž¥qßñ©öK‚Îùt½]¿š†qäØjrZÓ>ÿôŒ4Åå‚öŒ™/¦)€ð›¨ã\¸ƒ6Úä„ÜW4äGÁS“ÄïÞÅï4•4˜dõäq¾:R~ÜÙ8CYL±éü8‚³mq,*vÛA §¯@†é›ôÔA ßôƒO¹S ’£¶VI¨õä@|4ˆß©iØð4ðéÖaDwWˆÉO&üiîz倇t\ŠóëNQ©¹¡r$Lº¼öÓþååÊÖù=¶óµÙIxmJèÕï±Ñ&'|mª<´µƒ:Z“™rÜý9ˆÉy.9´?a²Ç¦Ç]T;?3“[½îy 4–‚3…¥¡ÞOQe½ßbué÷§ßÊñ·kX×Qýî¼6<'ÏUEÖz#Çç*Ã0 Ã0ÌÄ%0ÈÀ0šM¢ètÊL!AÁåC½g–%¥ÒI;èq”îãUo®äa§/XáÁ&øºgdB\G†a†a˜ŸÁ?2̆f“ÀCÅÉO*B£º«0pç Ä——ÇÇá¯zse¢Ø™+|݆a†a˜ g20 Ã0 Ã0 Ã0  œÉÀ0 Ã0 Ã0 Ã0L(pa†a†a†a˜Pà Ã0 Ã0 Ã0 Ã0¡ÀA&hŠÊ¬ç¿sìlLø‘¦¨L•å éX<_ÿOšÎpO%ªõfÞ„¥‡a†a†a˜;B†ðLSÞj> çúvlÃ['ª°®é9,Šè"‰3Ø.öçEÅ«îÜáΜäAØÊ‡"_‹È‹ÏcF! #ÇÅÕS69pךQ±ÀÙ1Š+žD¢[oúæKw÷yëØƒ¾Ã‡2´ƒAOÙÛ¨j¨’dyšËÝ‘9$u9:Rm´àÕ•f 2<Ï|ñù³³±såÚMœùãšåFΧ°¤ÁC@Ëþ8ÞÑ¢ (HÑa(Ζâ÷õÊBÄòh³’¢x 8øúÍ;:UŸ·Þ@(8ðpÜ8µàbÌ’kcˆí¡¨1Àþ=AdmgŽŒ÷´ˆ©ç–=™ôXÛ“®Oõ ö5kÁDÆðœ |®Ú°‰×⥠U²\nˆ±ó8ð(:›Ew÷Q±='mÏUÖçmžÏU†a†a˜ ˆ5“aî†íh¬Qýç^z>¢l½•ÕPÜê•e õêÌ¡óÝ]r³`Ó”Íî/qBkCI²Hõü gžä¶òaÈ›ÿ„È‹›€Ãžž' e2˜äÛdotVé)Ö™ Ú.<ç TPù(®6oÖÛ[;èrézüPû”âÝd½ “ÍT=nú2¨œ›¥‘̬ð`ÖïÅ›Ùrѓɠ²`âñZ,’w#8»wÞï’;­P†À‚ó¦ìƒdꌛ…`“ šÖÏŽøƒ”ÙP+‹ûËî\_ˆ¡ÑÔÊîu•=ÑWWUª»Ý¥Z\G‹[p5c6e­Hv†ª6‚O¦énÁö7Å šSoöP{¢ )FeHç]fÉ2À̸FÇNÊ‹ééElúlD'«}ç—”bCãl¨Õ2¹®H­·~wèõª:ír_ýîÅB'IÒuT7-Æüó§ñç´@àíE—Ç¢û¶^¶êY½»CׄÝå´å9_©×ÝÕåÚiÓc’_^±Ë嶇žNÙƒok:W÷ÿ9˜ìAíï×­ÑöXõøî‡ÌçåÖž>ý‚€zï7Ãýé=_·zûDýÿ7d^™ž“éÏÕüôdó\e†a†™¸X3.Üí{ψ$áø¼Ü‚é9ÁJóÞ…¯ç2Þ’{€Ap×ÌŒ -ÖèÅô6ÕóC=Gɬ€m(’Ù›­åÃ’+ 1cãT5‰åÅWµŒ0ÉwájóÜ\ªä Fzy¤í²ÙÀÔ} e&TÜI£qD6iYÓ‡ˆ'Ój§Mµ¸·¢ß¤¼¨Ö=íƗ[/›žG¤ÛÉ’p2+¼Xôûp2[èI!R‹Êø2ãe°heƒÞ‘9ì•c?÷¡ ¹.Ç=()ÄÛòóÊl‹¸×V­/´Ê—‘2±¯×q<% ¢ù=Uö‚ÞN)AõÊžø] ¨­»Gf*¨²Ðux ÄÉ+ßbÊœ{U|ö¦›ù³ý#`ų Zæþ®ä¾, D´øæ”×–"D§öÉì€CíbÿŠiÊᓟPÆ€XŽË…ÓED„ÓõÕþ^$Êg£èTb7 PºD8eSð•¡¼Cz½¢ÞAU^,£ ËäØ ­[Õv¿*$!GuËåÐÎzd±\ßÒ$ìô@ŽsôšpX·^–Û&=’ɳ1gDeC$Ï×Cª¤wN—“=qÚu–=zöCôi}¾ùùfÇr†Õºëˆ›ÛÁ9F.Ôžú|möäÒþAöØôÔ×G[ËuIðy)RÛsuãt|ãè9:¤¥özM÷› hYîOa\Ç["Ëçjf²y®2 Ã0 ÃL\‚?—(/âÕz"îé©9†·´cô Va‡pšòcžp¬ŸÇ=_ƒëIA=µ}X)kÊðöü[ʇ"?„ÄïݴؾáíÔÛäpœô†#2ýõûÃOªr½jìye)”£ °÷œÓ2 ¤¬qéöXõx  ÁŒáS²„]K+0rÎ_6m(œÝ-곿èšõçÂE|~°S®]èË¿6NžŽ|‹ð(/œð8õÓïÆ”þîç ç㸹Û*Ïo€ÀŽël d͵ïp]¯A½;dPN,? ¥yòT¥pÄû|=÷²X;†ƒcò/*§ Ò3èö Ð…þÉSäj¢½OÉoôâsQÐZY‰Y(ÂrrþiIö0kŒõŽ!ö‰ëþ9 'zðõ˜töŽ÷ýG…ÓGŽ_Ò™Uõ.‡¿ÝÎP2u?y¾;ç—»åýxäº}Ôýc“Û0·ƒXImO£=ÂîÜÛß@€žË× m\ŒÕOiA6¤ÖKú{ vÔkºßl÷gP;‡ye=§Ï»ÌÏÕÜõ„ñ\e†a†ùac 2¬kRQ¤æ9á­By¤Ö8{Àgâ2½•=ß ¢`ÁJéXË—-êéW»4£éÕëöòaÉS;gî®JÊ©W mÚIß…«Ú€O¨}&úâmSu§b),ÎÚžTägÓÚÒŽ]ÉzòĪŸq¡@Çèw8©7TnÄúàì^­ð‘Úå‰ìö8´¡Biïäü;‹Î( nk½ÂŒµ‰ö¼õ;ò¶sI)(C+®¾Û 4¨Ï ¬åÃ’ûP½Vê3/9 ‹µ\@™ £½õÝ—ðmáf×?P¶À¬ÉÉ)'鳊¹‰ï¬rÙ3:8*þNÁòPeœ„2‹že3&áúÐ÷z+ˆ8.ɱ(ª°®>5“¡÷WêÕLGi®eîÍ&º®#Q^’ì¹¥^õY7ìyõ]]è//Cý-ð’K½yrùõuMÇ– )óXìºQ„Ÿ½À(©›îïYפËÕ§%Ù1†ËÚ–ú:·gßhõ´çÕþ)öéÑP°áPû gzm0œ—©Þ”ûjµ“±E½>,÷§ÓÎ* $êÓæuTÙEÙN¹k®æ¢çõ\e†a†¹Na™œ¶¿ÆK³Zð[™úNã1¬‚KKh{CËóÀ4¥—”Ñ(Üú3 ¹­‚4MÝH˜ô9ÉÕ@ŽÎP—î ˆ6¹zit§°tk$Œ6z§ªô–·Ù)HÓãÓá u‘ž¥qßñ©öK‚Îùt½]¿ÿý ‘Óšöù§g¤).´gwÀ4ð#~uœ wÐF›œûІ|ã(¸ScjƒøÝ{£X㦒“¬¾‘<ÎWGÊÀ;gb(‹)6ÝGp¶-ŽEÅn;¨ãôÈ0ð#}“ž:h"á›~ð)wJArÔÖÊÁ 5°žˆñ;5 ž>Ý:Œèî*à1ùÉ„?ÍÝÀÐT¯ðŽKq~Ý)*57Ô@Ž„I—×~Ú¿¼\Ù:ߢÇv¾6; ¯Mɽú=6Ú䄯Mõ€‡¶vPçBkc2SCŽÛ ?1Ù#Ï%‡ö'LöØôø¯‹jcçgfÒc«×=/¡ƒÆRp¦°4Ôû)ª¬÷[¬.ýþôÛC™ þv ë:¦Øðœ4>WYë9ŒŸ« Ã0 Ã0—À ÃLdh6‰¢Ó)3U„C–õfœY"4–”J'í ÇQº#ŒW½¹’‡¾`…›|\àëž‘ q†a†a~BüÈ0šM'?©êb¬ÂÀ 0_^‡o¼êÍ•‰bg®ðug†a†a&œÉÀ0 Ã0 Ã0 Ã0L(p&Ã0 Ã0 Ã0 Ã0¡ÀA†a†a†a†aBƒ Ã0 Ã0 Ã0 Ęp )*³žwþα³1eàGš¢2U–+¤cUð|ý?yh:Ã=•¨Ö›y–†a†a†aî~lÀ3Mex«ù€œë?Ú± oлP…uMÏaQä¢gŽð©xÕ;Ü™“<[ùPäkyñyÌ($¡b䨣¸zÊ&îZó!*8;FqåÀ“HtëMß|éî>ï1c{ÐwøP†v0è){U R’,Os¹;2‡¤.GGª¼ºÒì±@A†Ç‘ÿ½àagcæÊµ›8óÇ4Êœ1NaI‚‡€–ýq¼£EAP¢ÃP6œ),Åïë•…ˆåÑf%/Dñ:qðõ›wtª>o½Ppàà¸qjÁŘ%ׯÛCQc€ýz‚ÈÚÎïiSÏ+,{2é±¶']ŸêAìkÖ‚‰Œá9ø\µayÞæó\e†a†™€d•É0U¸¿xñ-Ìݰ‹âáåˆxÛ8‰¢³ùQtwÏCÕ¦mzŸ [ù°ä½*9-HP䯢tÁ`RÖyl3ví¿kM=&uìÑû=†i­Écd€!C;¤ëY‹ÈRèí=¸‚”­Y œÚ¬ez9&*¾¤MQŒ6Œ­3‹þLœx ÛC0,[63b½Ø°¿[,ùHOíè€?À@œ ½Ù‚x§¥W¢÷¡©D î0.ÃW!;ÐÙu½taŸÁi%gµ°½Cì;-–Z¿Ô;lXôdb¼Úçvóƒ¿îÛsÒö\µa}Þæù\e†a†™€X3ænØŽÆÕî¥ç£mx«g#^úU1>¹Ñ<{be¯Îü:ßÝ%7 6AÙìnñT5”${€TÏ¿pæIn+†¼ùOˆ¼¸ 8ìéy’P&ƒI¾MöVAg5ž’a™  íÂsÞ@•âjóf½­±µƒ.—®ÇµO)ÞMÖ«0ÙLõ×ã¦/“ʹYÉÌ fý^œŒZ÷fµ¨,˜x¼‹dçÝÎîÝ÷»äN+”!°à¼)û@V H¸Y6¹ iýLàˆ?HA™ µ²¸¿ìÎõ…-A­ì^WÙ}uX¥ºÛ]ú°¡Åu´(±W3f3PЊdg¨j#ødšîlSÜ 0õfµ ºraTv€tÞe@‘, Él€ktìÔ1 ¼‘ž^ĦÏFt²Úw~I)64ΆjQ-“ëŠÔzëwG®1Q¯ª#ÑÞ!÷Õï^,t’$]GuÓbÌ?þ@ Þ^ty,z±oëe«žÕ»K1tMØ]N[žóÕ˜zÝ]]®6=&ù勱\n{èé”=ø¶v suñŸƒÉäÐþ~ÝmUï~È|^iíéÓ/¨7ð~3ÜŸÞó5q«×ѹOÔÿoqCæ•é9éþ\ÍOO6ÏU†a†a˜‰‹5“áÂÁؾ÷ŒxAŽÏË-è‘Î}.!œÊ¿®ÅÐG·Ök}×ÌŒ -ÖèÅô6ÕóC=Gɬ€m(’Ù›­åÃ’+ 1cãT5‰åÅWµŒ0ÉwájóÜ\ªä Fzy¤í²ÙÀÔ} e&TÜI£qD6iYÓ‡ˆ'Ój§Mµ¸·¢ß¤¼¨Ö=íƗ[/›žG¤ÛÉ’p2+¼XôûèÄûÍÛô=’B¤•ñ7ľmØß,ZÙ w¤CûÁFåØÏ}¨B®ËqJ ñ¶ü¼2ÄrXµ¾Ð*_FÊľj\Çñ”,ˆæ÷TÙ z;I¤ÕC*{âw1 ¶î™© ÊR@B×á 0'¯|‹)sîUuðÙ›Ô>zùXñlƒ–¹¿+¹/‹­¾9嵥ѩ}2;àP»Ø¿bšrøäg”1 –£Àrátát}µ¿‰òÙ(:ÕØ”.NYã|e(ï^o¨wP•ËèÂ29vBëVµÝ¯ IÈQݲG9´³Y,×·4 ;=ã½&Ö­—å¶IdòlÌQÙÉóõj'éÓådOœvež}GÇ}ZŸ¯A~¾Ù±…œaµî:âævpŽ‘ µ§>_›=¹´=6=õuÂÑÖrE|^ŠÔö\Ý8ß8zŽi©½^Óý&Z–ûÓF×ñ–Èò¹š™lž« Ã0 Ã0—àÏ%Ê‹øEµžˆË ‚úL¢Å36í0O8ÖÏãž/ÈÁõ¤ žÚŒ>¬Ž5e x{þ-åC‘Bâ÷nZlßpvêmr 8NúÇ@Ùþúýá'U¹^5vƒ<޲ÊŠQPXƒ{ÎiRÖ8‹t{¬z?Ø)×.ôÇå_'OHG¾Ex”NxœúéwcJÿ ÷ó†óq\ˆÜm•ç7@`Çu6²æÚw¸®Wƒ ÞίìRËãhiž_ϽìÖŽáàÀ˜ü‹Ê)ˆô º=Ãt¡ò¹šhïSò½ø\g´VVbаœœZ’=Ìc½cˆ}â:¤è‰|=&½ã=@ÿQáô‘ã—tfU½ËáïA·3”LÝOž¯ƒÁÎùåny?¹nuÿØä6ÌíàVRÛÓh°;÷ö7 çòµDcõSZ ©õ’þƒõšî7ÛýÔÎa^GDÏ)Ïü\Í]OÏU†a†a˜6Ö Ãº&åEjžÎÑ*”Gj…´ K«¦«´Ó$äx+Äú3©ã²åûA,X)kù²E=ýj—f#½£zÝ^>,y*cçÌÝUI9õj¡M;é»põ@°à µÏD_c£mªNâT #…ÅYÛ“Šü¬bZ[ZàÁ±+YOžXõ3.èý'õ¦‘ÊX_œÝëd2è ]žÈÞhC*”öNο³èŒâ¶Ö+œÁXû˜hÏ[Ÿ±#o;—”Â3¶«‹Mž qÜc áfxzü­„Õþ=NÖ¹jÈ&ØF½w”°¯£?W†a†a²Æd øým#r éu·©“ï,ÞÏ(ÔqYCN6º“/m×  7&×éS2´âê»­@ƒú¬ÀZ>,¹Õk¥>cðâ‘SР°XË”©0Ð[ß} ßÎSçBÔE1•ÊgeÙ>ô¢š6:¹îmû"›QË¿ÆÍÑ ãàcvýãe Ìšœœr’>«˜›øÎ*—=£ƒ£âï,ePÆI(³èY6c®}¯·‚ˆã’‹¢ ëêS3Šq¥^Í„p”æàZæÞl¢ë:å%Éž[êUŸuÞwQßÕ…þò2Ô/Ñ/¹Ô›'—_áP×tlÉ2ˆÅΡEøÙ ÁŒ’ºéþžuMº\}Z’c¸¬m©¯s{ööPO{^íŸbO µ¡p¦×Ãy™êM¹¯V; YÔëÃr:í¬²@¢>}a^G•]”í”»öçj.z~PÏU†a†a˜ÛHà–Éi+ñk¼4«¿Õ©ï.ùOÁ'1Mé%e4 ·þLBnë MS7&=DNr5£3Ô¥;¢M®^Ý),ÝÁ ã€Þ©*½åmv Òôøt8h]¤giÜw|ªý’ s>]o×o„îÊhñ8ƒíÍ}þ{ƒ¦¸\ОqÜÓÀ@øMÔq.ÜAmrBî+ò£àN©I âwïbwšJL²úFò8_)?îlœ‰¡,¦Øt~ÁÙ¶8»í ŽÓW ÃÀôMzê ‰„oúÁ§Ü)ÉQ[+ƒ$ÔÀzr >ÄïÔ4lxøtë0¢»«€?Ää'þ4wwCS½rÀC:.Åùu§¨ÔÜP9&]^ûiÿòreë|‹ÛùÚì$¼6% ôê÷Øh“¾6ÕÚÚA ­ÉL 9nƒþÄd<—ÚŸ0ÙcÓã¿.ªŸ™I­^÷¼„KÁ™ÂÒP裡²Þo±ºôûÓoe‚øÛ5¬ë˜>`cÀsÒø\Ud­ç0r|®2 Ã0 ÃL\ƒ 3‘¡Ù$ŠN§ÌT Y>Ô›qf‰ÐXR*´ƒGéŽ0^õæJvú‚lòq¯{F&Äud†a†ù <ð#ÃL`h6 ùZD^|3 I¨9ö(®ž²É»Ö|ˆŠÎŽQ\9ð$ÝzÓ7_º»Ï{ÌXÇô>”¡ zÊÞFUC…”$ËÓ\îŽÌ!©ËÑ‘j£¯®4{,Páqxæ‹ÏŸ˜+×nâÌÐ<(7rÆ8…%ZöÇñŽAAŠCÙp¦°¿¯W"–G›•¼ÅcèÄÁ×oÞÑ©ú¼õBÁG€ãÆ©c–\Cl Eöè "k;sd¼§EL=¯°ìɤÇÚžt}ª±¯yX &2†çdàsÕ†åy›Ïs•a†af’U&Ã\TáþâÄ{´#8»w¶¿¬–¼ ô"¶qEgó£èªMÛô>¶òaÉ z)TrZ( 0È+^Eé‚Á¤¬óØ f<ìÚךzLêØ£÷{ ÓZ“ÇÈC†vH׳‘¥ÐÛ{p5([³8µYËôrLT8|I0š¢mþX\µl°èÏĉ׼pë†eËfbF¬öw‹%ÿé©ðˆóq¡7»Cï´tãJô>4•hÁæÁ…cø*d:²®÷ƒ.ì38­ä¬¶wˆ}§ÅCë—z‡ ‹žLŒWûÜn~ð×}¢b{NÚž«6¬ÏÛ<Ÿ« Ã0 Ã0k&ÃÜ ÛÑX£úϽlù ¬kÚüÛx¿K ózuæÇÐùî.¹Y°éÊfw‹—8 ª¡$Ù¤zþ…3Or[ù0äÍBäÅMÀaOÏ“„2Lòm²· :«ô” ëÌmžó*¨|W›7ëm­t¹t=~¨}Jñn²^…Éfª¿7}™ TÎÍÒHfVx0ë÷âÍl¹èÉdPY0ñx-ÉÎ; Le¾g(C`ÁySö“:“p³lrAÓú™À‚2jeqÙë 14Z‚ZÙ½®²'úê*°Ju·»ô`C‹ëhQ c ®fÌf , ÉÎPÕFðÉ4Ý-Øþ¦¸A3`êÍj/@t!å¨ìé¼Ë,€"Y’Ù×èØ©c@y"=½ˆMŸèdµïü’Rlhœ Õ¢Z&שõÖïŽ]c¢^UG¢½Cî«ß½Xè$IºŽê¦Å˜þ4þü¼½èòXôbßÖËV=«w—bèš°»œ¶<ç«1õº»º\;mzLòË+c¹ÜöÐÓ){ðmí@çêã?“=È¡ýýº5Ú«ßýù¼ÒÚÓ§_Poàýf¸?½çkâV¯£sŸ¨ÿßâ†Ì+ÓsÒ!ý¹šŸžlž« Ã0 Ã0k&Ã…ƒ;°}ïñ‚$Ÿ—[Ð##§‡}*ýjv¼" 8äÁ]3K06üµX£7rÐÛTÏõ%³¶¡Hf l¶–K®(ÄŒGPÕ$–_Õ2Â$ß…«Í{ps©’S €^éå‘¶Ëf‹VjÐÇPfBÅý˜4Gd“–5}ˆˆp2­vÚôøX‹{+ ñí@Ê‹jݘ1Üj|¹õR°éyDº, '³Â‹E¿N¼ßL-t¤©Eeü ™í²¿ X´²AïH‡öƒÊ±ŸûP…\—ã”âmùye6ˆå°j}¡U¾Œ”‰}Õ¸Žã)YÍ全ôv’H ª‡TöÄïb@mÝ=2SA•¥€„®Ã` N^ùSæÜ«ê à³7ݬŸí+žmÐ2÷w%÷e` ¢•À7§¼¶!:µOfjûWLSŸüÌ€2ÄrX.œ.""œ®¯ö÷"Q>E§:»Q€Ò%Â)kœ‚¯ åÒë-õªòb]X&ÇNhݪ¶ûU! 9ª[ö(‡vÖ#‹åú–&a§rœ£×„úõ²Ü6é‘Lž9#*"y¾Rí$½sºœì‰Ó®³ìѳïè¢Oëó5ÈÏ7;¶3¬Ö]GÜÜÎ1r¡öÔçk³'—ö²Ç¦§¾®@8ÚZ®³H‚ÏK‘Úž«§ãGÏÑ!-µ×kºßd@ËrÚã:ÞY>W3“Ís•a†afâü¹Dy1¿¨ÖqÝSã8•jù$^‹ÂiÊy±~÷|A®'õÔfôa¥p¬)SÀÛóo)Šü¿wÓbû†k´So“SPÀqÒ?ŽÈô×ï?©Êõª±äq”¥PVŒ‚ÂÜsNË(²Æ X¤ÛcÕã‚3†?NÉrv-­ÀÈ9Ùt¶¡pv·¨Ïþ¢kÖŸ ñùÁN¹v¡?.ÿÚ8yr@:ò-£¼pÂãÔO¿Súo¸Ÿ7œãBän«<¿8®³(p5×¾Ãu½õvÊ€-? ¥yòT¥pÄû|=÷²X;†ƒcò/*§ Ò3èö Ð…þÉSäj¢½OÉoôâsQÐZY‰Y(ÂrrþiIö0kŒõŽ!ö‰ëþ9 'zðõ˜töŽ÷ýG…ÓGŽ_Ò™Uõ.Ï–T×ÌP2u?y¾;ç—»åýxäº}Ôýc“Û0·ƒXImO£=ÂîÜÛß@€žË× m\ŒÕOiA6¤ÖKú{ vÔkºßl÷gP;‡ye=§Ï»ÌÏÕÜõ„ñ\e†a†ùac 2¬kRQ¤æ9á­By¤Ö8{Àg:‘#ß ¢`ÁJéXË—-êéW»4£éÕëöòaÉS;gî®JÊ©W mÚIß…«Ú€O¨}&úâmSu§b),ÎÚžTägÓÚÒŽ]ÉzòĪŸq¡@Çèw8©7TnÄú¸c˜|”ßïÅAöF{ÚP¡´wrþEg·µ^á ÆÚÇD{ÞúŒyÛ¹¤ž±]]lòLˆã[7sÀÓão%¬ö·èq²ÎU«@@6Á†0ê½£„}ø¹Ê0 Ã0 “5Ö e+ìo‘3GHÇH~6‘ÚcS…uõ ád;ä9ÙèN¾´<\ƒ‚Þ˜\§OÊЊ«ï¶ ê³kù°ä>T¯•úŒÁ‹GNAƒÂb-P¦Âh@o}÷%|[8O QÅT*Ÿ•=~dûЋjÚè亷í‹lF-ÿ7G+ŒƒÙõ”-0krrÊIú¬bnâ;«\öŒŽŠ¿S°<”A'¡Ì¢gÙŒI¸>ô½Þ "ŽKr, õ{ñSŒû+õj&„£4×2÷f]ב(/IöÜR¯ú¬ö¼‹ú®.ô——¡~‰xÉ¥Þ<¹üz ‡º¦cK†”ù@,vÝ(ÂÏ^`”ÔM÷÷¬kÒåêÓ’ìÃemK}Û³o´‡zÚójÿ{‚ôh(Øp¨} …3½6ÎËToÊ}µÚÉXÈ¢^–ûÓig•õé ó:ªì¢l§Üµ?WsÑóƒz®2 Ã0 ÃÜF§°LN[‰_ã¥Y-ø­L}§i÷VAŽ¥%H´½¡åy`šÒKÊhný™„ÜÖAš¦n$Lzˆœäj Gg¨KwD›\½4ºSXºƒ5ƽSUzËÛì¤éñépкHÏÒ¸ïøTû%Aç|ºÞ®ßˆÿ~$Î`{sŸzFšârA{ÆqL?Rá7Qǹpm´É ¹¯hÈ7Ž‚;5¦&1ˆß½7Š5Þi*i0ÉêÉã|u¤ ü¸³q&†²˜bÓøqgÛâXT춃8N_ ?Ò7驃&¾éŸr§$Gm­ ’PëÉøh¿SÓ°áiàӭÈî®þ“ŸLøÓÜÝ MõÊé¸ç×¢RsC äH˜tyí§ýËË•­ó-zlçk³“ðÚ”0Ыßc£MNøÚTxhku.´6&35ä¸ ús“=ò\rhÂdMÿº¨6v~f&=¶zÝó:h,g KC½Ÿ¢Êz¿ÅêÒïO¿=” âo×°®cú€ÏIãsU‘µžÃÈñ¹Ê0 Ã0 3q 20ÌD†f“(:2SEHP0dùPoÆ™%BcI©tÒz¥;ÂxÕ›+yØé Vx°ÉǾî™בa†aæ'DðÀ 3¡Ù$ðPqò“ŠÐ¨.Æ* ܹñååñqøÆ«Þ\™(væ _w†a†af‚Á™ Ã0 Ã0 Ã0 Äg20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L(p š¢2ëyçï;S~¤)*Se¹B:VÏ×ÿ“‡¦3ÜS‰j½™7aéa†a†aæŽaàÇ<ÓT†·šȹþ£ÛðÖ µÇ;¿¢í üö`§\ωŠWݹÃ9Ƀ°•E¾‘ŸÇŒB*FŽ=Š«§lrà®5¢b³cW<‰D·ÞôÍ—îîó3Ö±}‡ehƒž²·QÕP!%Éò4—»#sHêrt¤ÚhÁ«+Í dxžùâóggcæÊµ›8óÇ4Êœ1NaI‚‡€–ýq¼£EAP¢ÃP6œ),Åïë•…ˆåÑf%/Dñ:qðõ›wtª>o½Ppàà¸qjÁŘ%ׯÛCQc€ýz‚ÈÚÎïiSÏ+,{2é±¶']ŸêAìkÖ‚‰Œá9ø\µayÞæó\e†a†™€d•É0U¸¿xñ½M†â¿Ãö—·É%¯½ˆmœ‡ÄGÑÙü(º»ç¡jÓ6½Ï„­|Xr‚^ •œ $( òŠWQº`0)ë<6ˆ»ößµ¦“:öèýžÃ´Öä12À¡Òõ¬Ed)ôö\A ÊÖ¬NmÖ2½_RŒ¦(F›?ƈÖŒE&N¼&î…[0,[63b½Ø°¿[,ùHOíè€?À@œ ½Ù‚x§¥W¢÷¡©D î0.ÃW!;ÐÙu½taŸÁi%gµ°½Cì;-–Z¿Ô;lXôdb¼Úçvóƒ¿îÛsÒö\µa}Þæù\e†a†™€X3¼™ ^¶¿Ü’wï«êÕ™Cç»»äfÁ¦#(›Ý-^‪†’dêùÎ<ÉmåÃ7ÿ ‘7‡==OÊd0É·ÉÞ*è¬ÒS2¬3´]xΨ òQ\mÞ¬·5¶vÐåÒõø¡ö)Å»Éz&›©þzÜôe2P97K#™YáÁ¬ßKÖ5=‡EZ¿èÉdPY0ñx-ÉλœÝ»ïwÉV(C`ÁySö¬@p³lrAÓú™À‚2jeqÙë 14Z‚ZÙ½®²'úê*°Ju·»ô`C‹ëhQ c ®fÌf , ÉÎPÕFðÉ4Ý-Øþ¦¸A3`êÍj/@t!å¨ìé¼Ë,€"Y’Ù×èØ©c@y"=½ˆMŸèdµïü’Rlhœ Õ¢Z&שõÖïŽ]c¢^UG¢½Cî«ß½Xè$IºŽê¦Å˜þ4þü¼½èòXôbßÖËV=«w—bèš°»œ¶<ç«1õº»º\;mzLòË+c¹ÜöÐÓ){ðmí@çêã?“=È¡ýýº5Ú«ßýù¼ÒÚÓ§_Poàýf¸?½çkâV¯£sŸ¨ÿßâ†Ì+ÓsÒ!ý¹šŸžlž« Ã0 Ã0k&Ã…ƒ;°}ïñ‚$Ÿ—[Ð#£m@eŠqÜ/œ¤¯Ð²ë*õA9p×ÌŒ -ÖèÅô6ÕóC=Gɬ€m(’Ù›­åÃ’+ 1cãT5‰åÅWµŒ0ÉwájóÜ\ªä Fzy¤í²ÙÀÔ} e&TÜI£qD6iYÓ‡ˆ'Ój§Mµ¸·¢ß¤¼¨Ö=íƗ[/›žG¤ÛÉ’p2+¼XôûèÄûÍ”ÑB÷H ‘ZTÆßÙ.ûÛ€E+ôŽtÈa?بû¹UÈu9îAI!Þ–Ÿ7PfƒXN«ÖZåËH™ØWë8ž’Ñüž*{Ao'‰” zHeOü.ÔÖÝ#3TY Hè:<âä•o1eνªÎ>{SeüÈå#`ų Zæþ®ä¾, DTüÞ¾9嵥ѩ}2;àP»Ø¿bšrøäg”1 –£Àrátát}µ¿‰òÙ(:ÕØ”.NYã|e(ï^o¨wP•ËèÂ29vBëVµÝ¯ IÈQݲG9´³Y,×·4 ;=ã½&Ö­—å¶IdòlÌQÙÉóõj'éÓådOœvež}GÇ}ZŸ¯A~¾Ù±…œaµî:âævpŽ‘ µ§>_›=¹´=6=õuÂÑÖrE|^ŠÔö\Ý8ß8zŽi©½^Óý&Z–ûÓF×ñ–Èò¹š™lž« Ã0 Ã0—àÏ%Ê‹øEµžˆ«ž!‹籸ÃqšâXô×e‘Ü™'ëçqÏäàzRPOmFV Çš2¼=ÿ–ò¡È!ñ{7-¶o¸F;õ69'ýc áˆLýþð“ª\¯»AGY eÅ((¬Á=ç´Œ)kœ€Eº=V=(H0cøã”,a×Ò Œœó—Mg gw‹úì/ºfý¹pŸëOi.ôÇå_'OHG¾Ex”NxœúéwcJÿ ÷ó†óq\ˆÜm•ç7@`Çu6²æÚw¸®Wƒ ÞNËãhiž_ϽìÖŽáàÀ˜ü‹Ê)ˆô º=Ãt¡ò¹šhïSò½ø\g´VVbаœœZ’=Ìc½cˆ}â:¤è‰|=&½ã=@ÿQáô‘ã—tfU½ËáïA·3”LÝOž¯ƒÁÎùåny?¹nuÿØä6ÌíàVRÛÓh°;÷ö7 çòµDcõSZ ©õ’þƒõšî7ÛýÔÎa^GDÏ)Ïü\Í]OÏU†a†a˜6Ö Ãº&åEjžÎÑ*”GjÅß_=q$g’@âD;z"Å9Ï*ðýÀ ¬”޵|Ù¢ž~µK3Š‘ÞQ½n/–<•±sæîª¤œzµÐ¦ô]¸z  Xð„Úg¢/ޱÑ6U'q*†‘Ââ¬íIE~V1­--ðàØ•¬'O¬ú tŒ~‡“zÓHåF¬¯Îîu‚r:h—'²7ÚãІ ¥½“óï,:£€¸­õ g0Ö>&ÚóÖgìÈÛÎ%¥ðŒíêb“gB÷ØB¸™ž+aµ¿E“µp®Z² 6„Qï%ìëèÀÏU†a†a˜¬±(~Ûz>ÒŽ‘ülâ5 «C‘*÷‰‡¢ÜÉrÈr²Ñ|i+x¸½1¹NŸ ”¡WßmÔgÖòaÉ}¨^+õƒœ‚…ÅZ. L…Ñ€ÞúîKø¶pž:¢.Š©T>+{üÈö¡Õ´ÑÉuoÛÙŒZþ5nŽV³ë([`Öä䔓ôYÅÜÄwV¹ì§`y(ƒ2NB™Eϲ“p}è{½D—äXâwSŸšÉPŒûßR&„£4×2÷f]ב(/IöÜR¯ú¬ö¼‹ú®.ô——¡~‰xÉ¥Þ<¹üz ‡º¦cK†”ù@,vÝ(ÂÏ^`”ÔM÷÷¬kÒåêÓ’ìÃemK}Û³o´‡zÚójÿ{‚ôh(Øp¨} …3½6ÎËToÊ}µÚÉXÈ¢^–ûÓig•õé ó:ªì¢l§Üµ?WsÑóƒz®2 Ã0 ÃÜF§°LN[‰_ã¥Y-î,•ñÒ¯jõ_ÞÁþrÄ4¥—”Ñ(Üú3 ¹­‚4MÝH˜ô9ÉÕ@ŽÎP—î ˆ6¹zit§°tk$Œ6z§ªô–·Ù)HÓãÓá u‘ž¥qßñ©öK‚Îùt½]¿š†qäØj‰3ØÞÜç ”¦¸\ОqÜÓÀ@øMÔq.ÜAmrBî+ò£àN©I âwïbwšJL²úFò8_)?îlœ‰¡,¦Øt~ÁÙ¶8»í ŽÓW ÃÀôMzê ‰„oúÁ§Ü)ÉQ[+ƒ$ÔÀzr >ÄïÔ4lxøtë0¢»«€?Ää'þ4wwCS½rÀC:.Åùu§¨ÔÜP9&]^ûiÿòreë|‹ÛùÚì$¼6% ôê÷Øh“¾6ÕÚÚA ­ÉL 9nƒþÄd<—ÚŸ0ÙcÓã¿.ªŸ™I­^÷¼„KÁ™ÂÒP裡²Þo±ºôûÓoe‚øÛ5¬ë˜>`cÀsÒø\Ud­ç0r|®2 Ã0 ÃL\ƒ 3‘¡Ù$ŠN§ÌT Y>Ô›qf‰ÐXR*´ƒGéŽ0^õæJvú‚lòq¯{F&Äud†a†ù <ð#ÃL`h6 þ€Þ¯IœÁvQ&g*^uçwæ$ÂV>ùZD^|3 I¨9ö(®ž²É»Ö|ˆŠÎŽQ\9ð$ÝzÓ7_º»Ï{ÌXÇô>”¡ zÊÞFUC…”$ËÓ\îŽÌ!©ËÑ‘j£¯®4{,Páqxæ‹ÏŸ˜+×nâÌÐ<(7rÆ8…%ZöÇñŽAAŠCÙp¦°¿¯W"–G›•¼ÅcèÄÁ×oÞÑ©ú¼õBÁG€ãÆ©c–\Cl Eöè "k;sd¼§EL=¯°ìɤÇÚžt}ª±¯yX &2†çdàsÕ†åy›Ïs•a†af’U&Ã\TáþâÄ{ÄÆ‰×°ýåmîòÑE Þ§ æ„xÛ8‰¢³ùQtwÏCÕ¦mzŸ [ù°ä½*9-HP䯢tÁ`RÖyl3ví¿kM=&uìÑû=†i­Écd€!C;¤ëY‹ÈRèí=¸‚”­Y œÚ¬ez9&*¾¤MQŒ6Œ­3‹þLÈûâÖ Ë–ÍÄŒX/6ìïKþÒS;:à0çãBov† Þi鯕è}h*Ñ‚;̃ ÇðUÈt6d]ï]ØgpZÉY-lïûN‹%†Ö/õ=™¯ö¹Ýüà¯ûDÅöœ´=WmXŸ·y>W†a†a& ÖL†¹¶£±FõŸ{éùhÞ:¡7P…uM»ïwiQ¶P¯Îü:ßÝ%7 6AÙìnñ'´6”${€TÏ¿pæIn+†¼ùOˆ¼¸ 8ìéy’P&ƒI¾MöVAg5ž’a™  íÂsÞ@•âjóf½­±µƒ.—®ÇµO)ÞMÖ«0ÙLõ×ã¦/“ʹYÉÌ fý^èx‹"´~ѓɠ²`âñZ,’w#8»7ó}B Λ²d‚„›…`“ šÖÏŽøƒ”ÙP+‹ûËî\_ˆ¡ÑÔÊîu•=ÑWWUª»Ý¥Z\G‹[p5c6e­Hv†ª6‚O¦énÁö7Å šSoöP{¢ )FeHç]fÉ2À̸FÇNÊ‹ééElúlD'«}ç—”bCãl¨Õ2¹®H­·~wèõª:ír_ýîÅB'IÒuT7-Æüó§ñç´@àíE—Ç¢û¶^¶êY½»CׄÝå´å9_©×ÝÕåÚiÓc’_^±Ë嶇žNÙƒok:W÷ÿ9˜ìAíï×­ÑöXõøî‡ÌçåÖž>ý‚€zï7Ãýé=_·zûDýÿ7d^™ž“éÏÕüôdó\e†a†™¸X3.Üí{ψ$áø¼Ü‚éy ‚‡VaQüïr0îšY‚±á¯Å½¸‘ƒÞ¦z~¨ç(™° E2[`³µ|XrE!fl<‚ª&±¼øª–&ù.\mÞƒ›K•œôÂH/´]6˜Ú ¡Ì„Šû1i4ŽÈ&-kúádZí´éñ±÷VâÛ”Õº'0c¸Õørë¥`Óóˆt;YNf…‹~x¿™²ZèI!R‹Êø2ãe°heƒÞ‘9ì•c?÷¡ ¹.Ç=()ÄÛòóÊl‹¸ÿV­/´Ê—‘2±¯×q<% ¢ù=Uö‚ÞN)AõÊžø] ¨­»Gf*¨²Ðux ÄÉ+ßbÊœ{U|ö¦7óXñlƒ–¹¿+¹/‹­¾9嵥ѩ}2;àP»Ø¿bšrøäg”1 –£Àrátát}µ¿‰òÙ(:ÕØ”.NYã|e(ï^o¨wP•ËèÂ29vBëVµÝ¯ IÈQݲG9´³Y,×·4 ;=ã½&Ö­—å¶IdòlÌQÙÉóõj'éÓådOœvež}GÇ}ZŸ¯A~¾Ù±…œaµî:âævpŽ‘ µ§>_›=¹´=6=õuÂÑÖrE|^ŠÔö\Ý8ß8zŽi©½^Óý&Z–ûÓF×ñ–Èò¹š™lž« Ã0 Ã0—àÏ%Ê‹øEµžˆ§ôÔTa]ýèéÈÎ123O8ÖÏãž/ÈÁõ¤ žÚŒ>¬Ž5e x{þ-åC‘Bâ÷nZlßpvêmr 8NúÇ@Ùþúýá'U¹^5vƒ<޲ÊŠQPXƒ{ÎiRÖ8‹t{¬z?Ø)×.ôÇå_'OHG¾Ex”NxœúéwcJÿ ÷ó†óq\ˆÜm•ç7@`Çu6²æÚw¸®Wƒ ÞίìRKêx&¹òT¥pÄû|=÷²X;†ƒcò/*§ Ò3èö Ð…þÉSäj¢½OÉoôâsQÐZY‰Y(ÂrrþiIö0kŒõŽ!ö‰ëþ9 'zðõ˜töŽ÷ýG…ÓGŽ_Ò™Uõ.‡¿ÝÎP2u?y¾;ç—»åýxäº}Ôýc“Û0·ƒXImO£=ÂîÜÛß@€žË× m\ŒÕOiA6¤ÖKú{ vÔkºßl÷gP;‡ye=§Ï»ÌÏÕÜõ„ñ\e†a†ùac 2¬kRQ¤æ9á­By¤Ö?{e1àŒ?³!¾DÁ‚•Ò±–/[ÔÓ¯viF1Ò;ª×íåÃ’§2vÎÜ]•”S¯Ú´“¾ W´ žPûLôÅ16Ú¦ê$NÅ0RXœµ=©ÈÏ*¦µ¥»’õä‰U?ãBŽÑïpRo©Üˆõ5ÀÙ½N&ƒÚå‰ìö8´¡Biïäü;‹Î( nk½ÂŒµ‰ö¼õ;ò¶sI),¹Õk¥>cðâ‘SР°XË”©0Ð[ß} ßÎSçBÔE1•ÊgeÙ>ô¢š6:¹îmû"›QË¿ÆÍÑ ãàcvýãe Ìšœœr’>«˜›øÎ*—=£ƒ£âï,ePÆI(³èY6c®}¯·‚ˆã’ü¤HýnüãþJ½š á(ÍÁµÌ½ÙD×u$ÊK’=·Ô«>ë†=« ýåe¨_¢^r©7O.¿á®éØ’!e>‹C7Šð³‚%uÓý=ëšt¹ú´$;ÆpYÛR_çöìí¡žö¼Ú?Åž = 6jCáL¯ †ó2Õ›r_­v2²¨×‡åþtÚYeD}ú¼Ž*»(Û)wíÏÕ\ôü ž« Ã0 Ã0·‘À),½ÓV¾4«¿Õ©ïrºÂúx~ÓVz1Mé%e4 ·þLBnë MS7&=DNr5£3Ô¥;¢M®^Ý),ÝÁ ã€Þ©*½åmv Òôøt8h]¤giÜw|ªý’ s>]o×o„¦a\9¶šƒœÒ´Ï?=#Ý3 Ú3Ž;`ø‘¿‰:Î…;h£MNÈ}EC¾qÜ©15‰Aüî½Q¬ñNSIƒIVßHç«#eàÇ31”Å›îÀ#8ÛÇ¢b·ÔÀqú dø‘¾IO4‘ðM?ø”;¥ 9jkå`„XOÄGƒøš† OŸnFtwð‡˜üdŸæî`hªWxHÇ¥8¿î•šj G¤Ëk?í_^®loÑc;_›„צä€^ýmrÂצzÀC[;¨s¡µ1™©!ÇmПƒ˜ì‘ç’Cû&{lzü×Eµ±ó33é±Õëž—ÐAc)8SXêýUÖû-V—~úí¡L»†uÓl xNŸ«Š¬õFŽÏU†a†a˜‰K`a&24›DÑé”™*B‚‚!ˇz3Î,KJ¥“vÐã(ÝÆ«Þ\ÉÃN_°ÂƒM>.ðuÏÈ„¸Ž Ã0 Ã0?!‚~d˜ Í&‡Š“ŸT„Fu1VaàΈ//Ã7^õæÊD±3Wøº3 Ã0 Ã0 Îd`†a†a†a&8“a†a†a†a˜Pà Ã0 Ã0 Ã0 Ã0¡ÀA†a†a†a†aBƒ L8ЕYÏ;çØÙ˜2ð#MQ™*ËÒ±*x¾þŸ<4ážJTëͼ KÃ0 Ã0 Ã0w„ ?6à™¦2¼Õ|@ÎõíØ†·N¤ÌíOsÿïÝ÷»ôf.T¼êÎîÌI„­|(òµˆ¼øRY ”áð`ü üö`§.‘%Ô«3?†ÎwwÉÍ‚MGP6»[¼ÄU %É Õó/œy’Ûʇ!oþ"/n{zž$”É`’o“½UÐY ¤§dXg&h»ðœ7PA壸ڼYoklí Ë¥ëñCíSŠw“õ*L6Sýõ¸éËd rn–F2³ÂƒY¿—*¬kz‹"´~ѓɠ²`âñZ,’wÙe¼P†À‚ó¦ìY áf!Øä‚¦õ3#þ e6ÔÊâþ²;×bh´µ²{]eOôÕU`•ênwéÀ†×Ñ¢@Æ\͘Í@¿‘ÉÎPÕFðÉ4Ý-Øþ¦¸A3`êÍj/@t!å¨ìé¼Ë,€"Y’Ù×èØ©c@y"=½ˆMŸèdµïü’Rlhœ Õ¢Z&שõÖïŽ]c¢^UG¢½Cî«ß½Xè$IºŽê¦Å˜þ4þü¼½èòXôbßÖËV=«w—bèš°»œ¶<ç«1õº»º\;mzLòË+c¹ÜöÐÓ){ðmí@çêã?“=È¡ýýº5Ú«ßýù¼ÒÚÓ§_Poàýf¸?½çkâV¯£sŸ¨ÿß(Xž5='ÒŸ«ùéÉæ¹Ê0 Ã0 3q±f2\8¸Û÷ž/HÂñy¹=Ò9¢öcxëå7¯ß…¯¨O(r0îšY‚±á¯Å½¸‘ƒÞ¦z~¨ç(™° E2[`³µ|XrE!fl<‚ª&±¼øª–&ù.\mÞƒ›K•œôÂH/´]6˜Ú ¡Ì„Šû1i4ŽÈ&-kúádZí´éñ±÷VâÛ”Õº'0c¸Õørë¥`Óóˆt;YNf…‹~x¿™²ZèI!R‹Êø2ãe3^Èa?بû¹UÈu9îAI!Þ–Ÿ7PfƒXN«ÖZåËH™ØWë8ž’Ñüž*{Ao'‰” zHeOü.ÔÖÝ#3TY Hè:<âä•o1eνªÎ>{SgýÈÌ`ų Zæþ®ä¾, D´øæ”×–"D§öÉì€CíbÿŠiÊᓟPÆ€XŽË…ÓED„ÓõÕþ^$Êg£èTb7 PºD8eSð•¡¼Cz½¢ÞAU^,£ ËäØ ­[Õv¿*$!GuËåÐÎzd±\ßÒ$ìô@ŽsôšpX·^–Û&=’ɳ1gDeC$Ï×Cª¤wN—“=qÚu–=zöCôi}¾ùùfÇr†Õºëˆ›ÛÁ9F.Ôžú|möäÒþAöØôÔ×G[ËuIðy)RÛsuãt|ãè9:¤¥özM÷› hYîOa\Ç["Ëçjf²y®2 Ã0 ÃL\‚?—(/âÕz"®{jTÏue'9-Àã»ðÒ†*¹'wæ ÇúyÜó9¸žÔS›Ñ‡•±¦LoÏ¿¥|(òCHüÞM‹í®ÑN½MNAÇIÿh8"Ó_¿?ü¤*׫ÆnÇQ–BY1 kpÏ9-£@Ê'`‘nU Ìþ8%ËAص´#çüeÓÙ†ÂÙÝ¢>û‹®Y.\Äç:u¡?.ÿÚ8yr@:ò-£¼pÂãÔO¿Súo¸Ÿ7œãBän«<¿8®³(p5×¾Ãu½õvR@N.? ¥yòT¥pÄû|=÷²X;†ƒcò/*§ Ò3èö Ð…þÉSäj¢½OÉoôâsQÐZY‰Y(ÂrrþiIö0kŒõŽ!ö‰ëþ9 'zðõ˜töŽ÷ýG…ÓGŽ_Ò™Uõ.‡¿ÝÎP2u?y¾;ç—»åýxäº}Ôýc“Û0·ƒXImO£=ÂîÜÛß@€žË× m\ŒÕOiA6¤ÖKú{ vÔkºßl÷gP;‡ye=§Ï»ÌÏÕÜõ„ñ\e†a†ùac 2¬kRQ¤æ9á­By¤V΀‡VaÎèì…cxkï fUγ |?0ˆ‚+¥c-_¶¨§_íÒŒb¤wT¯Ûˇ%Oe윹»*)§^-´i'}®h<¡ö™è‹cl´MÕIœŠa¤°8k{R‘ŸULkK <8v%ëÉ«~Æ…£ßá¤Þ4"Ç0Îîu2tÐ.Odo´Ç¡ J{'çßYtFq[ëÎ`¬}L´ç­ÏØ‘·KJáÛÕÅ&Ï„8î±…p3<=þVÂj‹'ká\µ dl£Þ;JØ×ÑŸ« Ã0 Ã0Yc 2P üþ¶ô|¤#ùÙÄkâE2ŽD¤Ø *”#’ÌrÈr²Ñ|i+x¸½1¹NŸ ”¡WßmÔgÖòaÉ}¨^+õƒœ‚…ÅZ. L…Ñ€ÞúîKø¶pž:¢.Š©T>+{üÈö¡Õ´ÑÉuoÛÙŒZþ5nŽV³ë([`Öä䔓ôYÅÜÄwV¹ì§`y(ƒ2NB™Eϲ“p}è{½D—äXUXWŸšÉPŒû+õj&„£4×2÷f]ב(/IöÜR¯ú¬ö¼‹ú®.ô——¡~‰xÉ¥Þ<¹üz ‡º¦cK†”ù@,vÝ(ÂÏ^`”ÔM÷÷¬kÒåêÓ’ìÃemK}Û³o´‡zÚójÿ{‚ôh(Øp¨} …3½6ÎËToÊ}µÚÉXÈ¢^–ûÓig•õé ó:ªì¢l§Üµ?WsÑóƒz®2 Ã0 ÃÜF§°LN[‰_ã¥Y-ɱÔ`WΠÞÁþrÄ4¥—”Ñ(Üú3 ¹­‚4MÝH˜ô9ÉÕ@ŽÎY¹ƒ Úäê¥ÑÂÒ¬‘0ØèªÒ[Þf§ MO‡ƒÖEz–Æ}ǧÚ/ :çÓõvýFhÆUc«9$Î`{sŸzFšârA{ÆqL?Rá7Qǹpm´É ¹¯hÈ7Ž‚;5¦&1ˆß½7Š5Þi*i0ÉêÉã|u¤ ü¸³q&†²˜bÓøqgÛâXT춃﷔aàGú&=uÐDÂ7ýàS䨭•ƒAj`=9 âwj6< |ºuÑÝUÀbò“ š»;€¡©^9à!—âüºSTjn¨ “.¯ý´y¹²u¾Eí|mv^›’zõ{l´É _›êmí Î…ÖÆd¦†·Ab²GžKíO˜ì±éñ_ÕÆÎÏ̤ÇV¯{^B¥àLai¨÷STYï·X]úýé·‡2AüíÖuT¿;ï€ÏIãsU‘µžÃÈñ¹Ê0 Ã0 3q 20ÌD†f“(:2SEHP0dùPoÆ™%BcI©tÒz¥;ÂxÕ›+yØé Vx°ÉǾî™בa†aæ'DðÀ 3¡Ù$ðPqò“ŠÐ¨.Æ* ܹñååñqøÆ«Þ\™(væ _w†a†af‚Á™ Ã0 Ã0 Ã0 Äg20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L(p š¢2ëyçï;S~¤)*Se¹B:VÏ×ÿ“‡¦3ÜS‰j½™7aéa†a†aæŽaàÇ<ÓT†·šȹþ£ÛðÖ !&‡òñT‘Älûó¢âUwîpgNò låC‘¯EäÅç1£„Š‘câê)›¸k͇¨XàìÅ•O"Ñ­7}ó¥»û¼ÇŒuìAßáCÚÁ §ìmT5THI²<ÍåîÈ’º©6ZðêJ³Ç‚¼'à™/>v6V`®\»‰3@ó ÜÈã– xhÙÇ;Z): eÙÂRü¾^YˆXmVòB¡_¿yG§êóÖŽ§\ŒYrm ±ý15Ø 'ˆ¬íÌ‘ñž1õ¼Â²'“k{Òõ©ľæa-˜Èž“ÏU–çm>ÏU†a†a˜ HV™ sQ…û‹Gï•ñ’t&·a»XöwVᥠUª`Nˆ±ó8ð(:›Ew÷4•hÁæÁ…cø*d:²®÷ƒ.ì38­ä¬¶wˆ}§ÅCë—z‡ ‹žLŒWûÜn~ð×}¢b{NÚž«6¬ÏÛ<Ÿ« Ã0 Ã0k&ÃÜ ÛÑX£úϽlÿè"v,hÇö7)þøm®Ù Ô«3?†ÎwwÉÍ‚MGP6»[¼ÄU %É Õó/œy’Ûʇ!oþ"/n{zž$”É`’o“½UÐY ¤§dXg&h»ðœ7PA壸ڼYoklí Ë¥ëñCíSŠw“õ*L6Sýõ¸éËd rn–F2³ÂƒY¿—*¬kz‹"´~ѓɠ²`âñZ,’w#8»wÞï’;­P†À‚ó¦ìY áf!Øä‚¦õ3#þ e6ÔÊâþ²;×bh´µ²{]eOôÕU`•ênwéÀ†×Ñ¢@Æ\͘Í@Y@+’¡ªà“iº[ÜßU¦Þì¡öDR.ŒÊλÌ(’e€!™ pŽ:”!ÒÓ‹ØôÙˆNVûÎ/)ņÆÙP-ªer]‘Zoýî(Ð5&êUu$Ú;ä¾úÝ‹…N’¤ë¨nZŒùçOãÏhÀÛ‹.E/öm½lÕ³zw)†® »ËiËs¾S¯»«ËµÓ¦Ç$¿¼b1–Ëm=²ßÖt®î1þs0ÙƒÚ߯[£í±êñÝ™ÏË!­=}úõÞo†ûÓ{¾&nõ::÷‰úÿ-nȼ2='ÒŸ«ùéÉæ¹Ê0 Ã0 3q±f2\8¸Û÷ž/HÂñy¹=Ò9Ò=ìÅeê¯à—+kݗ͸kf Ɔ¿kôâFz›êù¡ž£dVÀ6ÉlÍÖòaÉ…˜±ñªšÄòâ«ZF˜ä»pµyn.Ur Ð #½<ÒvÙl`jƒ>†2*îǤÑ8"›´¬éCD„“iµÓ¦ÇÇZÜ[QˆoR^TëžÀŒáVãË­—‚MÏ#ÒídI8™^,ú}tâýfÊj¡{$…H-*ão¨Œ—6`Ñʽ#rØ6*Ç~îCr]Ž{PRˆ·åç ”Ù –Àªõ…Vù2R&öUã:ާdA4¿§Ê^ÐÛI"%¨RÙ¿‹µu÷ÈLU–ºO€8yå[L™s¯ª3€ÏÞTY?rùXñlƒ–¹¿+¹/‹­¾9嵥ѩ}2;àP»Ø¿bšrøäg”1 –£Àrátát}µ¿‰òÙ(:ÕØ”.NYã|e(ï^o¨wP•ËèÂ29vBëVµÝ¯ IÈQݲG9´³Y,×·4 ;=ã½&Ö­—å¶IdòlÌQÙÉóõj'éÓådOœvež}GÇ}ZŸ¯A~¾Ù±…œaµî:âævpŽ‘ µ§>_›=¹´=6=õuÂÑÖrE|^ŠÔö\Ý8ß8zŽi©½^Óý&Z–ûÓF×ñ–Èò¹š™lž« Ã0 Ã0—àÏ%Ê‹øEµžˆ«žš¯á“x-v¼²K.Ñøá†åË<áX?{¾ ד‚zj3ú°R8Ö”)àíù·”E~‰ß»i±}Ã5Ú©·É)(à8é Gdúë÷‡ŸTåzÕØ ò8ÊR(+FAa î9§eHYã,Òí±êñ@A‚çd9»–V`䜿l:ÛP8»[ÔgÑ5ëÏ…‹øü`§\»Ð—mœ<9 ùáQ^8áqê§ß)ý7ÜÏÎÇq!r·Užß ×Ù8Èškßáº^ ‚z;ßKr,“|yªR8â}¾ž{Ù ¬ÃÁ1ù•Sét{†?èBÿä)r5ÑÞ§ä7zñ¹Î(h­¬Ä,a99ÿ´${˜5ÆzÇûÄuHÿÐ=øzL:{Ç{€þ£Âé#Ç/é̪z—Ã߃ng(™ºŸ<_ƒóËÝò~êþ±Ém˜ÛÁ ¬¤¶§Ñawîío @Ïåkˆ6.Æê§´ Rë%ý=;ê5Ýo¶û3¨Ã¼Ž2ˆžÓç]æçjîzÂx®2 Ã0 Ãü°±Ö5)‡(RóœpŽV¡&ÚóÖgìÈÛÎ%¥ðŒíêb“gB÷ØB¸™ž+aµ¿E“µp®Z² 6„Qï%ìëèÀÏU†a†a˜¬±(~Ûz>ÒŽ‘ül"µÇ¦Ï<^Œ³g—æíƒœlt'_Ú ®AAoL®Ó§ehÅÕw[Eê³kù°ä>T¯•úŒÁ‹GNAƒÂb-P¦Âh@o}÷%|[8O QÅT*Ÿ•=~dûЋjÚè亷í‹lF-ÿ7G+ŒƒÙõ”-0krrÊIú¬bnâ;«\öŒŽŠ¿S°<”A'¡Ì¢gÙŒI¸>ô½Þ "ŽKr,Š*¬«OÍd(Æý•z5ÂQšƒk™{³‰®ëH”—${n©W}Ö {ÞE}WúËËP¿D ¼äRož\~=†C]Ó±%CÊ| ;‡nág/0Jê¦û{Ö5érõiIvŒá²¶¥¾ÎíÙ7ÚC=íyµŠ=Az4l8Ô>†Â™^ çeª7å¾Zíd,dQ¯Ëý鴳ʉúô…yUvQ¶SîÚŸ«¹èùA=W†a†an#SX&§­Ä¯ñÒ¬üV¦¾{úËn0?+¦)½¤ŒFáÖŸIÈm=¤iêF¤‡ÈI®rt†ºtA´ÉÕK£;…¥;X#a°Ñ;U¥·¼ÍNAšŸ­‹ô,ûŽOµ_tΧëíúÐ4Œ« ÇVsÓšöù§g¤).½†Z0 üH„ßDç´Ñ&'ä¾¢!ß8 îÔ˜šÄ ~÷Þ(Öx§©¤Á$«o$óÕ‘2ðãÎÆ™ÊbŠMwàGñ{i‹cQ±Ûjà8}2 üHߤ§šHø¦|ÊRµµr0HB ¬'â£AüNMƧO·#º» øCL~2áOsw04Õ+<¤ãRœ_wŠJÍ 5#aÒ嵟ö//W¶Î·è±¯ÍNÂkSrÀ@¯~69ákS=à¡­Ô¹ÐÚ˜ÌÔã6èÏALöÈsÉ¡ý “=6=þë¢ÚØù™™ôØêuÏKè ±œ), õ~*þ¿°Ýo±ºôûÓoe‚øÛ5¬ë˜>`cÀsÒø\Ud­ç0r|®2 Ã0 ÃL\ƒ 3‘¡Ù$ŠN§ÌT Y>Ô›qf‰ÐXR*´ƒGéŽ0^õæJvú‚lòq¯{F&Äud†a†ù <ð#ÃL`h6 Qÿ¿Å ÿ?™ž“éÏÕüôdó\e†a†™¸X3.Üí{ψ$áøÈ¬rŽœC:sg#¿(ÖÈéÜ…ãgR2üÜ5³cÃ_‹5zq#½MõüPÏQ2+`Šd¶Àfkù°äŠBÌØxUMbyñU-#Lò]¸Ú¼7—*9è…‘^i»l60µAC™ ÷cÒh‘MZÖô!"ÂÉ´ÚiÓãc-î­(Ä·)/ªuO`Æp«ñåÖKÁ¦çév²$œÌ /ý>:ñ~3e¯¤f¶"µ¨Œ¿¡2[Ú€E+í™-ä°lTŽý܇*äº÷ ¤oËÏ(³A,âþ[µ¾Ð*_FÊľj\Çñ”,ˆæ÷TÙ z;I¤ÕC*{âw1 ¶î™© ÊR@B×á 0'¯|‹)sîUuðÙ›*»G.+žmÐ2÷w%÷e` ¢•À7§¼¶!:µOfjûWLSŸüÌ€2ÄrX.œ.""œ®¯ö÷"Q>E§:»Q€Ò%Â)kœ‚¯ åÒë-õªòb]X&ÇNhݪ¶ûU! 9ª[ö(‡vÖ#‹åú–&a§rœ£×„úõ²Ü6é‘Lž9#*"y¾Rí$½sºœì‰Ó®³ìѳïè¢Oëó5ÈÏ7;¶3¬Ö]GÜÜÎ1r¡öÔçk³'—ö²Ç¦§¾®@8ÚZ®³H‚ÏK‘Úž«§ãGÏÑ!-µ×kºßd@ËrÚã:ÞY>W3“Ís•a†afâü¹Dy1 ‚D<‹Tød¯vqë6üö`Ÿ–1O8ÖÏãž/ÈÁõ¤ žÚŒ>¬Ž5e x{þ-åC‘Bâ÷nZlßpvêmr 8NúÇ@Ùþúýá'U¹^5vƒ<޲ÊŠQPXƒ{ÎiRÖ8‹t{¬z?Ø)×.ôÇå_'OHG¾Ex”NxœúéwcJÿ ÷ó†óq\ˆÜm•ç7@`Çu6²æÚw¸®Wƒ ÞίìRËãhiž_ϽìÖŽáàÀ˜ü‹Ê)ˆô º=Ãt¡ò¹šhïSò½ø\g´VVbаœœZ’=Ìc½cˆ}â:¤è‰|=&½ã=@ÿQáô‘ã—tfU½ËáïA·3”LÝOž¯ƒÁÎùåny?¹nuÿØä6ÌíàVRÛÓh°;÷ö7 çòµDcõSZ ©õ’þƒõšî7ÛýÔÎa^GD·eÙ1?Wsׯs•a†aæ‡5È@ÙäEjžÎÑ*”Gjg '2R³Jd¶Ce™xå´óýÀ ¬”޵|Ù¢ž~µK3Š‘ÞQ½n/–<•±sæîª¤œzµÐ¦ô]¸z  Xð„Úg¢/ޱÑ6U'q*†‘Ââ¬íIE~V1­--ðàØ•¬'O¬ú tŒ~‡“zÓHåF¬¯Îê±J¶¤ƒvy"{£=m¨PÚ;9ÿ΢3 ˆÛZ¯pcíc¢=o}ÆŽ¼í\R ÏØ®.6y&Äq-„›9àéñ·Vû[ô8Y çªU ›`CõÞQ¾Žü\e†a†ÉkRà÷·È1¤c$?›è±9Ñ.S¿cúsŠ_®¬E¤»Ý^žœlt'_Ú ®AAoL®Ó§ehÅÕw[õYµ|Xrª×J}ÆàÅ#§ Aa±– (Sa4 ·¾û¾-œ§Î…¨‹b*•ÏÊ?²}èE5mtrÝÛöE6£–›£ÆÁÇìúÇʘ599å$}V17ñU.{FGÅß)XÊ Œ“PfѳlÆ$\ú^oÇ%9EÖÕ§f2ãþJ½š á(ÍÁµÌ½ÙD×u$ÊK’=·Ô«>ë†=« ýåe¨_¢^r©7O.¿á®éØ’!e>‹C7Šð³‚%uÓý=ëšt¹ú´$;ÆpYÛR_ç†\öPO{^íŸbO µ¡p¦×Ãy™êM¹¯V; YÔëÃr:í¬²@¢>}a^G•]”í”»öçj.z~PÏU†a†a˜ÛHà–Éi+ñk¼4«¿•©ïÎô•œé*å4†Ú‰Êf KÓ”^RF£pëÏ$ä¶Ò4u#aÒCä$W9:C]ºƒ Úäê¥ÑÂÒ¬‘0ØèªÒ[Þf§ MO‡ƒÖEz–Æ}ǧÚ/ :çÓõvýFl÷CŸzÆ”Am˜~¤Âo¢ŽsáÚh“r_ÑowjLMb¿{ok¼ÓTÒ`’Õ7’ÇùêHøqgãL e1Ŧ;ðãζű¨Øm5pœ¾~¤oÒSM$|Ó>åN)HŽÚZ9$¡Ö“ñÑ ~§¦aÃÓÀ§[‡Ý]ü!&?™ð§¹»šê•Òq)ί;E¥æ†È‘0éòÚOû——+[ç[ôØÎ×f'áµ)9` W¿ÇF›œðµ©ðÐÖê\hmLfjÈqôç &{ä¹äÐþ„É›ÿuQmìüÌLzlõºç%tÐX Ζ†z?E•õ~‹Õ¥ßŸ~{(Äß®a]Çôž“Æçª"k=‡‘ãs•a†afâd`˜‰ Í&Qt:e¦Š `Èò¡ÞŒ3K„Æ’Ré¤ô8Jw„ñª7Wò°Ó¬ð`“ |Ý32!®#Ã0 Ã0ÌOˆàfC³Ià¡âä'¡Q]ŒU¸sâËËããðW½¹2Qì̾î Ã0 Ã0̃3†a†a†a† Îd`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L(pa†a†a†a˜Pà 4EeÖóÎß9v6¦ üHST¦Êr…t¬ ž¯ÿ'Mg¸§Õz3oÂÒÃ0 Ã0 Ã0Ì!ÃÀ x¦© o5sýG;¶á­ZþÊ*”Ó¼ÿ{wàý.YX`“[¨xÕ;Ü™“<[ùPäkyñyÌ($¡b䨣¸zÊ&îZó!*8;FqåÀ“HtëMß|éî>ï1c{ÐwøP†v0è){U R’,Os¹;2‡¤.GGª¼ºÒì±@A†Çá™/>v6V`®\»‰3@ó ÜÈã– xhÙÇ;Z): eْ~/ Ë£ÍJ^ˆâ1tâàë7ïèT}Þz¡àÀ#ÀqãÔ‚‹1K®!¶?†¢Æûô‘µ92ÞÓ"¦žWXödÒcmOº>ÕƒØ×<¬Ãs2ð¹jÃò¼Íç¹Ê0 Ã0 3É*“a.ªpñâ=b£r#^’ŽQ h3‰MnE¼ˆmœ‡ÄGÑÙü(º»ç¡jÓ6½Ï„­|Xr‚^ •œ $( òŠWQº`0)ë<6ˆ»ößµ¦“:öèýžÃ´Öä12À¡Òõ¬Ed)ôö\A ÊÖ¬NmÖ2½_RŒ¦(F›?ƈÖŒE&N¼†í!–-›‰±^lØß-–ü ¤§vtÀ` ÎÇ…Þì A¼ÓÒ+ÑûÐT¢w˜Žá«èlȺÞº°Ïà´’³ZØÞ!öK ­_ê6,z21^ís»ùÁ_÷‰Ší9i{®Ú°>oó|®2 Ã0 ÃL@¬™ s7lGcê?÷Òó‘7›áˆ§e,Øä)P¯Îü:ßÝ%7 6AÙìnñT5”${€TÏ¿pæIn+†¼ùOˆ¼¸ 8ìéy’P&ƒI¾MöVAg5ž’a™  íÂsÞ@•âjóf½­±µƒ.—®ÇµO)ÞMÖ«0ÙLõ×ã¦/“ʹYÉÌ fý^ª°®é9,ŠÐúEO&ƒ¸šÊ×b‘ì¼Ë.»…2œ7eÈ  7 Á&4­Ÿ ñ)(³¡V÷—ݹ¾C£%¨•Ýë*{¢¯®«Tw»Kÿ6´¸Ž2¶àjÆlÊZ‘ì UmŸLÓÝ‚íoŠ4¦Þì¡öDR.ŒÊλÌ(’e€!™ pŽ:”!ÒÓ‹ØôÙˆNVûÎ/)ņÆÙP-ªer]‘Zoýî(Ð5&êUu$Ú;ä¾úÝ‹…N’¤ë¨nZŒùçOãÏhÀÛ‹.E/öm½lÕ³zw)†® »ËiËs¾S¯»«ËµÓ¦Ç$¿¼b1–Ëm=²ßÖt®î1þs0ÙƒÚ߯[£í±êñÝ™ÏË!­=}úõÞo†ûÓ{¾&nõ::÷‰úÿ-nȼ2='ÒŸ«ùéÉæ¹Ê0 Ã0 3q±f2\8¸Û÷ž/HÂñ‘Ù ä9†[ç®™%þZ¬Ñ‹9èmªç‡zŽ’YÛP$³6[ˇ%WbÆÆ#¨jË‹¯ja’ïÂÕæ=¸¹TÉ)@/ŒôòHÛe³© úÊL¨¸“FãˆlÒ²¦N¦ÕN›kqoE!¾HyQ­{3†[/·^ 6=H·“%ádVx±è÷щ÷›·é{$…H-*ãoˆ}Û°¿ X´²AïH‡öƒÊ±ŸûP…\—ã”âmùye6ˆEÜ«ÖZåËH™ØWë8ž’Ñüž*{Ao'‰” zHeOü.ÔÖÝ#3TY Hè:<âä•o1eνªÎ>{“ÚG/+žmÐ2÷w%÷e` ¢•À7§¼¶!:µOfjûWLSŸüÌ€2ÄrX.œ.""œ®¯ö÷"Q>E§:»Q€Ò%Â)kœ‚¯ åÒë-õªòb]X&ÇNhݪ¶ûU! 9ª[ö(‡vÖ#‹åú–&a§rœ£×„úõ²Ü6é‘Lž9#*"y¾Rí$½sºœì‰Ó®³ìѳïè¢Oëó5ÈÏ7;¶3¬Ö]GÜÜÎ1r¡öÔçk³'—ö²Ç¦§¾®@8ÚZ®³H‚ÏK‘Úž«§ãGÏÑ!-µ×kºßd@ËrÚã:ÞY>W3“Ís•a†afâü¹Dy1¿¨Öñ[N…Ogžp¬ŸÇ=_ƒëIA=µ}X)kÊðöü[ʇ"?„ÄïݴؾáíÔÛäpœô†#2ýõûÃOªr½jìye)”£ °÷œÓ2 ¤¬qéöXõx  ÁŒáS²„]K+0rÎ_6m(œÝ-곿èšõçÂE|~°S®]èË¿6NžŽ|‹ð(/œð8õÓïÆ”þîç ç㸹Û*Ïo€ÀŽël d͵ïp]¯A½;^Ù¥–ÇÐÒêþ±Ém˜ÛÁ ¬¤¶§Ñawîío @Ïåkˆ6.Æê§´ Rë%ý=;ê5Ýo¶û3¨Ã¼Ž2ˆžÓç]æçjîzÂx®2 Ã0 Ãü°±Ö5)‡(RóœpŽV¡(C+®¾Û 4¨Ï ¬åÃ’ûP½Vê3/9 ‹µ\@™ £½õÝ—ðmáf×?P¶À¬ÉÉ)'鳊¹‰ï¬rÙ3:8*þNÁòPeœ„2‹že3&áúÐ÷z+ˆ8.ɱ(ª°®>5“¡÷WêÕLGi®eîÍ&º®#Q^’ì¹¥^õY7ìyõ]]è//Cý-ð’K½yrùõuMÇ– )óXìºQ„Ÿ½À(©›îïYפËÕ§%Ù1†ËÚ–ú:·gßhõ´çÕþ)öéÑP°áPû gzm0œ—©Þ”ûjµ“±E½>,÷§ÓÎ* $êÓæuTÙEÙÍíÏÕ\ôü ž« Ã0 Ã0·‘À),“ÓVâ×xiV ~+Sßi*=$Î`{sŸE~@o0Mé%e4 ·þLBnë MS7&=DNr5£3Ô¥;¢M®^Ý),ÝÁ ã€Þ©*½åmv Òôøt8h]¤giÜw|ªý’ s>]o×o$è~ðLÏHS\.hÏ8î€iàG ü&ê8î 69!÷ ùÆQp§ÆÔ$ñ»÷F±Æ;M% &Y}#yœ¯Ž”w6ÎÄPSlº?Žàl[‹ŠÝvPÇé+aàGú&=uÐDÂ7ýàS䨭•ƒAj`=9 âwj6< |ºuÑÝUÀbò“ š»;€¡©^9à!—âüºSTjn¨ “.¯ý´y¹²u¾Eí|mv^›’zõ{l´É _›êmí Î…ÖÆd¦†·Ab²GžKíO˜ì±éñ_ÕÆÎÏ̤ÇV¯{^B¥àLai¨÷STYï·X]úýé·‡2AüíÖuL°1à9i|®*²Ös9>W†a†a&.A†™ÈÐlE§Sfª †,êÍ8³Dh,)•NÚA£tG¯zs%;}Á 6ù¸À×=#â:2 Ã0 Ãü„ø‘a&04›*N~RÕÅX…;` ¾¼<>ßxÕ›+ÅÎ\áëÎ0 Ã0 ÃL08“a†a†a†a˜PàL†a†a†a†aBƒ Ã0 Ã0 Ã0 Ća†a†a† 20á@STf=ï|ˆÐ”“·apǰ¹CvîL­c¼Ú'ŒzIÇ*gŠRÆM¹§Õz3oÂÒÃ0 Ã0 ÃüäÉ0ðcži*Ã[Íä\ÿÑŽmxë„–¿² å4ïÿÞx¿KVŽæã¨õÄlÇRñª;w¸3'y¶ò¡È×"òâó˜QHBÅȱGqõ”MܵæCT,pvŒâÊ'‘èÖ›¾ùÒÝ}ÞcÆ:ö ïð¡ í`ÐSö6ª*¤$Yžærwd´ï‹b³œŽ±ÕëÕ•fyíá™/þAŽèC@Ëþ8ÞÑ"´¿ú6´ÜÔ;;+0W®ÝÄ™? yPn„C&;S `AG–eŒSvÞz„To8S…Òój!byÜ›%/Dñ:qðõ›wtJDo½Ppàà¸q ÇŘ%ׯÛCQc€ýz‚ÈÚÎïé'SÏ+,{2é±¶']ŸêAìkÖ‚ Œñÿ—”ÿg³ø'øÿ_†a†aÆ“¬2æ¢ ÷ Þ#6*7â%ùÂÞÚt©Âºzr2·aûËoà,jñÒ†*½Ï„pž7ÎCâÀ£èl~ÝÝóPµi›ÞgÂV>,9A/*JN ¹pÐK &eÇ1ãa×þ»ÖÔcRǽß`˜Öš yóŸyqp8µ7„zXLrá¤7­tVé)Ö™ Ú.<ç TPù(®6oÖÛ[;èrézüPû”âÝd½ ›Í9eCÔë`Öï¥ ëšžÃ¢­_ôd2¨,˜x¼‹d§UJÖK ä$ÿ¦hÈ—qìÝ–½â²A"Ù;N=÷µRìÊ$%…xûŸ—`ŠÞDÿ@ÆLràœ7e¤×k•k(#‚Nï»ÍÎë 14Z‚ZÙݬ²'úê*°Ju?»xì']Õߤ÷ìSPGüÁ‘0ëÍ…°ë¥{c ®fÌf l«ɤu/Â'Ót·`û›âASoöP{¢ )÷GeHç]fÉ2À̸FÇNÊ‹ééElúlD'«}ç—”bCãl¨;HËäº"µÞúÝQ kLÔ«êH´wÈ}õ» $I×QÝ´óÏŸÆŸ?з]‹^ìÛzÙªgõîR ]v—Ó–ç|5¦^wW—k§MI~yÅb,—Ûz:e¾­è\Ýcüç`²9´¿_·FÛcÕã»2Ÿ—CZ{úô ê ¼ß ÷§÷|MÜêutîõ·f¸Ùÿÿz÷|áþ¿—®'øÿ_†a†aÆk&Ã…ƒ;°}ïᤊr™µ@/íN€!UXZ5Cý–ƒà®™%þZ¬Ñ ½ ´ 7T@=ïÉ^‰m(’½›­åÃ’+ 1cãT5‰åÅWµŒ0ÉwájóÜ\ªä zi¢í²ÙÀÔ} eTÜI£qDÄË”5}ˆˆp~¬vÚôøX‹{+ ñí@Ê‹Uݘ1Üš`xäÁíà`Ñï£ï7SöJjf‹ R‹Êøbß6ìo­lÐ;Ò9yrfMv¿áŽüÜþx‡2íž2 Ä"î¿UÂY]&Š4¿§dÔIvþó)8ÿG§|BKÍ{°Q9ºsªërK½69ÙCP€'º}±ÍNDJP=¤²'~jëî‘=÷ª,9èºÃMºŽÍöU lªÆuOɾ³Þ\»Þ“W¾Å”9÷&ÛØÆgoÒ}¨—€Ï6h™ûü’û²0ÑJà›S^[ŠÚ'³µ‹ý+¦)‡O~f@b9 ,NN×Wû{‘(Ÿ¢SˆÝ(@éá”5NÁW†òéõˆzUy±Œ.,“c'´nUÛýª„Õ-{”C;ë‘År}K“°Ó9ÎÑkÂaÝzYn›ôH&ÏÆœ• ‘<_©v’Þ9]NöÄi×YöèÙwt ѧõùäç›[ÈVë®#nnç¹P{êóµÙ“KûÙcÓS_W m-×Y$Áç¥HmÏÕÓñ£çè–Úë5Ýo2 e¹?m„q³#õÿ ÐÿuÏcÆðÇÖÀºÂüÿ/Ã0 Ã0? ‚?—(/âÕz"žu*ü/Ÿ}‹â-Y$æ WõXô¦~Í©ÍèÃJñò@=ÞžuKùPäééšÊ©·ÉÉ9‘nú”ác áˆLïüþð“ª\¯»AGÙeÅ((¬Á=âeHÊ(²Æ X¤ÛcÕã¡`“éeLص´#çüeÍr[û(Ìúsá">×™,úãòoý,Ð#ÏýËê.œ/·ÓïÆ 6(±Lÿ¹Û>@ N eý¹7ȱmÖ…'×R¯M~’‚31ôÇ”lˆ@8®ƒäHg 9æ-˜‰ƒžàFnäWï­“g½×þÿìý}lTW¶ÿ ~g:!¾Á¶‰ ˜ØÁFf~e~Â& Üø6„~è<"Aß‘ 奣þ£CZŽ„4£H±ˆ4=Ý¡£ õ$â637VB3ÐÐmÂ<™„8Ïc×Vl(Çvx±ËoÐÉÕÌ^{ïóZgŸª2‡²ÖG*¹Î:ûìµÎ>ÇuÎZ{í½¿Çuý5 êí|åÕ½êcÍ 3Yž¨Žx¿§ç^ökÇpèò„ü‹ªÙˆõ9=Ãö`à^•C3Öѯä7úð©Î(h«ªÂBa-9ÿô±{˜5z'8î8¤…ôD½™ÎÞé^`à¤púÈñ³Y¥w-¼=èf†íÔ}û|-ì\Zá”÷â’ëöQÿ¿&¹‰àv°+þö ´GØ{ûRÏ•ÁÄw­À¦'´ üz©þÞ;BôÝo¦û3¬£¼Ž²³ÂÅþ|¡ÀzÖÉç¿+¨ž^Oðó—a†a˜é1ȰµY½¨Çêž/íQ«Ïjõ™²\üeÆÞÂ.¡ vƒtpåKõô«]šqŒöëïæòQÉýL\ð§(l9e ]8çÔ{"^Ž·µª}Aô§01Þ®tç-,ÎÚ?rxÃÜö´Àƒe—­ÇÂ'Ϥ×XÿmäÝsC(­Q=ôµ…C8fò¤¦!OcçÁË(z¼¯…{J· íØˆËhzgµìG vÆ¿?תíØV|õޕɠƒ£“DöF»ÚH¡´wrþ­Î( n«^á &:&D{ÞúŠ“¶se¬©ú<˜ä™Ç=² N怫ÇßHTío¨ÇÊZ¸P£Ù¢Ð›Wnñ:fz¾ÈçìÜz+€\Ÿ¿ Ã0 ÃäcRà¶Ê9ä »6Ü#aA=‰2ÀiU ‚œl$m§·à¡:ô%äw*PŽ6\{¯ X§†ËG%÷ zýÕp7.9 ‹µ\@™ ã!½õÉoq³p‰:¢!Ž9T>+{¼Èö¡´´Ù·u¶ÂgYÈCôšë¿ÍG½«ð^_Æç,‘V ìùË0 Ã0ÌÔd`fæ@YEçs™"hÎ 9¡æ­/ï™ a•ÛÂÊ2é¤q9JyaªôæÊ$ìô+\˜äS_÷ŒÌˆëÈ0 Ã0Ì´ |âG†af ´ªÖ;+tD9úG¦ À@Ù4÷DÞ ÄçW¦Æá›*½¹2Sì̾î Ã0 Ã0‘Á™ Ã0 Ã0 Ã0 ÃDg20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L$p‰Z¢Ò·z^ ¥wE?ÙaäL•yÒûš_Çü|ÓˆB/Õ±ÑZ• „–oÜ_…½9i¢ª‡a†a†I#CažnÞ.¿ÑôO¯‘_Bþê^áTîÁÖ*-B5¶6“Lôq¡ÐšÙÍ'PMŸÞÐÂLå#‘oAì-ÓŸûÂäjnGþÄäý»q_À>÷1å›·(ah;ÔÓpÈ®Ã.ï–ù÷ õâ;ß4{f98n¯íªŽ"}棹D §ˆ4§=OLF/-‰3)¼«·'ÃL:ß(ÔÛ•B+æãÐêŸhÁd¡ßçÉüJž£éyõÿBKæË wë,›ö¯ÀSòGãÊÛcv‘϶Â^QÙ“©c{R¨y®Þ˜á=¯'ñœ ß`†a˜éEV™ ‹QÅ£HõŠªíxéÕeH¼Ü ÚtèÆ-»±çeõ9žªÇKMÕz_ÂyÞ¾c‡FwËÃH&— zÇn½/Sù¨äÄ8®j9}®Óâ yå(«²eݧ†Púcÿ]›1«s¿ÞÿÆ’ê%¡rn›}Lÿ±£¢dx;¤×#^NV‰Ö–Ûûqu*hpn§–éÏ)¡pä[]G^G1ö§žþ]&ÎüQ\÷?âïz3oǬéà­9¸ÄêÕóQšèu%ÅçÖ—jlÞæ TDdgÎD —–°49¾Ônõã—ñ;ÿ"ù3ø|'EDzßmMâjü¾) rýtÙ¾xó;½•?²Öûa<×ÿíFÎjaG§Øw^|hû\ï0a¨'SÕ>·›iÝg,†çu®ÏÙ ï Ã0 3Ý0.a¹¸ivÕÍÑ[½ïÆÛgèõ–ý+R"zä.”Õð,ŠÛ¬²PtiÝïí•›;N |QR<<ÅÑëJ„SïrÌéáJrSù(ä-ÿØ ;€cJ¯õ8Éwã¾æ À)t zJFöÛ7õB#CLi:Mеwß#ê^(jÿ^?Ò­^šŠÿzOØAÚ G¾æšÎÝCßÓnùŠïñû÷Çq–¶žãäö| ‡N:¾V=çQ7â¾t]rú z›N Ô 0àŒ/X¡I·Ó Føƒ,þ²·BTz)³.‡˜‚9\×ÂÍL?ß\‰LoÀ=žš¥¶Cÿùÿ³C:µv&~°P;zän™ÓÇïßl|áv0®ãhÏäôšägÉá–ÁàC0c8-PâìÕ›òo~˜œ^Jå§9Žl+Äj-Ë™u¾·Î$õ~ëúkäˆØsàüâ-$ÂYŠöûÅaœÖîÐå ùU³ërœÆ{0p¯ÊëèWò}øTg´UUa!аV8fr¾»‡Y¨w‰ãŽsÿ‘ÁI%†ÞLÈ!§{“4TB|ìÀ€Ò`fØNÝ·Ï×"ÀÎ¥Ny/.¹nõ{e’›n €¨¹'¼íh°;÷ö ¤ž+ƒˆïZMOhA6øõRý½v„è ºßL÷gX;Gy/yÅ8l0ýym~Φ×ü¾Á0 Ã0ÓcANâ(^\cuÏŠ—بˆÕ‹¿ÙO&ö÷ÎK@q¹ÞJç‡ËC(¨Ý kùЭ\€Yj—f£}ãú»¹|Tr?ªöÑŸÂÄx»ÒIœK`´°8k{üÈasÛÓ³ ´]¶ƒ^ õžè—ù±34äùÎ] ·n/ïžBiÊ*¨-Â1“g1 yrh;^FÑã•x-Üs˜±Ø™ ¹ô°3¹C¬ñïÃÛ¸j;¶Õ_½£çÁùXƒ'IcCÇ¡Ê,s%è»ývêÎ`¢cB´ç­OÔ8i;W–Á—¦0É3!Ž{d8¨Ûòä°ÞBTío¨§«Em_¨Q€l‚ QèÍ+·xÏkMÆçl®ï Ã0 3Ń 4‰ãÁöQ9ƒ|M¶æ4±ßÏjÀX÷§z+rv‘´ß‚‡êPЗßiÈA9Úpí½6`žEÙT>*¹• 01òµÞ¶pÉÉy·œu‚2ÆCzë“ßâfágFhÊ$ òYÙãE¶½°¤tÃg.y˜ÞEg¨.X*Î7)®C>ŽzWá½x²¦¥ßüS9YÔ«»PÈd5¼añØ÷ÆžMù×ì¹rÄ 7“=¿;(^ט'JÌžY(˜üôÝÞÕ/‚õÒ¼k‡ûœ¡]/ÌÆÚHl™^ç;Uz‰Õ¥³p}ø½F ßÊ¡jÕØÚèÏd(Æ{åŸ Gé~ fîÍ&z®c¬¢Äî¹¥^õ…7Ìy==¨(—«=¤‘‹ÞIråÍ„L­j_™–LƒÃ7Šð`†•&Jæy{Ö5éò”µQ ¸¢milpzöí¡žöIµ¿Ïž°z4l8Ú1ÂùnÎ+H¯ï¾Úde,d¡×ƒáþ´ÚYe¨ÕG,¢¼Ž*»ÈÛc~^;øŸ³iõäú¾Á0 Ã0SLèœ 4±X¼s7ÞÆ¯ñÒÂV9¶Þkï™ÓiìKìié÷ÈÇôXüPh+Mßšë@Êh6f×\ÖDå剠zˆœä®ñ‘‚‰NkR%“Ü?ÿ‚wâÄÀ M-šì¤Õã©ÃB×EõØó*¸ÈF¯@Ûãùv?¸Æ…{ÆŒ›!çî7qÈIò¬!Jf½:“ëј÷ÅJ¨’ãØk¨Ç]N®'Êž6†ÌÉ`4ñ£I¯IîÆ=!¢ÉÎÍîqù4ße§G‡{b@*·&&‡vd–•^j 0ÐÜ~dy×\ÄL?_ÉTéxæ Á™øq_µ§°¼ØùÿRcºõ1ˆCþïhLºÒD"m,¼#/ç+°çApMÄG“ø›‹¦_}qñ}jN2áMsWãÙMzMcüi,¼gÎÌj"G"¨.·ý´m…²u©¡Óùšì$Ü6ÙºëwÙh’ž6ÕsJ˜ÚA }›™ñ9ÊN"Èy.9´?d©ïuñN´TI¯s^¢Ž“ÀZÝþAzÿŠjãý–hH¿?½öP&ˆ·]£ºŽis)Ÿ×‰ÐçlZ=‚°÷ †a†™n„OüÈ0ÌôB®œ1]Y8¡ù‚²ŠÎç2ELÕùN‘Þ°€Îmae™tÒŽ¸¥¼0Uzsevš&;4ɧ¾î™בa†a¦)á?2 3m ôÈ4 0-ïÓ‘b{ITLÕùNY;×Ë9/ò` >¿25ßTéÍ•™bg®ðug†aæ6™ Ã0 Ã0 Ã0 ÃDg20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L$p‰Z¢Ò·>x^ ¥wE?é`äL•?r½¯ùup;ç‡(ôRõ2žL0´|ãþ*ÔèÍIU= Ã0 Ã0Y!ȰO7o—ßhMö§×ȯ!u¯p*÷`k•ÙTck3íËÂá¤5¤›O š>/¼¡…!˜ÊG"ß‚Ø Z¦?÷5„ÉÕºÕŽü/ˆÉ5ë-vã¾€}îcÊ7oQÂÐv¨§á]‡]Þ-óï¤ë¥z©?frpd^ÛU)'úÌGs‰NiÎsž˜IziéLœIá]½=¸óC Þ®Z1‡VÿD & =&à,y>ަçÕï-M˜/'Ü­w²lÚ¿OÉO+oýQØD>Û:ÿyEeO¦zŒíIA æ¹zc†ô~âJ{ÏI'üýŠa†™îd•ɰÕXP<ŠT¯Ø¨ÚŽ—^]†ÄË­ M?‹›¶cyêRà>/ÂÉݾc‡FwËÃH&— zÇn½/Sù¨äÄ8®j9}®Óâ yå(«²eݧ†Púcÿ]›1«s¿ÞÿÆ’ê¡Y9·Í>¦ÿØQQ2¼ÒëëUÐÛûqu*hpn§–éÏ)¡pä[]GÞˆ9óGìyùø»ÞÌÂQi:xkŽ&±zõ|”&úD]Iñ¹õ¥ ›·ùÙ™33X/-%ir@ézÕ_Æïü‹Õs;燈ô¾ÛšÄÕø}SÔûé² |ñæwz+d­÷Ãx®þÛœœÕÂŽN±ï¼ø$Ðö¹ÞaÂPO&¦ª}n7ÓþºÏX ï'8б?8ï'ý#–Ü@†÷+†afúc\ÂrqÓ쪛£·z?Þ·ÏÐ7ê=úW¤ÞyôÈ]*ñ«b|úrâ2âtR´{iÝïí•›;N |QRexÏ8pÛlЫíù®³¥µ…b›)Ö9ÒñÏ¡”Ä‚‰Nç¼Ò¡Ì•g±úP dÿ]N‹>xñбžMûÊ0<(ì® -×ùjüvN]ަz‚äWÖ¯ÀZ¹í¢·ZFŒí@çêã=‡ {Cû{ëÖh{Œõxî‡Ìçe‘Öžžú!zCï·€ûÓ}¾AÜêu´îõž˜r=÷½˜ßOžÃ=Ÿ9ï5éõ„¿_1 Ã0ÓcABAƒ ýxý-àé´ ?È œÍâ6 Bоð ƒóð|àÄ’í¸Y[¬œaÛñ†í ›Ê '9 ù5dpkŒ·£û¿_¼·#'œ}£úa¨‚"VaMß'èþÛ”oô‘:”."¡rêÿ¹Â|^õè`„"ýa-q.h(F€^|Ø ³$䃛ŽY•’ç6¹ºÿšÓöFµÿ ¯éV/Åÿ}ù·ƒ ´AŽ|Í 4»‡¿§Ýòßã÷ïã,m <ÇÉíùŽt@­z Δ nÄ}é«äô6@¨=`À_°B“n§ŒðYüeo…™®—@árL)ˆô0pÀu¸™éç›+3^oÀÿtFh˜Úý{’ùy„tjÿì8L4ü`¡vôÈ!Ü2§Žß#¾ÙøÂí`6\ÇÑžyزlBü\ǃÂ!?Ù‰á†jY_Ñ.ÇÙ·Ê“³oá×+¾AÇ!u üÛÒ.q¡m¶Êgá̺\"½Þ2‚Lû|]Çøí$‡üÁQíÈ»p×ã>_“\ãµ…ÈÔª§fH–1Ûã:.‹ö'‚t™ê :Þ"ÐfMúýǰ尻Î+H¯é~kûó`àýé>_?Q^Çð ƒÿýD½sÈ®+ß»Lp=éïW Ã0ÌÌ!|¸DE1º¤¾¥B_àÔ0‰Vå-Kì‡Pÿ1•Ú/9·ýØ€jÉv÷ÀÊG"OOçSÃLrõŒ%i(Ã'â=÷„LÿûáØcª\Ÿz0ÊãèaZ^Œ‚Â:ÜsAË(ýo³5.1Ýc=. vˆðÈ'¾‡¯°kU%F/財z“BŸ$œK`´P\oñ"P¸È%¿%.áÓ#ÝòÛÅ”üFç@ µz@ë“51\ì/5óîÆìŽsÒ•ÂÅØÝæq¯ÂaY<0œõp‡³g…óz0‰Öaã*!>äôšägÉñ•ÁàC0c8-2âìÕ›òo~˜Yz)¥žÆîÙVˆÕZ–ÜÎùa’z¿Çuý5 rD^‘s‰Ï/ÐÒI"œ¥ø`¿ÏQÆiíà]žQ5±Þ!Çiü°÷ª©±Ž~%¿Ñ‡OuFA[U¢k…c&çK°{˜5z'8î8÷œTbèÍ„"qº8IC%ÄÇ (½A†`†íÔ}û|-ì\Zá”÷â’ëöQ¿Ï&¹‰àv ˆš{ÂÛžö»soÿBê¹2X€ø®Øô„dƒ_/Õß`GˆÞ ûÍt†µs”×ñâ‘WŒÃ$ÓßOö⽃Ð{ ½ß¹†…¦×ü~Å0 ÃÌŒA9y£x‘‹Õ=+^ê6¢"V2™c5VUÏ*7ê—@Q`½øîLéå‡ËC(¨Ý kùª\€Yj—f£}ãú»¹|Tr?*ç`/tÉÖ‰ýºeyùò'?4)$™µæo€œ]$mç·à¡:ô%äwrPŽ6\{¯ X§g6•JîAeLŒ|­·-\rrÞÝÎ:e „9éÉoq³p‰3CrCs¨|Vöx‘íCp{؆…ÎbøÌ%7éõq׊%(E~ïÆ+§¦×@8ê]…÷âÉšb”~óOåtP/çB!“Ôð†Åcß{úüå_³çNȃÞLöüî xyZcž°0{f¡<`2<Òw{W¿˜^ziüÿÚá>;í^B÷ fcm$¶p;S¥—X]: ׇÐ[a¤ð­54¯ÑŸÉPŒi+ŽÒýÌÜ›Mô\ÇXE‰ÝsK½ê o˜ó.{z0PQ.W{H#½“äÊ› ™ZÿÔ¾2-™;‡oáÁ +M”4Ìóö¬kÒå( j£@&pEÛÒØàôìÚC=í“jŸ=aõh(Øp´c…óÝ6œW^ß}µÉÊXÈB¯Ãýiµ³ÊQ«XDyUv‘·óÉü~âP°T¼G%Åû&­ž\߯†a˜iGèp‰ŠbgâÇ1kØ„ÿje+ÌÁò_‰ïz™ËÜØ‹k4 £^¢ÈŽz7R=Ê8•Bév 4Ê›êÉYN㕬ºY¥é©(ºAžü-®t–h¹ø¬CèCUê=ü5bÛýåMö³.…uŽnk)JƒÄ¤—¨´ë¨¬üZë¥á!Ÿàfísö¾ð€ƒû~PÙ+“»§¿™…kf¡ë?UÚ59”B¶K-/ù›øM´ºæ? ÙbÄÔ~J§åOèm~fLÕ“+½aöXP ¡ë~gÆü@;Õ.#Ç@ýãê8÷œ* ýÍSÛaÌt½”Á4é#qúQ¾Áû¢Ìí<óô›ãÂa³þßMôƧIýûòªø}évg2œÂñv¨gíFüÈ…P³~ÆÏeÙ;üùüµ£ÀN_—ãâ3ô,trB¦ÓËtwñ±–,ÌI¯€ÆÂÓñvú|–Rqzp‘8V-§˜k=&;Û^<ñeµiçe Ó Ù–ªÁ´áéràÓ8m¶t£hÿ/z­zÄÒãd2˜ì™LûÙcªÇjOúÐýàzTO ^ÏyUá‚+Cä7ˆFä~FyÓ0¾Ÿ¸ß£²˜ó)ç÷+†afº>ñ#Ã0Ó ¹rÆltY[æ‹©Òe9ÏeŒ¸ÓÚyŠôR ‰2U‚I·…•ehú%p$ß)èS¥7W&a§i²Ã°Ió_÷ŒÌˆëÈ0 ÃÌÂ'~dfÚ@Ù‘)r§Bo&ZÞ§¡)ÅöЕ¨¸ÓÚyÊ®oM±œk#oâó+SãðM•Þ\™)væ _w†a†É+œÉÀ0 Ã0 Ã0 Ã0L$p&Ã0 Ã0 Ã0 Ã0‘ÀA†a†a†a†a"ƒ Ã0 Ã0 Ã0 ÃD˜hXókï:×ù¢¦´TeÔ“ÿEÎTÙÉzo ¯ùup;燙¬—êð-ßÉøx¢ ÖÒ›·DTõ0 Ã0 3)2ÖáéæíòÛÏžÙ‹§×ȯ!—ë•ïÁÖ*-B5¶6ëuÊõÇ)o ò ”[ë ¿ð††`*‰| b/h™þÜ×&îÚü—ü/ˆU*¹Â½.´³Ï}Lùæ-JÚõ4²ë°Ë»eþ}‚ ½“±ƑËýk´†¿üÌGs‰NiNlž`½™¡¥3q&…wõödàvÎÓJoW ­˜C«¢“…ž¿“ è–<GÓóê÷–&Ì—îÖ;Y6í_§ä'ŽÆ•·Çþ(ì "Ÿm„ÿ¼¢²'S=Æö¤ Pó\½1à zó¿7¦½×¥þ>Æ0 3óÈ*“a1ª± x©^±Qµ/½º ‰—[A›^FñÕ;»±çeõyûŒ"œØíK0vøat·<Œdr ªwìÖû‚0•JNŒãª–ÓçÚ9-’W¾²Ú![Ö}j¥9ößµ¹³:÷ëýa,©"•sÛìcú%ÃÛ!½ñðZ½½WQ§‚çvj™þœ G¾Õuè„ý¡œù£¸æÄßõfÞ/îMoÍá#V¯žÒDŸ¨+)>·¾”_ó6_ "";s†õæ -éhré>©¿ŒßùçvÎ3\ï»­I\ß7eAÌŸ.›Ào~§·òGÖz?ìÁçzàÿ÷"gµ°£Sì;/> ´}®w˜0Ô“‰©jŸÛÍ´¿î3ÃûŽbìúÝJ|úG,¹ ïc Ã03ã–‹›ö`WݽåÐû±< Þ”EêWðAmS&ÃvàXÛ èïÒºßÛ+7 vœ@ù¢¤øq5­+N½Ë1§_’›ÊG!oùÄ^Øó;Ô‘’ïÆ}Í€S*è@õ”Œì×µ]xÁ¨ òq\kÙ©·5¦vÐåÒëñBíS†÷l½ ·Í½9Ûo‚®û³X£ï—pÜ4PL9R©z,—y @™ï rÞ~S4Œ¦Vç…„z;éÅž²ÖH‚1´ê—}êQ®—bG&))Ä¡ÇK0[obà²§Þ È±¬íJzGƒ^£\C°Øyö¿ä¶ÉÎ×¶bx¼õ ië;|ùï—ÑßP‰rÛEöÁz“ÑKuÕ|ã\C á„7Åí¬`½Ùé¥ßº§p-íÞòCÙƒëíÞLõÛ L“lÅž·Äƒ-ä¨?‚nÑNõBw ¾¬@lM qP;ïÔËüó"YÆiá¬Ò±s&€Š"Äzû˜·ñ{Õ¾®•ehÚµ²‰ty÷Ϩ_oã¾8Ð3!ô*cr_ã¾¢N’¤×QÓ¼K»Î㣵@@ö_Ðåä±èïëÙ´¯ ÃÂî Úr¯Æo'áÔåØiª'H~eý ¬•Û.z»q eÄØt®Î1Þs²9´¿·n¶ÇXç~È|^iíé©_¢7ô~ ¸?ÝçÄ­^Gë>QïÅ)×{ŽóûØs¸ç3ç}*½žð÷1†a˜™ˆ1È ¡¬… ýxý-ài™½àþa 2XΦ`ìKìi9¬7Òq~Œ!€cÉvܬ-VΰíxÃvMå‡;K"‘_“A†çPZ¨ oG÷~+¾¨Dºœpöꇃ ŠX…5}Ÿ ûo P¾Y4ËHJ‘²#Ã?W˜Ï+°ŒP¤?¼$îÀ ÅЫ&9ØïÑÝî{„¶7¢¨ýOxýH·z¨ÿ#ôeØ*Ð9ò57ÐtînzünœvËW|ß¿?޳´-ð'·çcX¼ðKGЪÇà\ÈàFÜ—ÎINAoÓ „ÚCœñ+4év:ÁÅ_öV`½“ÓK§p9ˆ¼z8àº÷ÜÌôóÍÖ;I½¿a¡9oj;ôï§ÿ·6;¤SûgÇa¢á µ£Gá–9ý8püáðÍÆn³á:ŽöÌÖeâwï:áøÉN 7TËúŠv9ξUžœ} ¿^éð :©;Xàß–vÉ ˆ m³Uä8 gÖíàéõ®Ùä`Úçë:Æo'9äŽjGÞ…»÷ùšäê¯-D¦vP=5C²ŒÙ×qY´?¤ËTOÐñ6kÒï·8†-‡Ýu^AzM÷[ÛŸïO÷ùú‰ò:†üïc*p »ê|ïPÁõ¤¿1 ÃÌd‡KT©KêûX*à M7>hq†JOÕã•gÖé}&–Ø?ÊýÇTj¿äÜNôcªed×Ýo(‰<=½M [0ÉÕ!–¤!Ÿˆ÷¾2î‡c©r}êA!£‡Ky1 ëpÏ-£t¸ÍÖ8½t{Œõ¸(Ø!H#ŸøF®U•½ Ëõæhÿ¤¸„OtËoRòo1ÔêžOÖÄp±K<äçÝÙ7œ—õ®.Æî6/ð‹†³îpö¬p"&Ñ: lêyd’›n €¨¹'¼íh°;÷ö ¤ž+ƒˆïZMOhA6øõRý½v„è ºßL÷gX;Gy/yÅ8,4ý}l/®Ñû½OÑû¬klz=Áïc Ã03cANâ(^lbuÏŠ—œ¨ˆÕ‹¿ÙO6õ÷Nœ0ðÃå!Ôn޵üQ®\€Yj—f£}ãú»¹|Tr?‚'!°å ¢íPélâar¸¨}Tí ¢?…‰ñv¥“8—ÀhaqÖöø‘Ã*æ¶§´]¶ƒÞœíÏïžBiÊ*¨-Â1Ó›ç4äÉ¡q즑ïsè­¦áûÃ0Ì­b 2PVÂÁöQ9ƒ|ÁI¶#¸éTckã³² ‚ gIÛù-x¨} ùRöËцkïµëô,»¦òQÉ=¨l€‰‘¯õ¶…KNÎ;9ë”10Ò[Ÿü7 —837Ä1‡ÊgeÙ>ô@³‡mXè,†Ï\r“Þ\íÏÂQï*¼OÖ£ô›ª—pêõ[(d²€Þ°xì{cÏ—¿üköÜ 9bЛɞß/kÌfÏ,”LGúnï꬗ qøk‡ûìôw ÝŸ˜µ‘ØÂíLÜiz‰Õ¥³p}ø½F ßZCÅóÔK1Ø+;e@8J÷c0so6Ñsc%vÏ-õª/¼aλhìéÁ@E¹\í!\ôN’+o&djýSûÊ´dì¾Q„3¬4QÒ0ÏÛ³®I— ,¨™ÀmKcƒÓ³hõ´Oªý}ö„Õ£¡`ÃÑŽ ÎwÛp^Az}÷Õ&+c! ½ ÷§ÕÎ* D­>båuTÙEÞÎ6óû˜CÁRñÞ˜ﳚ´z¦ãûÃ0Ì-:\¢¢Ø™øÑ ÐxPJá܈ ÌÁò_‰ïr™KKNŸgQÕ­Æâ›Ù‹k4 £^²ÇŽ7R=ʈ0•Bév 4Ê›êÉYNã甬ºY¥­©¨²Ažü-®t–h¹ø¬CèCFê=ü5bÛýåMö³…uŽîæCjŸ?‹AbЛ³ý&Ü÷ÃXOßõ²§“áô7³°qÍ,tý§JC&Çî@BÈv©å%¿‰V×ü$[Œ˜ÚOií¢üé½Mã¢ÏŒ©zrÅ 7Ì 4tÝïÌ h§ÚeäX¨\ç^‚S¥e߃¢yj; Ö;9½”Á4é#qúQ¾ÁûÂÊíÌzý˜ô›ãÂa³~ßLôƧIý{úªø=ívìOáx;Ô³—ög–X³~ÆÏeÙ;üùüµ£ÀN_—ãâ3ô,trB¦ÓËtwñ±–,ÌI¯€ÆÂÓñvú|–Rqzp‘8V-§˜k=&;Û^<ñeµiçe Ó Ù–ªÁ´áéràÓ8m¶t£hÿ/z­zÄÒãd2˜ì™LûÙcªÇjOúÐýàzTO ^ÏyUá‚+Cä7ˆFä~FyÓ0¾¹ß³˜Ä1²÷1†a˜éCøÄ ÃL/äʳÑeMl™/î4½!P–CÑù\æÞȾ¾ùaŠôR‹2d‚X·…•ehú%p$ß)èS¥7W&a§i²Ã°Ió_÷ŒÌˆëÈ0 3ƒ Ÿø‘a˜i9(G¦È1º“ôf¢å}Sl™‰ ¾¾ùaÊb9ÇGÞ ÄçW¦Æá›*½¹2Sì̾î Ã0ÌÙ Ã0 Ã0 Ã0 ÃDg20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L$p‰†5¿†ýè¼PS Zª2êIø"gªìd½ù!Oz_óëàvά7w¨ßòŒ'ª`-½yKDUÃ0 ÃDD† Ã:<ݼ]~ûÙ3{ñôùU ärýî=ØZ¥EšÅM{ÔÚÝâóRSµ–¨|åÖºÀ/¼¡…!˜ÊG"ß‚Ø Z¦?÷5„É»6ÿÅ%ÿ b•J®p¯“ììsS¾y‹†¶C@= ‡ì:ìòn™Ÿ ]¯ù¼Žk-;õ¶ÆÔº\z=^¨}Êðž+p@¸m6è5ž—€2+¶×¡@o¢ïÛ¾tª±µùY,Ñ÷K8n_Ê‚)G*Uå2B=Нì{%r¢~S4Œ¦VçM½cô¢KÙk¤ÁZõË/õìÖK±#“”âÐã%˜­71pÙSoäàÕv%½NœA¯Q®¡Œ XXëá›ì|m[!†ÇKP¿¶¾Ã—ÿ~ý •Ø(·]da¬W1“ôR]5ß8÷Ž­pÂüâvV°Þé­—~۟µ´{ÚeK®·{3Õ³™&ÙŠ=o‰yÈQÝ8¢>ê…î(@|=Ù&8¨wêeþy‘, ã´pÖéØ9@Eb½}HÌ[„ø½j_×Ê24íZÙDº¼û±á×Û¸/ôL½JÇXG§Ü׸o…¨“$éuÔ4¯ÀÒ®óøèC-ýt9y,úpàÅ+Æz6í+Ãð °»‚¶\ç«ñÛI8u9všê ’_Y¿kå¶‹Þnh1¶«sŒ÷‚ìAíï­[£í1Öã¹2Ÿ—EZ{zê„è ½ßîO÷ùq«×ѺO”r½×y1¿>‡{>sÞ[Óë fæÇ1È ¡¬… ýxý-|A*ûoâ’¥Séü8Aþ Å’í¸Y[¬œaÛñ†í ›Êw–D"¿&ƒ Ï¡´P8ÞŽî?üV|Q?˜érÂÙ7ª,UPÄ*¬!'ýo P¾YüàÔ¡t )‹à1üs…ù¼ëñ8ûé?æwà‚zeFDàyÑ#¾ÓAÄŒ?ÈDÛQÔþ'™Ñbg¹„¼ÚAÚ G¾æšÎÝCßÓnùŠïñû÷Çq–¶žãäö| ‹`éYõ^¶ep#îKo¤—dƒÞ¦µ‡ 8ã VhÒít‚þ ‹¿ì­Àzg–^ xm„ËQ£ ÙÃÀ×=ïf¦Ÿo®°Þ¦7à7;#4ÇOm‡~^䨡‘N퟇‰†,ÔŽ9„[æôãÀñ{„Ã7_¸̆ë8Ú3[–MˆßùëxP8„ã';1ÜP-ë+Úå8ûVyrö-üz¥Ã7è8¤î`[Ú%ƒ .´ÍV9ã,œY·ƒK¤×»BfCƒiŸ¯ë¿ä?8ªyîzÜçk’«c¼¶™ÚABõÔ É2f{\ÇeÑþD.S=AÇ[Ú¬I¿ßâ¶v×yé5Ýom ¼?Ýçë'Êëdð¿ªÀìšô½«דþþÌ0Ì‹ðáÅ@ê’ú>– À‹²±X=Š;ÕP‰=§°üßÔ|f–Ø?RýÇTj¿äÜNôcªe¤ÓÝo(‰<=ÝK [0ÉÕd,IC>ïA'dzØÇSåúÔ§<Ž~lË‹QPX‡{.h¥‡m¶Æ­¥Ûc¬ÇEÁñ=ò‰ïÇYصª£tY£^Ãy5Ä1§¯-=»aR\§zÈÌÅ”üFç@ µzÀã“51\ì½ywcöÀ çåµ+…‹±»Íã"Å íâᬇ;œ=+œ¹ƒI´ÏÐP ñ¡gƒ^“ü,9‚2¸`f §Å ;qöêMù7?°Þü09½”bNcÙl+Äj-Ë nçüÀz³bð{\×_à GÄšÏé•_< ¥“D8KñÁ~Ÿ£8ŒÓÚÁºIïï®a¿éõ¿?3 óãÂdØÚ¬ô±ºgÅC#*bõâoÈäK½)Œ}©‡RÎt 7Vl,ÿÃå!Ôn޵ü‘ª\€Yj—f£}ãú»¹|Tr?‚½l[Þð(JÑ•Þ%~\·µª}Aô§01Þ®tç-,ÎÚ?rXÅÜöô m—­Ç ×é|óÉ»ç†PZ£² j ‡pÌô&6 yrh;^FÑã•x-üM‹aB±3réùe˜é fÇ¿¿—«¶c[ðÕ;V'…îܘ$ ‡6R(³@Ε ?î^ôÛ©W8ƒ‰Ž Ñž·>Qã¤í\Y_~¥Â$Ï„8î‘e@â nË“ÃzGQµ¿¡ž®µ}¡F² 6D¡7¯Üâu4¾jäûäÜz+€\ߟ†™‘ƒ ´ìÆÁöQ9ƒ|à'[Ã'öëéÇp¬ÚYmbÍ2T„e?³‹¤íüCÏòG''d:½LwkÉœô h,<o§Ïg8 !§‰cÕrйÖc²³íÅó_V›v^Ö0 ’m©L¾‘.>í€ÓFaK7Šöÿ¢×ªGÜ(=N&ƒÉžÉ´=¦z¬ö¤Ýî¡AõêõœW.¸24LzƒhDî÷g”×1 ãû§û}2‹Is~ff&>ñ#Ã0Ó ¹rÆltY[æ Ö;åP–CÑù\æüȾ¾ùáÓK3ÊÌ œÝV–¡é—À‘|§ O•Þ\™„¦ÉÃ&AÌ;|Ý32#®#Ã0?*8ÈÀ039V¡Z:.ߎëD¹z€_ßüpÇÝÏ´ªDÈÊ>ÌÌ€Ó|†É7d`†a†a†a&—°d†a†a†a†É20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀDÚ_ÿžr^ ÉÅvãI½9m™*;Yo~ø‘ë¥ '=:¸óëÍQè¥:|Ëw2>ž¨‚µôæ-U= Ã0Ìm#CažnÞ.¿ý왽xzü*r¹žõl­Ò"édêu¬­>ÖHå(·ÖÉ}á - ÁT>ùÄ^Ð2ý¹¯!Lܵù/.ù_«Tr…{Ý`gŸû˜òÍ[”0´êi8d×a—wËüûzƒê‘X:ýç4ÉáÅÏZþÈ®ùh.ÑÂ)"ͩˬ7?Ì$½´t&ÎÜÚŠÜÎùõæ‡@½])´b>­þ‰Lz¿š\À¾äù8šžWÏ;Z5 _N¸[ïdÙ´ž’Ÿ8WÞû£°3ˆ|¶uþóŠÊžLõÛ“‚@ÍsõÆ 'ð=Ù礽·§î/0Ì“¬2£ ŠG‘êUÛñÒ«Ëx¹´isæØóònçóñ% Õ¯w!ÙíK0vøat·<Œdr ªwìÖû‚0•JNŒãª–ÓçÚ9-’W¾²Ú![Ö}j¥9ößµ¹³:÷ëýa,©~d*ç¶ÙÇô;*J†·Cz=âÇmôö~\E œÛ©eúsJ(ùVפ×P xÄ1Þò FåÑY"¯ÿñw½™7Ä‹]SKú­^=¥‰>QWR|n}©·æm¾@EDvæ ëÍ3X/-qhrŒèÿ¢~ü2~ç_ߌÛ9?°Þü‘Þw[“¸¿oÊ‚Ô?]6/ÞÌÿ’¡Yëý°–K$gµ°£Sì;/> ´}®w˜0Ô“‰©jŸÛÍ´¿î3Ã{2Žbìú]›Þ©G,¹ þÃüX1.a¹¸ivÕÍÑ[½ïÆÛgèEÛÿ©w^Á=r—‹jl¥,†ÿ´OCÑÁ¥ t¿·Wnì8òEIñÏ'Ž^W"œz—cNÿœ$7•BÞòˆ½°8¦ô:PÄ2H¾÷5oN© ÕS2²_;ðj»ð‚;PAåã¸Ö²SokLí Ë¥×ã…Ú§ ïÙzn› z}¤×CÇ5â;}Ìе~ËcôýŽÛÊ‚)G*Uå2b;Нï93¿)ö¬©N½EôâGÙk¤ÁZõË õ°ÖK±#“”âÐã%˜­71p9ãZíähÕv%½Î”A¯Q®¡Œ Xì<û_rÛdçkÛ 1<^‚ú…´¥Ö±ïo 5íån‡,ì‚õ*Xof½TWÍ7Î=kAÁ2œðݸ¬—õJ|zéYö®¥ý/ù¡ìÐõvo¦zvÂ#Ó$[±ç-ñâ’rÔA7Žh§z¡‡; _V ¶&8¨wêeþy‘, ã´pÖéØ9@Eb½}HÌ[„ø½j_×Ê24íZÙDº¼û1é×Û¸/ôL½JÇXG§Ü׸o…¨“$éuÔ4¯ÀÒ®óøèC-ýt9y,úpàÅ+Æz6í+Ãð °»‚¶\ç«ñÛI8u9všê ’_Y¿kå¶‹Þnh1¶«sŒ÷‚ìAíï­[£í1Öã¹2Ÿ—EZ{zê„è ½ßîO÷ùq«×ѺO”ß“r½Çz1¿o?‡{>sÞÓÓë ÷æÇŠ1È ¡¬… ýxý-ài™½àþÇ 2ÐЉڎУóÏ ùK¶ãfm±r†mǶƒl*?ÜY‰üš 2<‡ÒBmàx;ºÿð[ñEý€¤Ë gߨþñ ;+k­Âš¾OÐý·(ß,~GêPºˆ„”ñþ¹Â|^õè`„"ýÇMâ\PfB€Þô@Š¿žlƒ t?¸ïÚÞˆ¢ö?áõ#ÝêG·ø¡÷„T  räkn éÜÝ8ôøÝ8í–¯ø¿gi[à9NnÏǰx!”Ž‘UáåS7â¾t?zi4èm:P{(À€3¾`…&ÝN'á²øËÞ ¬—õf£—már˜(X÷0pÀõ¿æf¦Ÿo®°^Ö›•Þ€gTF<ïLþgivH§öÏŽÃDÃjGÂ-súqàø=Âá›/ÜfÃu홇-Ë&Äsí:áøÉN 7TËúŠv9ξUžœ} ¿^éð :©;Xàß–vÉ ˆ m³Uä8 gÖíàéõ®Ùä`Úçë:Æo'9äŽjGÞ…»÷ùšäê¯-D¦vP=5C²ŒÙ×qY´?¤ËTOÐñ6kÒï·8†-‡Ýu^AzM÷[ÛŸïO÷ùú‰ò:†üïÉ*p »b}ïæÁõ¤û óc'|¸DE1º¤¾¥²|àUckãèíÌy–Øÿ´ýÇTj¿äÜNôcªeäÏÝo(‰<=ýI [0ÉÕF,IC>ï'dºÔÇSåúÔ‰<Ž~|Ê‹QPX‡{.h¥Km¶Æq¥Ûc¬ÇEÁñƒ5ò‰ïÇJصª£tÙP½Šàz¢à>=Ò-¿]HÉ¿atÄP«>YÃÅ.ñ˜w7fÜp^æºR¸»ÛGq§µƒ;tyBþEÕlÄz‡§ñà ܫrþÆ:ú•üF>ÕmUUXˆ"¬Ž™œ/ÁîaÖê@â¸ãÜdpR‰¡7rˆÄé^`à$ •;0 ô‚¶S÷íóµ°si…SÞ‹K®ÛG½o˜ä&‚Û jî o{Ú#ìνý©çÊ`â»V`ÓZ ~½To€!zƒî7ÓýÖÎQ^Ç‹G^1ûMOÞ‹kôNMïé䯸†9§×ì/0Ìcak³zðÅêžÁ¨ˆÕ‹¿YLF´f#–ãK=¤ÂÌ—‡PP»A:ÖòŸ¶rf©]šqŒöëïæòQÉýL\î¾·å ¢íPéNâÇæp;Pû¨ÚD ãíJ'q.ÑÂâ¬íñ#‡UÌmO Qã¤í\Y_>©Â$Ï„8î‘e@â nË“ÃzGQµ¿¡ž®µ}¡F² 6D¡7¯ÜâuÌôž,ý‚¹ ôV¹ú ó#Ádø e7¶Ê9ä0ÙšÅÄ~:‹¡í°Þœ]$mç·à¡:ô%äwrPŽ6\{¯ X§ga5•JîAeLŒ|­·-\rrÞ]κÌé­O~‹›…Kœeâ˜C峲NjlúÁ³‡mXè,†Ï\r“^¹ž)B8ê]…÷âÉšb”~óOõ’F½B …LPÃ}oìñ—Íž;!G z3Ùó»ƒâa³Æ<‘^öÌByÀäa¤ïö®~Áz‰;M/K_;Üg§ƒKèÿ³±6[¸ Ö5ÓK/±ºt®ÿ ·ÂHá[9ÔT½7y)Ækå®LGé~ fîÍ&z®c¬¢Äî¹¥^õ…7Ìy==¨(—«=¤‘‹ÞIråÍ„L­j_™–LƒÃ7Šð`†•&Jæy{Ö5éò”µQ ¸¢milpzöí¡žöIµ¿Ïž°z4l8Ú1ÂùnÎ+H¯ï¾Úde,d¡×ƒáþ´ÚYe¨ÕG,¢¼Ž*»ÈÛ™šÍ{rÁRá$…¿¢I«'Wa~$„—¨(v&~³†MÈñ‚”â·˜ƒå¿r-U™eƒb/®W½¤‹%l8¤&z”C*3„Òíh0”7Õ“³œÆW)Yu³JkRQGƒ<ù[\é,ÑrñY‡ κÐ{økĶûË›ì1 g©- ëÝ͇Ô>ƒÄ ×Xu¾4Ö¬P´½øº<û~xë³Yº4„ÓßÌÂÆ5³ÐõŸ*M• !Û¥–—üMü&Z]ól1bj?¥y‹ò§ô6›=3¦êɃÞ0{,(ÐÐu¿3Ãx j—‘c  þquœ{ N•¶{Šæ©í0X/ëõ¤—2‚&}$N#Ê78öÜά×ëuØ›¢(óoIDATõü2ÑsŸ&õóòUñ¼ìvg2œÂñv¨w+ÚÿŒxh‡P³~ÆÏeÙ;üùüµ£ÀN_—ãâ3ô,trB¦ÓËtwñ±–,ÌI¯€ÆÂÓñvú|–Rqzp‘8V-§˜k=&;Û^<ñeµiçe Ó Ù–ªÁ´áéràÓ8m¶t£hÿ/z­zÄÒãd2˜ì™LûÙcªÇjOúÐýàzTO ^ÏyUá‚+Cä7ˆFä~FyÓÈøž¬>'qÌÙ_`˜á?2 3½+gÌF—5±e¾`½ùaªô†@YEçs™k$øúæÖ›(`GAA»ÛÂÊ24ý8’ïô©Ò›+“°Ó4ÙaØ$ˆy‡¯{FfÄud˜9d`˜‚3¿P-¥–ïgÖ{û™*½Ùå,þ|}óëÍ´ªDÈJFÌÌ€Ó|fêá Ã0 Ã0 Ã0 Ã0‘¾„%Ã0 Ã0 Ã0 Ã0L–pa†a†a†a˜Hà Ã0 Ã0 Ã0 Ã0‘ÀA&ÖüÚ».p¾ É¶vãI½9m™*;Yo~`½·špÒ£ƒÛ9?°Þü0“õR¾å;OTÁZz󖈪†a˜<’!ȰO7o—ß~öÌ^<½F~¹\ßy¶Vi‘`qÓµ®sÀ¾@*ß@¹µnì oha¦ò‘È· ö‚–éÏ} arà®ÍqÉÿ‚X¥’+Üëè:ûÜÇ”oÞ¢„¡íPOÃ!»»¼[æÙg¶ß¿c·Þpa’Ïrx²ÖK?²k>šK´pŠHs®òëͬ73´t&ÎÜÚŠÜÎùõæ‡i¥·+…VÌÇ¡Õ?Ñ‚ÉBï““ë (y>ަçÕóV È—îÖ;Y6í_§ä'ŽÆ•·Çþ(ì "Ÿm„ÿ¼¢²'S=Æö¤ Pó\½1à ò/ü~DšŸ’N¸ÄÜ)d•ɰÕXP<ŠT¯Ø¨ÚŽ—^]†ÄË­ M!ßV—Âñ—wc}>Naù†uzgÂyÞ¾c‡FwËÃH&—dpfM壒㸪åô¹vN‹ƒä•o ¬vÈ–uŸBéCŽýwmnĬÎýzÿcKªºÊ¹mö1ýÇŽŠ’áí^øg_½½WQ§‚çvj™þœ G¾Õµ˜Î+BÎüQ\÷?âïz3oˆ¦–Ö[½z>J}¢®¤øÜúÒgÍÛ|ŠˆìÌÖ›XoÎÐRƒ&…þëÇ/ãwþuƸóëÍ3\ï»­I\ß7eAùŸ.›ÀoæÉЬõ~؃Ë%’³ZØÑ)öŸÚ>×;LêÉÄTµÏífÚ_÷‹Á¿ÀQŒýÁñ!úG,¹ þsç`\Â’²vÕÍÑ[½ïÆÛgèEŸÿ©w^Á=ÖöF@ï§Ì‡Ÿ¦þ„×tÓÎt(Z¶4î÷öÊÍ‚'P¾()nF z]‰pŠ]Ž9ݬ$7•BÞòˆ½°8¦ô:P/H¾÷5oN)§ê)Ù¯j»ð‚Û¡§òq\kÙ©·5¦vÐåÒëñBíS†÷l½ ·Í&û”A±½z}Ÿ(;Lò@ª±µùY,Ñ÷K8n( ¦©T=–Ëæ(¾²ï•tÈ©øMѰgqê=‘kóS6©@0†VýrD=õRìÈ$%…8ôx fëM \θv99<µ]I¯ScÐk”k(#‚;Ïþ—Ü6ÙùÚ¶B — ~!m©uÝûhw¹Û! ûƒ`½ Ö;}õR]5ß8ÿ+¤Ã o°ÛYÁzY¯dšè¥g÷S¸–ö?ì‡Þ ×Û½™ê]™&ÙŠ=o‰µ £þºqD;}Ô =ÜQ€ø2zs™@â vÞ©—ùçE² 0ŒÓÂY¤cçLEˆõö!1oâ÷ª}]+Ëдkdéòî×¿ÞÆ}q gBèU:Æ::徯}+D$I¯£¦y–vÇGj€ì¿ ËÉcч/^1Ö³i_†…Ý´å:_ßN©˱ÓTOüÊúX+·]ôvã@ˈ±è\c¼çdrhoÝm±Ïýù¼,ÒÚÓS¿ DoèýpºÏ7ˆ[½ŽÖ}¢ü<êî 4ûÏážÏ¿$½žpÿˆ¹s0$”µ°¡¯¿<-³Ü7¢?È@8§Œƹy!oØX²7k‹•n;Þ°sSùáÎ’Hä×dá9”jÇÛÑý‡ßŠ/ê*]N8ûFõ?“ ŠX…5ä¤ÿmÊ7‹„‘:”."!e<†®0ŸW`=g?ýŸ]â \˜ì§F|§ƒ9Þ6’›‚ t?¸ït*jW&ù#TüЗ;¨@äÈ×Ü@Ó¹»qèñ»qÚ-_ñ=~ÿþ8ÎÒ¶ÀsœÜžañ‚$«Ã˘ nÄ}éoôeÐÛt¡öP€g|Á MºN0Âdñ—½X/ëÎz)À·.Ç…‚„\ÿãnfúùæ ëe½ÓZoÀ39#4‡Sm‡~ð¿;d‡tjÿì8L4ü`¡vôÈ!Ü2§Žß#¾ÙøÂí`6\ÇÑžyزlB<ǯãA᎟ìÄpCµ¬¯h—ãì[åÉÙ·ðë•ߠ㺃þmi— ‚¸Ð6[å@޳pfÝ.‘^ï ™ A¦}¾®cüv’Cþà¨vä]¸ëqŸ¯I®ŽñÚBdj ÕS3$˘íq—EûAºLõoh³&ý~‹cØrØ]ç¤×t¿µýy0ðþtŸ¯Ÿ(¯cxÁï_¨Àìzöù"Áõ¤ûGÌGøp‰Šb uI}Kex¨CU÷Ÿ°çåVà{ñR“øÑe‰}÷³Rûçv¢P-#aîžCùHäéé@jØ‚I®þbIÊð‰xNžéC?{L•ëSÿXò8úg,/FAaî¹ e”>´Ù×”n±;Ä?ðÈ'¾^aתJŒ^°ÊìoˆcN_[zvƒI>).áSÉrq %ÿ†Ñ9C­÷dM »Äâ¼»1{à†órÓ•ÂÅØÝæqsâ…gñÀpÖÃΞÎÍÁ$Z„gh¨„øÐ‹•A¯I~–#Ü03†Óâ…Ž8{õ¦ü›Xo~`½Ù@)×4¶ûȶB¬Ö²Üàvά7?Ì0½ƒßãºþ9"öœ]¿x@K'‰p–âƒý>Gq§µƒ;tyBþEÕlÄz‡§ñà ܫrÇ:ú•üF>ÕmUUXˆ"¬Ž™œ/ÁîaÖê@â¸ãÜdpR‰¡7rˆÄé^`à$ •;0 ô‚¶S÷íóµ°si…SÞ‹K®ÛG½_™ä&‚Û jî o{Ú#ìνý©çÊ`â»V`ÓZ ~½To€!zƒî7ÓýÖÎQ^Ç‹G^1sN÷/öâšåGæÖ^O°ÄÜyƒ [›Õƒ V÷¬x(lDE¬^ü ™œgÍF,Ç—zxÄ)¼ýΗ@ÝFcù.¡ vƒt¬åM\¹³Ô.Í8FûÆõwsù¨ä~&.{Ù¶¼áQ”¢*ýGüónjUû‚èOab¼]é$Î%0ZXœµ=~ä°Š¹íéÚ.[ÓyMÞ=7„Ò•UP[8„c¦'õ4äÉ¡qìŽzWá½x²¦¥ßüS½´P/ÉB!“Ôð†Åcß{ üå_³çNȃÞLöüî øñ]cžÐ.{f¡<`2-Òw{W¿`½ëš`½4>|ípŸ–-¡ßÌÆÚHláv&XoÔ°^‹Õ¥³p}ø½F ßÊ¡µÕØÚèÏd(Æ‚L«‘YGé~ fîÍ&z®c¬¢Äî¹¥^õ…7Ìy==¨(—«=¤‘‹ÞIråÍ„L­j_™–LƒÃ7Šð`†•&Jæy{Ö5éò”µQ ¸¢milpzöí¡žöIµ¿Ïž°z4l8Ú1ÂùnÎ+H¯ï¾Úde,d¡×ƒáþ´ÚYe¨ÕG,¢¼Ž*»ÈÛylö/ – ?")ü3MZ=¹úGÌ–ÐáÅÎÄcÖ° 9~ŽRÞ6¢s°üWâ;-sÙsï·c½o9¬ b/®Ñ$Œz‰;jÖpHMô(#hTf¥Û)Ð`(oª'g97R²êf•棢pyò·¸ÒY¢åⳡÿ”Rïá¯Ûî/o²Ç€œµµ(¬st7Rû³ÌöõUjyã´a’qߨ믗= §¿™…kf¡ë?UÚ&9B¶K-/ù›øM´ºæ? ÙbÄÔ~J·åOèmGzfLÕ“+½aöXP ¡ëþûì¾@;Õ.#Ç@ýãê8÷œ*õÍSÛa°^Öëg:é¥ † I‰Ó߈ò Ž}·3ëõÃzS¥—Ø›õ¼6!Þ?Mê÷ƒWÅûA·;“ᎷC½KÒþgÂV%jÖ/Âø¹,{‡?¿‚¿vØéër\|†žåNNÈtz™î.>Ö’…9éÐXx:ÞNŸÏ2p@C*N.Ǫås­ÇdgÛ‹ç1¾¬6í¼¬a$ÛR5˜6|#]|ڧ–níÿE¯U¸QzœL“=“iÿ {LõXíIºÜC‚ê Ôë9¯*\peh˜ôшÜïÏ(¯cFÿÂíGd1‰cÎþóc%|âG†a¦råŒÙè²&¶Ì¬7?ÜizC ,‡¢ó¹Ìq’|}óëÍS¤—…”‰(¼-¬,CÓ/#ùNAŸ*½¹2 ;M“†M‚˜wøºgdF\G掃ƒ 3Ccת¥Åòý"Ézo?wšÞlˆr6} ¾¾ùõ懩қiå&ffÀÎ龎Ìt„ƒ Ã0 Ã0 Ã0 ÃDBø– Ã0 Ã0 Ã0 Ã0YÂA†a†a†a†a"ƒ Ã0 Ã0 Ã0 ÃD˜hXókø×ÛÍ 4ùÔ®b<©7§-Se'ëͬ7?äI/M8éÑÁíœXo~`½¹Cuø–ïd|¦|ó% m‡€zÙuØåÝ2Ï>³ýõøË§Ù3ÃÈáÅÀZ?üÈ®ùh.ÑÂ)"ÍÉɬ7?°Þü0½´t&ÎÜÚŠÜÎùõæÖ+èJ¡óqhõO´`²Ðûóä:dJž£éyõ>C«äË wë,›ö¯ÀSòGãÊÛcv‘϶Â^QÙ“©c{R¨y®Þ˜áDä…ûƒw.Ye2,F5"Õ+6ª¶ã¥W—!ñr+hӆ俎¿¼{Äç`w5^jªÖ;ƒÎóö%;ü0º[F2¹Õ;vë}A˜ÊG%'ÆqUËésíœÉ+ß@Yí-ë>5„Ò‡ûïÚ܈YûõþÇ0–T7aåÜ6û˜þcGEÉðvH¯GÜü« ·÷ã*êT°âÜN-ÓŸSBáÈ·º–`ûË×Yõ(½*èqcpÊöèú3qæâºÿ×›yC<ø›"Xânõêù(Mô‰º’âsëK5oó*"²3gXo~`½ù!½´äŸÉQ ßúñËø½/nçüÀzóëï¶&q5~ß”uBütÙ¾x3ÿK†f­÷ÃX.‘œÕÂŽN±ï¼ø$Ðö¹ÞaÂPO&¦ª}n7ÓþºÏX þT®~PðNƸ„åâ¦=ØU7Go9ô~¼oŸ¡oýW¤ÞyôˆMÊb¨íÀž·NÑNtø7àõ–ÃjÛE–&ÐýÞ^¹Y°ãÊ%ÅŪו§Øå˜ÓÅ#¹©|ò–ÿ@ì…À1¥×"ZAòݸ¯ypJ9íTOÉÈ~8PÛ…Ü *ǵ–z[cj].½/Ô>exÏÖ«pÛl°ß§W6ýø­Ú–бÏážÏÌúEkbkó³X£ï—pÜ4PL9R©z,—½Q|eÝ+ÐËýoІ=knSo‚\#Ÿ²ÖH‚1´ê—êq¬—bG&))Ä¡ÇK0[obàrƵ¼Éñ¨íJz ƒ^£\C°Øyö¿ä¶ÉÎ×¶bx¼õ iK­sÞß@kžËÝYØëU°^Ö+q饺j¾qþG-(8ˆÞ #·³‚õ²^É®—ÞUžÂµ´ß?”ý»ÞîÍTïFðÈ4ÉVç½9rÔA7Žh§z¡‡; _V ¶&8¨wêeþy‘, ã´pÖéØ9@Eb½}HÌ[„ø½j_×Ê24íZÙDº¼û5ȯ·q_è™z•ޱŽN¹¯qß Q'IÒë¨i^¥]çñчZ û/èròXôáÀ‹WŒõlÚW†áAawm¹ÎWã·“pêrì4Õ$¿²~ÖÊm½Ý8Ð2bl:Wçï9ÙƒÚß[·FÛc¬Çs?d>/‹´öôÔ/Ñz¿ÜŸîó âV¯£uŸ(¿6åòS¼˜ý)¯”^O¸?x'c 2H(P°¡¯¿<-³Ü& ÈИÂT?°Å_ÚÛ~œ‹ ycÉvܬ-VN¸íÃvÌMå‡;K"‘_“A†çPZ¨ oG·t¸Õ –.'œ}£úæRA«°¦ïtÿmtâÇFêPºˆ„”]ðþ¹Â|^õXA‰!à ì§2«Rö¹ÈàÆ\ëÜÔ?Œ 1¥é4A÷ƒû¡í(jÿ^?Ò­þ)‹ÿú0µƒ ´AŽ|Í 4»‡¿§Ýòßã÷ïã,m <ÇÉíù/ ÒQ°ê1¼œÈàFÜ—F/½M'jpƬФÛé#üAÙ[õ²^Ö›7Âå@Ppòaà€ë·ÅÍL?ß\a½¬—õð’O'œÿ]);¤SûgÇa¢á µ£Gá–9ý8püáðÍÆn³á:ŽöÌÖeâ½å:áøÉN 7TËúŠv9ξUžœ} ¿^éð :©;Xàß–vÉ ˆ m³Uä8 gÖíàéõ®Ùä`Úçë:Æo'9äŽjGÞ…»÷ùšäê¯-D¦vP=5C²ŒÙ×qY´?¤ËTOÐñ6kÒï·8†-‡Ýu^AzM÷[ÛŸïO÷ùú‰ò:†üþ”Ù ®'Ýd2 —¨(R—Ô÷±Tøâ™?âxªÞž“!žúcz—™%öEí?f¥ö ÎíD?6 ZF†Ü=ÿ†ò‘ÈÓÓcÔ°“\ÝP±$ eøD<7„“¾y ~8ö˜*×§n4yÝœåÅ((¬Ã=´ŒÒi6[ã|Òí1Öã¢`‡¸¡G>ñÝÌ®U•½`•5ØOmLßõ¢Â‘vLè#€½¸f•§ëྑ—ðé‘nùíâ@Jþ £s †Z=@ìÉš.v‰‰ywcöÀ çaß•ÂÅØÝæqdâ`ñÀpÖÃΞNÆÁ$Z„gh¨„øÐ‹†A¯I~–Ü03†Óâ‡8{õ¦ü›Xo~`½ùarz)õ™ÆXÙVˆÕZ–ÜÎùõæÖ›ƒßãºþ9"öe¿x@K'‰p–âƒý>Gq§µƒ;tY¿½UÍF¬wÈq?ìÁÀ½*§s¬£_ÉoôáSQÐVU……(ÂZá˜ÉùìfM Þ $Ž;ÎýG'•z3!‡HœîNÒP ñ±JoP€!˜a;uß>_‹;—V8彸äº}Ôû¤In"¸(¢æžð¶g =ÂîÜÛ?€z®  ¾k6=¡Ùà×Kõ÷Ø¢7è~3ÝŸaíåu¼xäã°îtÊì¥×ì2!A†­Íê‡1V÷¬ø‘܈ŠÂ'«ùû[j>ú¼=PŒXª_ïIç‡ËC(¨Ý kyQ+`–Ú¥Çh߸þn.•ÜÏÄÏØ[Þð(JÑ•#nÆÃí@í£j_ý)LŒ·+ĹF ‹³¶ÇyàÏ2ÐvÙz|¸Ïkâ='øpír1 ì9dù¹ ôÖíåÝsC(­QYµ…C8fzrMCžÇ΃—Qôx%^ 21 3 °3ré‘dæÎ†:Æ¿ÿͨڎmuÀWïèwâugÝ$il(ð8´‘B™r®ýq÷¢ßN½ÂLtLˆö¼õ‰'mçÊ2øò…&y&Äq,u[žÖ;Bˆªý õtµ¨í 5*M°! ½y寣џÒdôƒrõï ŒA†Zvã`û¨œƒAþ@&[ tÖáé_ã«OBÆ™‘“¤í üƒÞ #…oåTÕØÚèÏd(Æ÷ÊlaGé~ fîÍ&z®c¬¢Äî¹¥^õ…7Ìy==¨(—«=¤‘‹ÞIråÍ„L­j_™–LƒÃ7Šð`†•&Jæy{Ö5éò”µQ ¸¢milpzöí¡žöIµ¿Ïž°z4l8Ú1ÂùnÎ+H¯ï¾Úde,d¡×ƒáþ´ÚYe¨ÕG,¢¼Ž*»ÈÛYnö§ü~PZ=¹úƒw¡Ã%*ЉǬar<¥€mDæ`ù¯Äw¹\%M¨SÃÜs5Ù‹k4 £N×·£H ‡ÔD2¢De†Pº †ò¦zr–Óø%«nVi/**e'‹+%Z.>´RCÈM*õþ±íþò&{ ÈYL Bg¨Cuó!µ/0‹Át^”ÞcÉñžhÓ[>›ÉKÜ÷ÃXOß³Y¾ÔÀéofaãšYèúO•ÆH/þB¶K-/ù›øM´ºæ? ÙbÄÔ~J{åOèmWy&ó @ zÃì± @C×ýÎ Ôvª]FŽ%€úÇÕqî%8UZç=(š§¶Ã`½¬×ëU A“>§¿åûngÖë‡õ*î4½Äæ¸pج÷=‡ñiR¿½*Þ‡ºÝ™ §p¼êÝ™ö?#^C¨Y¿ãç²ìþü þÚQ`§¯Ëqñz–?:9!Óéeº»øXKæ¤W@cáéx;}>ËÀ ©8=¸H«–S̵“m/žÇø²Ú´ó²†ilKÕ`Úðt9ðiœ6 [ºQ´ÿ½V=âFéq2LöL¦ýƒì1Õcµ'}è~p=ª'P¯ç¼ªpÁ•¡aÒD#r¿?£¼Žiý©ý œýÁ;‡ð‰†™^È•3f£ËšØ2_°ÞüÀz§Êr(:ŸËÜ*9À×7?°Þüp‡é¥åÚáàåmaeš~ Éw úTéÍ•IØišì0lļÃ×=#3â:2d`˜™‚C¾P-µ•ï+Ö{ûa½Ó‡(gµ·àë›Xo~¸ÓôfZ©Š™°sú〯ãÌ€ƒ Ã0 Ã0 Ã0 ÃDBø– Ã0 Ã0 Ã0 Ã0YÂA†a†a†a†a"ƒ Ã0 Ã0 Ã0 ÃD˜hXókø×ŸÍ 4Ó®b<©7§-Se'ëͬ7?üÈõÒ„“ÜÎùõæÖ›¢ÐKuø–ïd|­þ‰LáGL²ªäù8šžWïo´j@¾œp·Þɲiÿ <%?q4®¼=öGagùlë üç•=™ê1¶'šçêN çó³ðûÂý_‡¬2£ ŠG‘ꥭjlmŽ¿¼{^þ¾B=^jªrñCò«jô¼CòÝ8Ø]WžYGÎóö%;ü0º[F2¹Õ;vë}A˜ÊG%'ÆqUËésíœÉ+ß@Yí-ë>5„Ò‡ûïÚ܈YûõþÇ0–T¥rn›}Lÿ±£¢dx;¤×#n†UÐÛûqu*Xqn§–éÏ)¡pä[]‹é¼¨þ(IbToË€‡¸lVY²Ç†„qæâºÿ×›yC<›"Xjnõêù(Mô‰º’âsëKc5oó*"²3gXo~`½ùa륥÷L/ìôûS?~¿ó¯»ÅíœXo~`½ù!"½ï¶&q5~ß”uºütÙ¾x3ÿK†f­÷ÃX.‘œÕÂŽN±ï¼ø$Ðö¹ÞaÂPO&¦ª}n7ÓþºÏX þ#ŽbìŽØ?bÉ dðÝ—°\Ü´»êæè-‡Þwãí3zC@å¶á0^؈Wj;°ç­SRN™ë+/á¸Éñ¤hÊÒºßÛ+7 vœ@ù¢¤0¨^W"œb—cN'CrSù(ä-ÿØ ;€cJ¯Ex‚ä»q_óà”rÚ©ž’‘ý:p ¶ /¸z*ǵ–z[cj].½/Ô>exÏÖ«pÛl²_@…íÅjI Ð²Íg,³èÿÃoÕvÕØÚü,–Çè»ûzSL9R©z,—®Q|õÎ+ø GîLƒ^²S4ìYƒš¢ër­zÊFX#ÆÐªžÔóW/ÅŽLRRˆC—`¶ÞÄÀåŒk[“PÛ•ô¾äôåʈ €Åγÿ%·Mv¾¶­Ãã%¨_H[jÝïþZ\îvÈÂþ X¯‚õ²^É4ÐKuÕ|ãü6XPP'¼ÁMngëe½Ö›Që¥w³§p-í7Ëzç×ú]™&Ùjû a£þºqD;}Ô =ÜQ€ø²±5ÄAí¼S/óÏ‹d`§…³>HÇΙ*ŠëíCbÞ"ÄïUûºV–¡i×"È&Òåݯ}~½ûâ@τЫtŒutÊ}ûVˆ:I’^GMó ,í:>ÔÙA—“Ç¢^¼b¬gÓ¾2  »+hËu¾¿„S—c§©ž ù•õ+°Vn»èíÆ–c;й:ÇxÏ!ÈäÐþÞº5Úc=žû!óyY¤µ§§~AˆÞÐû-àþtŸo·z­ûDùñ)£nöŸÃ=Ÿ9~gz=áþ¯cABÃ6ôãõ·€§_]†Dš¡ÊÁ,nÛã u°á¤¬ªûK ×£pNò„bÉvܬ-ö9º°sSùáÎ’Hä×dá9”jÇÛÑ-kÕàérÂÙ7ª[E¬Âš¾OÐý·ÒY©Cé"RvÁcøç óyÖc9ÿ’ô›Aâ ˜ìwë €Ð±«Rö9Ê Ç\÷9› ”8÷=BÛQÔþ'qOt«›´ø¡;¨@äÈ×Ü@Ó¹»qèñ»qÚ-_ñ=~ÿþ8ÎÒ¶ÀsœÜžañ•/ìV=†‡µ nÄ}éQô5èm:P{(À€3¾`…&ÝN'á²øËÞ ¬—õ²Þ['*½ÐÜ׋<E¸~ÓÜÌôóÍÖËzYï­™Þ€w®ŒÐj»ÓÑÿn˜Ò©ý³ã0Ñðƒ…ÚÑ#‡pËœ~8~pøfã ·ƒÙpG{æa˲ ñžv ‡püd'†ªe}E»gß*Oξ…_¯tø‡Ô,ðoK»dÄ…¶Ù*rœ…3ëvp‰ôzWÈlr0íóuã·“òGµ#ïÂ]û|MruŒ×"S;H¨žš!YÆlë¸,ÚŸÒeª'èx‹@›5é÷[ÖÃî:¯ ½¦û­íσ÷§û|ýDyà ~ÿQdjÏ× ®'Ýÿ "|¸DE1º¤¾¥ÒŒüÙ3ÏbyªÕ•Ùð€txýH¿–…±Ä>ÉþcVj¿àÜNôcªe¤ÄÝóo(‰<=]D [0ÉUÇ’4”áñ;*œñÍ[ðñÇT¹>Õðò8ºXåÅ((¬Ã=´ŒÒK6[ã^Òí1Öã¢`‡¸À#Ÿø.®°kU%F/XeƒíWÃ$üÇ ¨í©ŒkS8ÒŽ ½+w.áÓ#ÝòÛÅ”üFç@ µzÀÔ“51\ìÿ4óîÆìÎï+…‹±»ÍãªÄqñÀpÖÃΞ/û“h6ž¡¡âC^ƒ^“ü,9 2¸`f §ÅŸ8{õ¦ü›Xo~`½ùafé¥dë|d[!VkYnp;çÖ›Xo~˜¤ÞÁïq] ƒ{®¶_< ¥“D8KñÁ~Ÿ£8ŒÓÚÁº¬ßJ«f#Ö;ä8ö`à^•Ã:Öѯä7úð©Î(h«ªÂBa­pÌä| v³&PïÇçþ#ƒ“J ½™C$N÷'i¨„øØ¥7(À̰ºoŸ¯E€K+œò^\rÝ>êýÙ$7ÜQsOxÛ3Ðawîí@H=W ßµ›žÐ‚lðë¥ú{ìÑt¿™îϰvŽò:^<òŠq{ºÿ¸×,?‘üo×°ýôz‚ýß ŒA†­Íê‡"V÷¬øÑ؈ŠX½øëLÞ"S£Š¿´{¦É‰ŒÕm”t¨*—ÂÌ—‡PP»A:Öò$+`–Ú¥Çh߸þn.•ÜÏÄÿØ…-ox¥h‡Jçp;Pû¨ÚD ãíJ'q.ÑÂâ¬íñcgø–]¶d÷{_ã_* EÈ¡`E¯*åwšrâ='(qír1 ì¹n/ïžBiÊ*¨-Â1Ó/ù4äÉ¡qìü·ªj;¶Õ_é9Ùö|¬;''IcCÇ¡Ê,s%è»ývêÎ`¢cB´ç­OÔ8i;W–Á—­0É3!Ž{d8¨Ûòä°ÞBTío¨§«Em_¨Q€l‚ QèÍ+·xþ£Fú¹sè­rðA†Zvã`û¨œƒAþ`$[íHE,e€¡å°*Lœé@/.!¡³~¶¡±dG`EBN6’¶3\ðP úò; 9(G®½×¬Ó³VšÊG%÷ ²&F¾ÖÛ.9 ‹µ\@™ ã!½õÉoq³p‰3gCs¨|Vöx‘í8„Ag1|fÚ öËa®ìŠD‘ 2¨ íPìÆ}4?Æß‚oÄÈŽzWá½x²¦¥ßüS=Ô(оPÈd5¼añØ÷ÆH²¿üköÜ 9bЛɞßÿœk̼eÏ,”L¶Dúnï꬗`½QÃz /½v¸ÏNS–Ðïfcm$¶p;¬7jX/q§é%V—ÎÂõáôV)|+çÜ¢Éáý™ ÅXP¥¿fB8J÷c0so6Ñsc%vÏ-õª/¼aλhìéÁ@E¹\í!\ôN’+o&djýSûÊ´dì¾Q„3¬4QÒ0ÏÛ³®I— ,¨™ÀmKcƒÓµhõ´Oªý}ö„Õ£¡`ÃÑŽ ÎwÛp^Az}÷Õ&+c! ½ ÷§ÕÎ* D­>båuTÙEÞ•]Ìþ£CÁRáç&…ÿ­I«'ÿ7t¸DE±3ñã˜5lBF,…\f6èÔ(©üÞþX¯eî,‡`öâšØm¥åÛQ•†Cj¢Ga¡2C(ÝNCyS=9Ëi<Š’U7«4¥1È“¿Å•Î-Z‘!tÞ¡÷ð׈m÷—7Ùc@ÎêY:Cìå'³Lçe‚Ò`¬òøNOÀi†ÆÛÑ5߈ < ®6Ë—8ýÍ,l\3 ]ÿ©Òúèü@BÈv©å%¿‰V×ü$[Œ˜ÚOéÇ¢üé½Mã ÏŒ©zrÅ 7Ì 4tÝïÌÈh§ÚeäX¨\ç^‚S¥9Þƒ¢yj; ÖËzý°^ÅTè¥ † I‰Ó߈ò Ž}·3ëõÃz¬×L”z‰Íqá°Yïc&zãÓ¤~ÿ{U¼ÿu»3Náx;°üWÊ7_u¨Y¿ãç²ìþü þÚQ`§¯Ëqñz–?:9!Óéeº»øXKæ¤W@cáéx;}>ËÀ ©8=¸H«–S̵“m/žÇø²Ú´ó²†ilKÕ`Úðt9ðiœ6 [ºQ´ÿ½V=âFéq2LöL¦ýƒì1Õcµ'}è~p=ª'P¯ç¼ªpÁ•¡aÒD#r¿?£¼ŽiýG·ŸhžÄÑ&ÿ7|âG†a¦råŒÙè²&¶Ì¬7?°Þü0UzC ,‡¢ó¹Ìé’|}óëͬ7/P`”2¯‚£·…•ehú%p$ß)èS¥7W&a§i²Ã°Ió_÷ŒÌˆëf† Çr/TKOåûEƒõÞ~Xo~˜*½Ùåìò|}óëͬ7OЪ!+s13ƒ™êœ2^8ÈÀ0 Ã0 Ã0 Ã0ÌMø– Ã0 Ã0 Ã0 Ã0YÂA†a†a†a†a"ƒ Ã0 Ã0 Ã0 ÃD˜hXók½”é …&9ÚUŒ'õæt‡&ˆóØ:Ãì¿%è\}Ël1>ž¨‚µDÖ-U= Ã0 Ã0ÌC†‰×áéær¼Ýr?{f/â»ñö!&‡ò¨"c_bدå_݈ Œâ«w^Á=Zl¢ò ”o¯C}o7®³ic*‰| b/<‡ÒB*FO=ŒkçLrà®ÍA%­9*ÇÕÃa,©7庣0G~wö¹™èÔk‘†¶C@=å‡P½®RJìò .™EоŒí ´3 yOÇ_þ#þ®E“…Öy^,¿E?£²qövr\×­Ïìnå¬ñKÝåh”öø¯®D}Œ¶Çn¹=MvҌ۷¾´ý^-Cb÷lÉóq<‚nyó»¼ÎîëÖ ~œœ˜Öä&&8˜@Ñ®ûCê #k;sdªgRöŸWTödªÇØžt}j†Âׯž)=§ÂžkÂŸË Ã0 Ã䃬2£ ŠG‘ꥭjlm$gr7ö¼ü'|…z¼ÔT TmÇKò…½²XF„ó¼} Æ?Œî–‡‘L.AõŽÝz_¦òQÉ z!QrúP A zYí-ë>5„Ò‡ûïÚ܈YÂ9Wû]†¹mö1Êqo‡ôz¶ ¶ z{?®¢å›·çvj™þœ G¾5Ê‡è ¶3gþ(î‡[0¬^=¥‰>4LŠO—lêJ }Ñ8Ý·jŸúñËékéO‘ýA׫å}úžÄE]ævðnkWã÷¡¹D òÌO—Mà‹ˆèlÈZï‡=8à´’³ZØÑ)öŸÚ>×;LêÉÄTµÏífÚ_÷K®Ï5žË Ã0 Ãäc&Ãâ¦=ØU§úÏÝô~¬³4Tnãõ#ÝZB½ƒÿŠT¦Lê¡Xš@÷{{åfÁŽ(_”/@õº_Ï¿xi ¹©|ò–ÿ@ì…À1¯e2É…“Þ¼ÐY TOɈÓãOÛ…Ü *ǵ–z[cj].½/Ô>exÏ0Ùì*ùQƒÞD°Fª±µùY,—=×—\™ * &•ªÇrÙ•]v õP×vy{é=½ÙÔc¯×n~m[!†ÇKP/»e½YÔÛ¯zÓë >]´U÷­ÃÀeYSÖ×ó.³t%®}azý¨5®õ†…ÐÛŠùiçiŸ;‚õÍÛæ'¼úLöÙÙßlÏd׺^iY†öô¶‘’ÃÐnn;)Àñ®eÌf ,¬õvg¨ºGá‘i’­Øó–øÈ@PoöpGâË('HeHç]fÉ2À°Ì¤cçLEˆõö!1oâ÷ª}]+ËдkT i™ü®ðëmÜz&„^¥c¬£SîkÜ·BÔI’ô:jšW`i×y|ô¡ܽèòXôáÀ‹WŒõlÚW†áAawm¹ÎWÔëîÔåØiª'H~eý ¬•Û.z»e¾©è\c¼çdrhoÝm±Ïýù¼,ÒÚÓS¿ DoèýpºÏ7ˆ[½ŽÖ}¢Þ/RÆÌ·lŸkéõ„?—†a†ÉÆL†‹G^Ážw¾sñâ-³èåÜ` sUõ X†ì¹k~ &F¾ßèÅ^Ú…* ž »÷a7Šd¯ÄNcù¨äŠB”n?êfñyá -#‚ä{…#¾ß­Rr Ћ ½Ñvù"`Î:} eT.À¬ñbâ¥GÊšÿ‚˜prŒvšêñ°ÿRYˆ›—}/P ¢t¤--Àà.olƒfºñA eµd°ÄêQ•ú“Ø·ÛåÖééÃxd—r,¯©”ß3Ž»• fXõ¢ÿ>Ô7¨òä´Ö|cõ®'¥J=ߪwœXõÝrX{ÞK qH?ÐeÅ}¿Q8í«iŸAo&½]ÃÚa =²ÞŸ ¼ð;ün0D¯([ƒë8í h3ì k‡\Èùz…´§e“-õdcçÙ«71ûþQmÂßߢûS>Ö?³NËœß5¹/‹¯¾9ç¶¥ñ9ý2;àh‡Ø¿~®røä0ÊŸ“ÀZát1át}q°c‹Pt®‰([)œ²]³ñE@y‹t½Bï*/>ãËÊåÜ m/ªíUHBŽêSû•C»ðç+ä÷§š….ÈqŽ ‡õÅ+r;¨ɽ‹pÿ¨Ê†°Ï×…ßNª÷þ+{â¼ã,»ê9prñ_êó wµX¶3¬¾;Žxp;XÇȵ§>_“=¹´˜=¦z „£­å:‹$ü¼þöÜ´k¾±ê99¬¥f½A÷› hîOQ\ÇìÈõ¹æ&ø¹Ì0 Ã0L~ .QQ ¤.©ïc©´‡Ÿ=ó,–§Z}‡\X"ÜçpÏgô"àJ<·ýØ ^¨GÂÝ£n(‰ü(Æþà¤eöÔi§Þ$'çü9Ä’4”á`Ý ™ÞùñÇT¹>5wƒ<޲Ê‹QPX‡{ÄK”Q e³°H·ÇX‹‚ω—®O|Y®U•½à-K¤—h‡P;så>Õ.Rò¯‰³g/KG²Ux4Ï;•éŒá´îÅ&‡Ó¢v¡#Ÿ4óîÆìN/|W cwë ð‚õæJiéO°ú¿Ý-¾YõŠºBõæJ4v‘óõ 9/+`!?v¦C ~ëúkÔÛùÊ«{ÕÇšKf²/n𻄋ð½Þ{çðnI!žŠ_þ»TœÓ{²€ãßã¬Þ ¤j;¶Õ_½ce2è é$‘½Ñ.‡6R(íœë£3 ˆÛªW8ƒ‰Ž Ñž·¾bǤí\Y×ܺ&y&Äq,ƒ“9àêñ7Uûê±².Ô¨@@6Á†(ôæ•[¼Ž¹>×ÒÈõ¹Ì0 Ã0ÌmÁd øƒí£rùb.‡M¨ê”{U‰I@Î+’öKCÁCu(èKÈï4T m¸ö^°N§ë›ÊG%÷ zMÔp7.99ãnç›2ÆCzë“ßâfágèACs¨|Vöx‘íC/bi3lëޞϼòÀò&½&;§”Y(×ü½–EO÷ðX kWÿDoùqê …zÉÞk/ I½í‹Ç¾7ögƧ—ê/¼µÃ7ð®ø^º¢H¼k¹Iïиø;k#™ì0¸TVÁüè'T4œ—â&ú­¹4Vø¯¯ùz­.…ëÃ?è­0RøVÎB“Öú3б JÍ„p”îÇ`æÞl¢ç:Æ*Jìž[êU_xÜwÑØÓƒŠr4®Ô7¹è$WÞLàhÏ<<•!e>ƒÃ7Šðàóጒ†yÞžuMº\ -ÉŽ \Ѷ468=ûöPOû¤ÚßgOX= 6í˜@á|· ç¤×w_m²2²ÐëÁpZí¬²@âžú¢¼Ž*»È»äq®Ï5"­ž\ŸË Ã0 ÃÜB—°´—­Ä¯ñÒÂV5¹#­"ñ«zgÒ)É%ìy¹C/_é³¼eÆ%«h–i=LBnë‰ ƒ–n$‚ê!r’« £¬©.eMrõRä,•åLÖHPLÚ„î%#ÝåMv ÒêñÔa¡ë¢zV¥<Çg,¤×dg Ö²¥.äuï÷.HK\ÖvdÌ| šHÒßÄéåö;|™¸‰ú"=ñ£aBHÂYVQMühMèÔ%Ð ºËJƆðû÷ÇEaWY97€Ò¦×„I/­Ì@¶ÉÉÇ•ÜSÖ¥—ûІ=úLöo±3ȉžœ‘†@Mæè'èzåÚžÎÄÞëKí¼¶k>†³XâÔ™øq_µ§°¼Ø¹ÕÄqú¿;ÃÄ4&Ý?i"áY~ð gIArÔ¶ÈÉ 5±žœˆ&ñ;7M¿þúââûª?'ä oš»3a^9á!çs~%*57ÔDŽDP]nûiÿÚ eëRC=¦ó5ÙI¸m²' t×ï²Ñ$'» @ÎÐÏFWÎûTB‰µÃNðè¶³²L:iG\ŽR^˜*½¹2 ;=Á &ù”À×=#3â:2 Ã0 “W8ÈÀ0Ö{oáë}Š´å!#@e„/É9-È2{„™Þ°sú〯#Ã0 Ã0~8ÈÀ0 Ã0 Ã0 Ã0L$„/aÉ0 Ã0 Ã0 Ã0 “%d`†a†a†a&8ÈÀ0 Ã0 Ã0 Ã0L$p‰Z¢Ò·îùŒ‚&ÜUŒ'õæt‡&~ôØ…ýTÇFk‰I&Z&pjô椉ª†a†a†™fd˜øqžn.ÇÛ-‡åZóñÎÝxûŒ“Cù‹T‘±/±Gì—˜ä&Ükc·®ƒíÁT>ùÄ^x¥ÖòÚ‚ÑSãÚ9“Ü¿÷8®~ cI½‰Ý¸¯yÔ üÎ>÷1ûÑìh†v¨§üª×É…ÿò´–¸%³Úç®ß$Ÿ òÚó^ùdqVjˆ~¥㪠ä`¯Z#^±áv¸„eŽö›Ú!š¥!ÅïÆ«Ë˜Ä½@ëóËuýåúûù›Þ­7 ü88›þ ,”ß&8˜@Ñ®ûCê #k;sdªWðŸWTödªÇØžt}j†p eD f*¾ç—çwÞz¶øŸ]&ʇ=w†a†¹cÉ*“a1ª± x©^ÚªÆÖFr&wcÏËÂW¨ÇKMÕ!râ…eûŒ~Ý-#™\‚ê»õ¾ L壒ôò¤äô¡@‚"@^ùÊj‡lY÷©!”>äØ׿FÌêܯ÷» sÛìcd€!C;¤×#^WAoïÇUÔ¡|óàÜN-ÓŸSBáÈ·¢Cy£|’œù£¸î·`X½z>J}h:˜Ÿ<.¥Ø•ú¦€Ú§~ü²7À@Ddÿ»­I\߇æ-È3?]6/"v ³!k½öà@€ÓJÎjaG§Øw^|hû\ï0a¨'SÕ>·›iÝg,G1öç¹Ð?¢ç)°ÝÇxË'Õ%C1•7>w†a†¹“1f2,nÚƒ]uªÿÜMïÇ:›ACå¶á0^?Ò­% “܆z@–&ÐýÞ^¹Y°ãÊ%ÅK P½®Äî)Q=ÿ™'¹©|ò–ÿ@ì…À1õÉwË^謪§dDg&h»ð‚;PAåã¸Ö²SokLí Ë¥×ã…Ú§ ïÙz&›Må½reƒÞÇ–`ª±µùY,Ñ÷K®L•“JÕc¹ìèÅW"zäN#Ô“^Ûåí¥÷ôºS}Í 4µ~‡×¶bx¼õ²ûØ›õ@½ýõÒ&àz¢§‹a£êfv¸,ëqÊŽy3dv€®Äµ/Lo ¹ÖSRˆC—`6‰}65o›œðê3ÙTC¥±,(ñ®eÌf ì¦õv'¦ºöðÈ4ÉVìyKüãe ¨7{¸£ñe”㣲¤ó.³Šd`Xf Ò±s&€Š"Äzû˜·ñ{Õ¾®•ehÚµê h™ü®ðëmÜz&„^¥c¬£SîkÜ·BÔI’ô:jšW`i×y|ô¡ܽèòXôáÀ‹WŒõlÚW†áAawm¹ÎWÔëîÔåØiª'H~eý ¬•Û.z»e¾©è\c¼çdrhoÝm±Ïýù¼,ÒÚÓS¿ DoèýpºÏ7ˆ[½ŽÖ}¢žÛ)CF=žÃ=ŸùŸKøÎ—É`®'¸¼ÂüÜa†aæÎ˜ÉpñÈ+ØóΗâeA8/·¢W:Þ9˜«ªç`xÀH0Éîš_‚‰‘¯Å7z1!½]õPψ°E2[`§±|TrE!J·Ÿ@u³ø¼ð†–Aò½¸Ö²ß­Rr ƒNÎ:m““>g>†2*`Öx 1áÀKYó_ΘÑNS=¶à_* qó²7`€†GQ:ÒøXÞ'ŸxÏÝ3%®fš^7Ýø …²Wèñ«GUêObßnl–oX§w¤CŽí‘]Ê^¼¦R~Ï8?@¬5Ã*ëá÷  ¾A•§@EÍ7V6DR:ËÔCOß/JG\}·ë–÷­}.ÈÑ—ÃtYqßoNûjÚgÐÈ$êyíñ»q:¨¼¨«×qÚдŸ¨?¬,Î^½‰Ù÷ÿ‹ÒÂßߢë®?ëŸY§eÎï…Ü—E€ˆWßœsÛR„øœ~™p´Cì_?W9|r˜e ˆÏI`­pºˆ˜pº¾8؇±ŠE(:׉Ä”­NÙ®Ùø" ¼EºÞ¡wH•Ÿñeårî„¶Õö€*$!Gõ©ýÊ¡]øóòûSÍÂNä8Ç…Ãúâ¹TäÞE¸TeCØçëÂo'Õ{•=qÞq–]õ89ø/õùÈ»Z,[ÈVßG<¸¬cä‡ÚSŸ¯Éž\Ú?ÌS= ÂÑÖrE~^ {nÚ5ßXõœÖR³Þ ûM´ ÷§‰(®c8 gÈs(ùݾeŒÏ†a†aî4‡KT©KêûX*­gägÏ<‹å©V_àÁ,Og‰p¬UÏJÿ1WŠå¹èÇñRD™î^tCùHäéi¥Ê¹6É)(ðbIÊð °î„LCýáØcª\Ÿš»AGY åÅ((¬Ã=´Œ)›­€Eº=Æz\ìziv­ªÄèoY"¸|ºÜ pȼmN\§:“åâ@Jþ5qöìeéð¶ Ïëâ™`ç71œÖ½íä[Ô.tä“fÞݘ=pÃÉjèJábìn=Q_°Þ@r­§¦‹ÃF ²ÐÇ΀˜ 9Øéfð{\×_àÞÎW^Ý«>Ö\,“å‰*áˆ÷{zîe/°v ‡.OÈ¿¨šXïÓ3üaîÕ9ýJ~£ŸêŒ‚¶ª*,DÖ’óO»‡Y¨w‰ãŽCúQHOôЛ éìîNžWŽŸíÌ*½káíA73l§îÛçk`çÒ §¼—\·ºßLrÁí`Vüíh°;÷ö ¤ž+ƒˆïZMOhA6øõRý½v„è ºßL÷gX;Gyeç@ZöÄÕ3D>WCƒÆŠàzÂ0?w†a†¹ó0¶6+Ç!V÷¬p"6¢"VïY=@¦J™ÖKi’ûùáò j7HÇZ:·ÔÓ¯viÆ1Ú7®¿›ËG%÷3q!¸;Æ–S¯ Ú¡†ˆ—¸Ãí»}Tí ¢?…‰ñv¥“8—ÀhaqÖöø‘Cæ¶§,»l=Sù4¹œkÎ4Æ–É/4|ÁÊ2 Ïûã8«wå ŒŒ®³j;¶Õ_½ce2è`ä$‘½Ñ.‡6R(íœë£3 ˆÛªW8ƒ‰Ž Ñž·¾bǤí\Yלµ&y&Äq,ƒ“9àêñ7Uûê±².Ô¨@@6Á†(ô效®£|~Í] ·"ÄðÜa†aæÎÄd øƒí£ré@Èaªgƒz0e Á·z„I9ÙHÚ/%Õ¡ /!¿SOz9Úpí½6`V`,•܃ê•QÃܸä4(,Öre*Œ‡ôÖ'¿ÅÍÂ%ê\ˆ†8æPù¬ìñ"Û‡i3xëޤϼrSys=C:åUÕ7õÌB¹žˆðµ,zö‡ÇbX»ú'zËSW(Ô›¿ð^{IHαxì{c¶‘\ë¡L‡…EÁ/‹ãfcm$“2šÛaué,\þAo…‘·rŽ šôÕŸÉPŒUúk&„£t?3÷f=×1VQb÷ÜR¯úÂ漋ƞ T”£q¥¸ÉEï$¹òfG{æá© )ó¡ì¾Q„Ÿ`”4Ìóö¬kÒåjhIvLàŠ¶¥±ÁéÙ´‡zÚ'Õþ>{ÂêÑP°áhÇ ç»m8¯ ½¾ûj“•±…^†ûÓjg•÷ÔåuTÙE楄 –ŠçWR%ðuÏÈŒ¸Ž Ã0 Ã0pa" -›À—%ž,ˆ¨È"+ƒ™þ°sú〯#Ã0 Ã0320 Ã0 Ã0 Ã0  áKX2 Ã0 Ã0 Ã0 Ãd †a†a†a†‰20 Ã0 Ã0 Ã0  d`¢–¨Ìz]õ¡É wãI½Éd -Ë·¿Ê^ÏÒDUÃ0 Ã0 Ã0? 2Lü¸O7—ãí–ÃrMüxçn¼}FˆÉ¡üÅªÈØ—Ø#öÕØÚü,–[ËûÛò*ß@ùö:ÈÅ]kt1•D¾±žCi! £§Ƶs&9p׿¿ ²ÖÚ1Ž«‡ÃXRozÖwö¹±×,m‡€zÊ¡z]¥”Øåi­rKf´/­~ëü’ènÙ©e“@Þp­«ž'(Ȱhz¥‰¸ÿ_]†Ä$ΉÖ×ëèËõîó7¼[o(ríxÖð·pÖáŸëùí ±?¤ž0²¶3G¦zæ}ÿyEeO¦zŒíI×§fZF´`¦âûÎæwψõ›êúÍžT= Ã0 Ã0LYŽ·|ŠUÍÛÿñ >è¡`ÂF|ÛBŽ— ,Tuÿ ¯éÖÇ(((ñÓTºÜ^ôñÛùžÛ†î÷öêý~Lå‘ükñ»8¦äôr —bô[Ž9½¤.MØöS½exO4Ž.÷9†·Cz=dÏ£ûéU/Þ±¤V¸±í¡ó2—Wú†0º¸v+A†%“2¸Â|:¾·ª+ÈY½öß®6Ég[áו=™ê1îÿѼì8’‘°ß=ÃsDÿnµ$PèúÝM#S= Ã0 Ã0ŒcaqÓìªSýçnz?ÖÙ *· ‡}Á|(nó–õà{‰£—ÆòEItŸG¯+ñõü)¹©|ò–ÿ&˜‚ º7 :«êq¿ôÒváµOAåã鎼©t¹ôz¼¤!“;òžn—mîÌ 8¶ãÎ`¹äÊdPªTªËeá(¾z‡‚Trg«WÏÇSE7…1Ì—…%¨É l¬ÄFÕµ.P2ÊZhÞV)ÊxeÄkÛ 1<.Ž—Ç|‡/ÿý2Z†ä®@( ¶ÞîÄTçL“lÅž·Ä ” ÞìáŽÄ—Q‹ªì€¶ÏÅW™P$ËÃ2`Ž3T!ÖۇļEˆß«öu­,CÓ®EPÉBZ&¿+üz÷Åž ¡Wéëè”û÷­u’$½ŽšæXÚu}¨·+E¼xÅXϦ}evWЖë|5A §.ÇNS=Aò+ëW`­ÜvÑÛ-kS;й:ÇxÏ!ÈäÐþÞº5Úc=žû!óyY¤µ§§~AˆÞÐû-àþtŸo·z­ûD=R†Ì(,½ç3ÿïbúïž¹op׋ù÷“a†a†ÉŒqN†‹G^Ážw¾/Y±z¹½Òùò ª±ªz†¬õüîÅ+¯ §3Õj0îš_‚‰‘¯Å7z¡#½]¸¡‚s;Ñ}j¥í»QDáäšÊG%W¢tû T7‹Ï oh$ß+œòýøn•’S €wrâi»|0g>f‡8—ʘ5žBl‡–5ÿ1áÄí4Õãa þ¥²7/û{óEéH[à˳S^èÛ\‡›§vŠ×{7âå[fh<,Ú]|((ãi ?Ýø e·¾G|ÄêQ•ú“Ø·ÛåÖéÁÌ^œþ÷!\_X‚¢ó}ørlÊK€w[“h:¨?âžÚ¸ñY¾å}%»(·\ÄJP3Ü'÷ý>Ô7¨ò&þþÙ¯?ëŸY§eÎ}/÷e` âUÀ7ç(B|N?_~;©Þû{TyúØÎ²«ž''ÿ¥>ßyW‹e 9Ãê»ãˆ·ƒuŒüP{êó5Ù“Kû‡Ùcª§±¡@8ÚZþœr¸ÃÏKáoÏM»æá«ž“ÃZjÖt¿É€–áþ4Åu ‡ô[øJG>I¼÷r$ªz†a†aîPÂ'~¬(R—Ô÷±TZÒÏžñNámíÇF¼"œµp–ÇZõHõûVËçv¢ÄË$e ¸{Ñ å#‘ÅØ´c->ý#uÚ©7É)( †t·|¬;òÍ[ðñÇT¹>5wƒ<޲Ê‹QPX‡{.hR6[Î{º=Æz\ìzÙv­ªÄèoYÂ]þ®Í; /êqÌA¥ jÐÇ?N9'.áSárq %ÿ†q=1ª²ƆpÌÕeJYGvUªÏ»6„1œ>û_òÛÙ«7åß0¨·ók®‘ÉòD•pÄû==÷²X;†C—uH§j6b½CNÏð‡=¸w¶ü:Öѯä7úð©Î(h«ªÂBa-9ÿô±{˜5z'8î8¤…ôD½™ÎÞé^`àäyåøÙάһÞt3ÃøB;˜öùZع´Â)ïÅ%×í£&˜4ÉM·ƒXñ·g =ÂîÜÛ?€z®  ¾k6=¡Ùà×Kõ÷Ø¢7è~3ÝŸaíåu”Aî´ì ìªßBù|ð]ƒ÷‚ë ÃüûÉ0 Ã0 Ãd‡1ȰµY9\±ºg…óµ±zÏê2żøKcïîß;/Ååz+.¡ vƒt¬¥£K=ýj—f£}ãú»¹|Tr?‚»±l9õv¡j˜‚xù=ÜÔ>ªöÑŸÂÄx»ÒIœK`´°8k{üÈasÛÓ–]¶·¼ÊhÀ" äP0&ASIß'*¨a}¦pò³â©8ðå¿[™ czODTmǶ:à«wt¶ÂÇ:¨6Ido´Ë¡J{'çßúèŒâ¶êÎ`¢c˜ž’ “¶se™¸0É3!Ž{dœÌW¿‘¨ÚßP•µp¡F² 6D¡7¯DtåïðÜzK`øÝË™¨êa†a†¹ƒ1(þ`û¨œƒA:^r؄ꢞ_`Y=âgµ`¬ûS½9ÙHÚ/sÕ¡ /!¿ÓPr´áÚ{mÀ:5¬ÀX>*¹Õ›¥†1¸qÉ)hPX¬åÊTé­O~‹›…KÔ¹”1@峲Njl ¤9ÿºî3¯<½¼7;ƒ21Èú.íYÔèØ9-¸‰~=§BóŠl2r%…oå\ÕØÚèÏd(Æ‚*ý5ÂQºƒ™{³‰žë«(±{n©W}áëz+Æž T”£q¥¸ÉEï$¹òfG{æá© )ó¡ì¾Q„Ÿ`”4Ìóö¬kÒåjhIvLàŠ¶¥±ÁéÙ´‡zÚ'Õþ>{ÂêÑP°áhÇ ç»m8¯ ½¾ûj“•±…^†ûÓjg•÷ÔåuTÙEæ%q –Šßá¤x>H‚÷ˆLõx1×Ã0 Ã0 ÃdOèêö²•ø5^Zت&w¬ÚŽ—~UïL*&¹„=/wàiÊxÐ’±ö°•%44é¡iE)ƒpxõ0 ¹­'‚ Zº‘ª‡ÈI®&r´¦º´——4Ê•óî,aé 1pÂFÓ„Š&;iõxê°ÐuQ=«RžãCËÛÐ9º&~tÛ#pŸs:4‡sÝ%rùÒ~ïÊ ´Äem‡1óENüˆkØùŸÿ‚CÞÿ'Ön»8qý ÖÄßáËÄMÔÝ@SëwxmW%Ë£5cCøýûãØ¼«ÖD´Äe*o™øq_µ§°¼Ø±SM§¯~†‰iLºÒDÂ3óÿÎlÿä¨m‘“Ajb=9Mâwn.š~ üõÅÄ÷UNÈ!Þ4wgà ½rÂC:Îçü:KTjn¨‰‰ ºÜöÓþµÊÖ¥†zLçk²“pÛdOè®ße£INxÚTOxhju.ômBfjÈyôp {ä¹äÐþD=¦z¼×Eµ±åtÕcÒ뜗¨ƒæR°V—ÐûWTï·DCúý鵇2A¼íÕuTÿwî Ϳÿ{šLõH¬ßÜz†a†a˜ìɰ„%Ã0Y±²L:iG\ŽR^˜*½¹2 ;=Á &ù”À×=#3â:2 Ã0 Ã0‘>ñ#Ã0Ùñù•©qø¦Jo®Ì;s…¯;Ã0 Ã0 ÃxàL†a†a†a†a"3†a†a†a†‰20 Ã0 Ã0 Ã0  d`†a†a†a&8ÈÀD-Q™õzôy€–®ÜUŒ'õ&ããÇÚ>´Lãþ*ÔèÍ)gºÙ3Sàv»3áëÎ0 Ã0? 2Lü¸O7—ãí–ÃøÙ3{ïÜ·Ï19”¿x@û{Ä~‡jlm~Ëc—\k“¨|åÛë Wb·Ö*ÃT> 9­‘¾®Rî¶ñØä^_}W?†±¤A^îª+°÷ñ·nOyTfiÝïíÕaöÚá®ÍAem¡ünÛN6ecyO ó5æm•¨Ñ·1´Lá])Õ½ùk©îô—‹˜¨tÜv[Ú':ÄïÆ«Ë˜Ä½Pò| GÞünrK’“òsà´ë¸Æ}+¿—¾ {ä踚!hÑ…Û¡õØÕÒ›ö¯ÀBýÝÖp–>4¯ÀÒ®óøèC½CãØ/¸Ñ‡zÕ ·Ô[ªqàÜlq>HL ís-_6‘vÞÓ¹ÝTÝEê»ë|M˜Ú‡P×`Ân?ٴíUýù´sÊ®{ÑýOÞ.[· öÂs(µ×®çr®Ïw#†÷»®‰Îýè?vT~g†aYe2,F5"ÕK[ÕØÚHÎänìyùOø õx©©Z–#7mÇòÔ%È¢¡ˆÚö%;ü0º[F2¹Õ;vë}A˜ÊG$?·SnÛŸSâ ;ò-)–ܵ¹³ÄÃUíwÀirˆþ:èmUùæ-êE 9Žñ–O0ªU<¨|®í7œ¯Ð[V;ä*?„Ò‡¨}ÄËÌ*Ëžý¸Šºìì9óGq?ä/À@´¼ŸDÓÁ$.êm])±ïöf4Ó´}~*Ö/nÅÑù°|/ùm/ž²óÐÛ¹=¡õØä¤)ÝG; °¶y®–ç†e?}N.BÓó÷è=š•ex„ Ú¹»UɯˆÏ™À€Øö3Û­±œX¥;€óõØ>¢Mš¤CÙz_Ýòýœ¨êϧS÷ÿ2“9б?ègµøôXÏå\ŸïLï'`˜ÛæèæÃ0 “Æ Ãâ¦=xåÕ¨ˆÕc׫”™0˵O¯éÆ-–3ÙϺ]¤ªíØV—Âñ·:´ „†8æôµ9ÎúÜB`Q\õ¢7ÿ1Ý‘N¸êæC!å#’{ G»Wÿæd P¹›—Ó®iòòbô%ôðÃù¯Å¼Hþý-;1¡å6¦ò„|ðŸç/>î,·œÚ& —ý¦óM~‹›¨Ä}â…›(XZ‰‰¡[¾ÌìTBñýŸÉqýÝe®ì÷ }ÜÃ%( f;¶>cíÛƒ­Ú1¹ÝP†Ã‘]ôIP¾z¾Þ7Í%Z(pީġÕ?ÑRr¨U^ÕóäFµ½1l´ömT΋µO}›š·‰cW;u‘Þ°z‚X-ÎçÐÆb’å õy(f½é2âµmâxûoûAÙMêÚ:×^ÉÄïÀzkß3ëÔ ÞÎÂŽ~ƒQ&dOí_!>q4®T2ê-´Ó©©çS;×ÔCªÊf™n-E}ŒÕ³í"ÍÒe•—Ǧ Âì :/Â9fEFç×ÏØhÚ¯KΔÍÆ/»Í{ÐøËyøæÏ*ÀP2¿èÿ¢ÍEûa(sÃto·¶'6Ñ“[›Ùíóù‘ΰ7óÅMÐýh§§}2ßçAõO†ébçí¾îAö×4«í…(ÂZkŸ¶ßÚ§>ŽMûıÏ;u‘Þ°zÂ0Ù¯Þ¿L÷ànQµ|ïÈùù®H«ßô~‚Ý(¢`…)ƒ’a†a ƒ ¼‚=ï|)^­Øór+zAÃôp ›j¬ªžƒánù}ë¿Õcøãìz³ïš_¢z”x%#í*‚N=òvÔ]?àÄÃÏT>*¹‡†GQ:¢Ü*Èqå‹€9ë´c¿c·Q.™»@ý<¤ÓÃ,ïí¡™ ¶¼X¼XrÑò/¼¡÷iÜöÏw/®µìÇw«”í…‚z(¶à_*ƒƒ+t¢¬ºG|ÄêQ•ú“Ø·Ûå²s6os†CL¼(^“ûšÎÜDýÃ…X-¤äŒ×|Ó§äâ³óì©âšWÌ—ÿ®Ê6¼Œ–!àÝVK' ÝÐûZ•³fí“ñÿ³Ñ܃ú¢ö¾«ñ9@H=&f/Nÿû®/,AÑù>|96 å%f½Æö‰• fXµÃï@}CøKûßߢë®?ëŸY§eÎï…Ü÷–¸I³ ^|sÎ}®E¸_Ü_²ùäâ¿,Óò`B{HØ´K8ÏÕ1Nk©ƒßžMr˜€*ZÜìcÝ)òf{‚Ï‹•û{:•\|2§®; e°ËWTÛŽ }œ!f(íŸÊÆ»=Ã)Êž¯FÑ9ïP€ñËWp%hl˜ás#¸2¨wh¦»9Ä« |A•`L톿Lv668ísà¹àaA¤ÿ¿LŽébçí¾îAöwµX:¬ k¨‘µO~N•)T€øœ!{ßø²rñ£ª¾Õc"÷ûV<ûé=£ù9”Ž|‚kçnõùž• 0k<…ØýŽãêb†a˜0‡KT©KêûX*-xð³gžÅòT« <¨aê{ö,ÆçpÏgôt†&P ¡ÄmpÊêU' å#“âa½ª£Täþ‡c©Á>`ô”vìßÛk”KÛGêôY<ÜÅC?´¯ÌTÞ×CaCrTÚåÓæaðÙ¯:_z)y±$ õøXw"mXDÁçefr\§G(\HÉ¿SËN[„®.Æî–½Sµ ]ò,è¿õWâ5w›Ê6°3 ÖÈI$4ßáK×Ë÷ï&9|ázbT76„c®îB³^N;œ½zSþ Cõ†élkŽ–ÉòD•pàú}ŽË°“ÂýaîÖÃ9i„¾…½~}.퉊àóZZá’» ž`'hàîåu —è™çôœöj‡L² º|¤ËžFµ«¶ñeÁ!Ò.qŸÅ«®§·ÏŒh79yÙ ‚Û'„€v0ÙyeP8­»V`ÓZ QµóŒ°3šëž«ýžz~îÎtš@â¸@øh’Ã;Lö²“'mø!Ô»†|?²:5&ñ|®?Ê´,¬Ã=ô;umöuj0 Ã0LÆ ƒLŽC¬îYáD¨aîô:J‹^_ü¥î¥T ¨Ü¨']úé5ª¼Ÿ.¡ vƒ|0J'–"æj—f£}Nª¾©|TrÊ@»Ú7I&ÞÓdñ¹vY<¤]s;‘kyô}b——÷ÄŒ>ûç«Ë©Þ ñòr¸]xÛÒIÁñR2·ýÇ›&YRˆÒ±ï'õrhetÖ(>4Ø ô<‡“ùpfL︽´à6ë•C£€¯Þ±2t0r’È^F׋{+ËPxãú-§†gK=Ã7”“BNÇÚy}9õ˜É⼆ÞL¸‚ÁÍйAŒÍË.ž]]À]1ºŸ{SÔ%4vÞž±Àž£a¦´È–L½Í~¼íc&ãýìÂê1¿P£Ú('8—úØ)vÚÜÂuÏÉ~¡ç‘ep22¦’‰ I™9Ùç{Öô§01îz:—Àha±Þ`†a3Æ ¥ÀlEïÇÚÃ&Tä›z0e€Á^UÂJ™·>¦á.èa…¤ýð’ÃôÜ4¡m¸ö^°N§ç™ÊG%—è,€Ï²XM!+vã¾uÞ¹Âq•§‡»=WÉuÆÙ¿¨Ñ²`¿é|©~÷ËõXŒ«lÙþ`ÈfU‰Êêÿ6³Ç¿ÇYñ}x,†µ™æa€‚ ¿O|‡ÒR÷±j¨‚—›èRßšWd“Q@Õ“+“Ñ› )|ÛCi2X&C1hÇ3#â…þ~ †öv–4ÌClÐrN96yzs ç:Æ*Jì^NO=AöH™pJ-ç%ÊÙâ>¯áEx0Ó¸rÞö™<55Eëq;…#øˆRÔw©áysBÍv£j`°Eõ^U4éí€á~Îd'9ÁG;&P8ß*c¸Ï õ&ûåShçd‰âÿ%Ý~Â;¯ˆbW´í ÙþÎÕÜþaö‡ÏÉ ´Ð Iñ~4‰ç;‘©~šÛ¡p‰ó¾A™”®z†aÆDè–ö²•ø5^ZØŠ×)õ½j;^úU=¼.‹¹Ê,—° Z*QÊhu=LBn Ç;liHÓ’‹“‘¯J9Û.¨gŸÆ5Zs‹t¹JSTKM¹—‚¢ñ”ÖR—©»ÍP^ÕMs>ˆ'=úO‰÷k K·ý{I)“ý†ó¥`‚³ÄURµ9M*i-‡e£÷Bך2W\ÈeMû½÷-qYÛ‘õøülxmMèbl¿›³‘kw]×}¡ó2xõz—Ϥ! ¿‰ëÇËr>šóa£ÿ¾LÜTó09MüˆjN?AõAåžÂ5ìüÏÁ¡‡ïÿk·Ý'ëíoÖkn7ײ™4¹e*oBf3ÉÛj_µ§°¼Ø¹¾ô»«Nßí  ¹îÔ«´´¢g‰F×Òô¾E¦ñO !œ9>ºeÄ[žÐǘä²7[Þ¸Ã8Mc®õÒ&{h’6{)C"CýYɵ̽o¬£3´×?°ž'ªB—°Tç« °Ç­Ó½Ÿjó œî-Jk«½¨‡xÚ·›p|›v-ò=¿(cÃ}žÔƒíÌ9`nŸ`;M÷T—·¿-é÷yXýAöAò©²ÓÔnYÉ'{Ý&û ç4ÜHÛ¯þ_¼çE?âÏÁsRÕC˜®‹É~õû™r½Wyß<ËHæò|×dª_bÕåy'{`†a‡Ð Ã0Ìm…œ¾_G\ŽÃ”b²'ÐycØç4ܱp»)n÷ýUý3ÅN†a†af$d`¦i½ëZ!HÖ»&{n·^ÓÍž;OëÀÉà^`Æ ·Ã0 Ã0̆a†a†a†‰„ð%,†a†a†a†a²„ƒ Ã0 Ã0 Ã0 ÃD†a†a†a†‰20w4Ûý~µæ?Ã0 Ã0 Ã0 -&~\‡§›ËñvËa¹&~¼s7Þ>#Äk~W~ñ€*2ö%öˆý@5¶6?‹å®È{?ÖåM¸×_v­ïlÄT>ùÄ^x¥Ö²Ò‚ÑSãÚ9“Ü¿õ8®~ cI½éYwÚÙç>Æ^ë:´ê)XÛ½V¶…µÏT¿a}mTfiÝïíÕ‚\÷ϫ˰×â¾uh½ñGЭ×mwÖñÏ 2üž5Òuü½k§{XŠpÛãÁPža†a†aæÇLV™ ‹QÅ£HõÒV5¶6Ç_Þ=/ÿ _¡/5UËrÂýÆWï\}B ä>ìÁ_ ¡íÅóBvz;nÙ†a†a†a˜Æ Ãâ¦=xåÕ¨ˆÕc׫”¡0˵O¯éÆ-VÏt7>ë•ßr¦!Ž9}mvÏÿ]s EqÕsÞüÄt;9æÕ͇BÊG$ϕ䷸‰JÜ× 6 –Vbbäkµ! zo^¦ ‚ÅnQPŸÁžôzŽbì;íïÿLŽëïn(€P‚«ºŒõ‡ÔC™Í'D»‹?;"ÊryåUëóküÌ–‰û`½µï™uê€IBY…ýžA™=µ…øÄѸRÉ(ÃÁAÍsåWÊXPe³.±² M²¼øü¼H Òì1•w˵n:¶©¹Jɛ˴mÚ.9¤Ã[ža†a†af&` 2\<ò ö¼ó¥pN[±çåVôâ’Ì^ðf'TcUõ tëmˆeóv- æ®ù%Ú)§á'P2Ò® y;+@;æ-;壒+ Qº];×/¼¡eD|/®µìÇw«”¼ð‚ÊLPA‘(_$Zc>†2*`Öx ±Z¦)F;MõxØ‚©ô! ¢tDÂÏ×Â]7óAfDdàïo9Ù+{>Ö?³NËœûFî{ë”>brÄ«€oι³Šp?ºe“ˆÿ²L˃É5caÓ®yøæ :æÀÉa-uðÛc*¿i×l|A2)ÖîSvÆ*€/öa¬bŠÎu"q£]Ã9ÒË3 Ã0 Ã0 ÃLw‡KT©KêûX*m\ýÏžyËS­:ð@Ú™Ÿã©ú,z®—Ç÷9Üó9è”Ú¯9·ýØ ë À)«·0”DN=ûÚ±Ÿþ‘:íÔ›ää´?‡X’†2|¬;!‡üpì1U®OÍÝ £ì…òbÖáž ZF”ÍVÀ"Ýc=. v<‡Ò‘O\Ã:aתJŒ^p—5µƒÂS/ó!TÖ‹.YsuDÍUˆö£ís½-v†*|؃{gG×ë/ô-ìõësá·ÇTžä(ÂZ+3Á•á0feAÜèçJP5±Þ!';"êób†a†a†¹ƒ [›•ë{V¥½ÇêÅ_• OP:üúâ/½ÓïÔÁ ?\BAíéøª9`–Ú¥ÇhŸ“Âo*•ÜÏÄ…`/Û–S¶Ú…ÓN½ÿ{qíp;Pû¨ÚD ãíJ'q.ÑÂâ¬íñS°ãÊç¶ ¿PvYz2Õo¬'[ª¶c[œ¹8>¿î“¥±¡‰ã!“(®,Cáëž¡·“Œö¸éÕÙÖçÅ+zÃ0 Ã0 Ã0Ì c²¶Ê"¤ã(‡M¨¹¨çZäªAÐä`ÌÊ‚‚œl$mg¸à¡:ô%äw*PŽ6\{¯ X§çg0•JîAe¨an\r k¹€2ÆSz#šÃ¡p‰=ׄÌ òYÙãE¶ÒVƒÐY Ÿ¹ä!õÖCçeÏ ±÷e1'·=ôW]w/ÅXP¥¿fÍUàž_A²² ÷cМU (i˜‡Ø 5t¡eÖü s)dEÏuŒU”Øžz‚ì1•§L„Šrïù„᫇Úcaƒ' Ã0 Ã0 Ã0·Bè–ö²•ø5^ZØŠ×tËžë—~U×J•‚KØórž–ý)ÆÚÿ¤Ê‡´„¢”Ѫz˜„Ü.1/ÝHÕCä$µ½T¤kyI£\9éΖIÇfeÐ< –s/q/%é.o²SV§ ]Õ³*å9^TH=2»a‘Úî?”gXÂRfµÈêGñU{ Ë‹;ì  HíªÓ­Gª ó2S½e8˜°øšæXÚuYC 4›ö¯ÀBý†Xª:³ $:&Ÿ£–’ô”'ô1&9é]+oèaœ¦¹ô’”&{LåiÈ„w˜D'þŠjµôå¹¹hú%ð×GßW ü9Dƒe?²¬&Ã0 Ã0 Ã0Ì4#4ÈÀ0SM„(œð#ÓeˆÁt³‡a†a†afÁA†a†a†a†a"!|u †a†a†a†a˜,á Ã0 Ã0 Ã0 Ã0‘ÀA†a†a†a†a"ƒ Ã0 Ã0 Ã0 ÃDîtÖü¯¼úküLo2 Ã0 Ã0 Ã0Ìd‰0ȰOßVg5Êúo·­ùcqÓ¼ÔT-¾EuNÕØÚ¼¯¼JŸú(8ñÌ:½áàØãÃPža†a†a†ùñÀ™ 3šuX_—§Gºõö$8óGìyùø»ÞºñAËn!kE¯–dOö0 Ã0 Ã0 Ã03–ÿÃûïÿçÿŸþîƒzÆ7¢BocíÂëä@R¯ô/ÐÒQ|õÎ+ø ‡Ê/Cª½Ëëæ¸äªõpïèqCÌÏžÙ‹õ•z—ô1!õ{ì±ÊSüvá3§Dyµìÿ´øYWÝšd+ö¼uJǫM·á°º&ÆöQò„ÕîÔVµâ|/‰öyËc$´ÚÌï8¢j;^úU=ä!„¯Ý¼öLå=r¥»—Ž-N• –ü_× Û´]×—a†a†a†™n„d2œÂÛ/S¶îÕûR;Âyo,¬µÏ $`yñ?¤ü`;°|ÃäÒãÿþ–U·ø| ¬·Óì@•pb•<…åÿ¶]9¬¿€p<]å)¸ ™#ìµÜ׊Ẁ¬›zéÉYÕÇÌÀ]‡UÕ@ÏÿêÎh#¹f,¬ÃÓ¿ªFuÝ?¾¤å~{LåI^ŒO­¶w]¯X%ðé;_b¬²ÅmÂWcÅø{èõe†a†a†a¦Y — Œ€eH´ÖÛÝø6%œ÷_íÅÓk´ÈF8îÚa¿8’-.yE8‰Ùõ@S¸š@|ìlâ’“Š¦½±b ¢±d‡S¯%—Œâ«O¬M~D=àk6byê®Ð>Q±f*’~}.üö˜Ê“`}ÀõkoU×gìK?#EÆëûc˜Oƒa†a†aæÇFÆ Ãâ¦í(nó:çV¦A¢V9ŠéÁ†[ j;¶ÕÁÉ”Hë1×T•£hÌȸsÐÙ$v%€¼¶Oö¸¡aVf}ìÃ0 Ã0 Ã0 3“ 2Ã_ü¼mõ*û `ÃÁöQ- XMÀ‡ÊNÈvõƒ¾•=à伺3ÿOÕˆ¥úÞÆ*—Ùõ.núWTdå\cA•þ:Qí¶[ݶVýU4ÜÁ”U °ÛGbë:<íÉ ÉO;ûê ²ÇTž2*ÿÕ{>a®ï&#…a†a†aæGÄOî++ÿ¿êï>„ƒÿìÿ‚ʲüü^¯>ÿSNýã><ýêóxLË\ð-Nÿ?>AR¸™+ÿç2\ûÿ¯â»àÿ ?/½ŠSÿß‹²¶¢ÿ¾V”À×Ö~Ãÿþ5[±~ ÕÿÂåÿüóÿ…ꨴôÎN`Ïÿýÿ)Ë÷Ý·[·ÿ/¶=Çÿo…Žb,ûÙrà?ƒÎa]·ÍE Þ·ÿ—ꘟ×ü`Û9ÝPí\þÂ9ŸýÛľxŸ{’<¨ýÚÇs®óq©ÝjÏ*}k0EXBÇy®¯Oþÿ:ìº.³ðÿùxKôõ ´ÇsÝåOáóï—ã—M[UۋϪû¾F–cþ7´õU ñ¿ÿø‹Äõ[ö¿ÿ½+èú2 Ã0 Ã0 Ã0ÓÕ%˜i M„øoÀëÓeˆÁt³‡a†a†a†™28ÈÀ0 Ã0 Ã0 Ã0L$d±ºÃ0 Ã0 Ã0 Ã0Lf8ÈÀ0 Ã0 ÃüÿÛ±c„AëŸÚž¸ ÀA öÀ0Ó½ˆ OIEND®B`‚cpptrace-1.0.4/res/from_current.png000066400000000000000000001411121504061443700173250ustar00rootroot00000000000000‰PNG  IHDR~σlÚsRGB®ÎégAMA± üa pHYsÃÃÇo¨dÁßIDATx^ìÝ{xTÕ½øÿ7d&×Éý$! 4ËEPT@AÅ•V{€"âýÅ6´Ñ#œ†oå ¶ZD‹m…D+7Q£ b A rB2¹ÎÌ}g’ì „øy=Ož‡ìý™ÅZkï=™ý™µÖî3ôŠ‘?"„B!„B!z¾Ê B!„B!„¢wÄB!„B!D/%‰!„B!„Bˆ^J?B!„B!„½”$~„B!„B!z)Iü!„B!„BôR’øB!„B!„è¥$ñ#D/0pf˲V°dþµÊ]B!„B!~Â<Â"úý·rc׺¿ÿ~—w- (%”sû>Uˆ$üÆû¹ÿŽëP½Ÿ¼rå^ÑggòÄœÛH2íæ«BåÞ®À¤;&éú`Ì~A‘2„‰,Èz;nœÌ ¶Ÿ$3Ù_([7á!–ݧªv¦Œ˜Go鼉u3Ü–êI@"#~?/šÁMCñvÝÛº¨©LMõSnu¢¡?›—“¹$¬×>Wî²Êæ•%éd.I'sÉvŠ•»»Rò}$CÉ¡Ê=B!„B!.²îMüøüŽÀx/ª¾|]¹W\–ÌÔ•ýÀ®®eÕªçXµê9^Þr˜J3xÆ`§ÈÄë¦1~ º†J½›«2Ù0§%°¡„Cß×*wZ©¯ƒ¸øF‚é89Ö(w !„B!„¸Èú ½bäÊ]¥ï}ï?¦}ë¾áØãs•»-Fþ‘ПqqÓHmþ'œ~ö÷öŸßç0 ꯤ¤b&Qc⫇fj¨=ü¥/<§9V÷ðGÄó·¿ÎúÃ+9ù¿,¿Ü¹‰¤‚é ž|ýããÐTú%¯/¤é„핃ÑÝù8¡#¯À/Ð ëVÓù*v=GÍGûm*úŸIxÞ·€ða–vA#¥ßRºášŽ–¸Æv@ø÷óËT?Šw=Ç[yʽ­»rÖãÜ]Ì»/lâ¸mcâϹ/µœ×ßþŬÅã‰9ÿ«Ömw}±“¨ïçîT/ ¶­åÓ˜_iª‹Û:t€×¬Ì@î³y|öµr¯vgg2/ ýß‹ü:‡‰©qëúSÚö:›¾>§ˆ  xg:¯ìq*dÂC,»9ŽšÜ—xvc¾uãDdM%Æ)Œ¢íd®ÉvÞâ†õuíÄŸùSû¬×`2^O˺93Ü¢%7”÷YoVîåŸý·¤ôÇG˜ê)?±Ÿwÿµƒ‚k@üžüÍUTíÌâÈàÅLNðÁt.‡¿¯ÊåʇçpM¸õEÛÉrn§w“îšÉ¨øK¹˜¨)?Îþ·^go±Ù'„B!„½@7Žø¹€´~ôêŽnQî ï›ˆŸ?™—iV^ø%L&üN—P<?ÄÀ ¶„ôÅÿaw3`á]ÊPM±šè£ züqú[“>žQ#pÓú@#'êÆ8%}ô‰úÅ é´QK?øÜEàgw•£]à…WÔâþ >ñN±T^ÖÚ(›ÖE]y7cbà|^ŽkÂåøÛÖ¤JÖ)^µ‡wñöÑ:åÞ6µZ‡0"Š0C$7>Kb„roG鉹u13FX“>>ýIõ?ï‚ãÖÕ&=”e­«ýDÃéŸ­ŠšrÑTräC÷I-åŽ_˜ÅŒ4kÒ@ïCøà˜·xƒó2 ?[ÈäKXðn·&}|b¯åN[ë†pçâ˜0Ø–ôÐã>”Édpg’LøB!„Bô.í&~tóÞ!nMIkrH|nÁwLR†¸wÇ$B|NSõö{ʽ¿‚ˆ¢šë 8õúý[8ŠcO?CáþLÊxÀCïç¿¡ð¹Û9öä‹”Ÿ·l÷v«2´ÝXó 7Xþ¿…+)³åŠÿmÝfù±öqFP Ôì_α…s(<Ú€GÌ•N1˜J¿âäëÿŸ½¬ÖE=þOxЪ¡<î¹°@€ξ½Ä÷—÷©2úDßù;§èºp¨åœe­àV àÆ¹–E•/~œ»o §zïÖ}¨á Q-X§xÿŽWÞÿ^¹ÓÅñŒ/ù°Ø†D~±²_%|÷¡¾è#^ÏÊ sI:[òª’Ç¥)ƒUêžu{ ScB?=œû–w_Zj-??ï;¥ UÆ )!pú o»|¦¥\ć˜«Çtz/ëŸÍ$sI:Ïd­eOQ=ø_Åä]Gë‡P“»–?î<®£dgýª Ø:,jè]sHõSùAÞ}AQ.¤NQùþ&„B!„—‰¶?SÞ fT?<­¿öõ #ìæ,bŸü}}¢ñX¸ƒ¤59 xX9Šæjü®‚p!?µg»)#°Üº5RñöÔí·Î©9óM¯ßIÕ&×xLœ^3×2¥©úuª¾, @ß 1ˆÕ¨þð‹”¾¾8JÓá.êÉó壜ùï…ÔïÿÄþš÷~L5Á¤÷ µoWß“ðI£/`:º™s»v[6çýžsG-k©xÄtáê6–F©¤#zü î»n r‡jQ7N!5°–CÛw)w©Ôù:8ûìé¯9l]ð~ñL˜rw‡Ôä®%kÍ~0Z¦ø×§>1C”¡—ÔÄÔþÀ)ö¼ô:9ÅŽuzÎÖ´= JwÍxù˜8ñ…õÜTÐRîð”8ô¦ãìXó.ßUYæu™ß³{m6%@t¼"YVÿ-;6OÖ²Î}Éê=ç(-w^g(«ã}Àtœ/¬'§Ä©Ü59RôKd¼Ó+„B!„âr×fâG7¸: îàrŽ-^ÂÉü𝄙$®z—„«Â€FŽ: îîž¡ÖoìMGÕ?íËTA½}àD–ñ6þè8mGc¬&5Ôìw ´ë—ä/ű…7pþKÇæ¾ãWúÇh-•´æ "|ûmÔ÷Ãlù"ýà_[Ë´üØ×*Ò·½fQ×9ɇë,‹*¯Zõ¯½ûe rõíÜ–è<¹M¥·s{ªçíâÃRµ§.®ƒRY[^?ŽÐŤð_µy©¨RU¢Éd.ä\ @+s.‰4ü½šrv•ûÚ6ñšDôõßóÙ-9ÚÊM *Ð'rûÒ,ËrúY:•h×aX5G÷òÓ[StÐy·U ä{rZTñ0ÇJL@0Q=pêB!„BtT›w³¶éP§ÖlúÝÔ?{'v~C­uäJ35T»\çu\´G¸xª»–ØNê{ç&âæ\OH¨º¥ï.çŽogýö4 #|`´rw»Â‡Dã¦Î°OÝZ¼øq~i}¤{ÌËïsol=c×Ù:´áËŒû1æâ<þïùfeD/åoIŽhý ®ìçòvórt¼ÜK ENH!„B!.cm&~ÜiÞ:—Ó[Ö­9¾ðμ®XÇéî5¹­?ÂÝ\oƒ~0Aœ[á18ÚšX9K}®r¯«Öc+¸`Y|§‹ÜEÀËš=Ô}Oñ_æ´\Kȉú~8ŠÉúúÆÃ+]Ö"rü¸Y[«¼²jÕË|X®ÜÑŽÆFm³ÃºCÖaì3W3ÌæsßóæÓg•»»FôHbü†ö{>jp—,4¤ÂiË($oF)öŒOé§Øâ0ôÆ+æßì:­Üe¥¥Ü|Œ –é[oÚ×0Rü¬p¿`|Ûê1›€è!ŒRfui$EëÁTÎIç‘‚B!„Bq™ÓœøiOŸŸ!P˜¾§tCëÏÂ6å¥ /Bñ†1W[vDÞŠç}›ˆtûô-ÛjCƒñ˜²Žè«,k¯4WÅxÔ%PCìn.Ø2Q#ðKéì:9¡ô±ói®/¡!ï(ÜŠ÷Ã÷Y»v¥¾þEíIK’ÈkØBït×?~í</~„»†‡+wµÊ7êZ~~ûPühàä÷'•»ÛUþáËö)[Î?o²qšãeʈŽÑù;úÔ3š{~=Š` $Ïñ¨ñ‚ˣݣFÌ` ·õÑã —r»õ‰UÝ/Ÿ‚rè™8Û²ŽŽwôhî\œÅäØVƯéF36ÉSþçmLãÒVî¢Jð¹‚ÛÞΕ1]5…ñs¾)¶Ôá–…·34Èa·,œI²˜Š’£|™B!„B\Æú ½bäÊw+þzš¨@¨;ø §Ö¸yš—ÝÃ7Ìßmö©þðJûSµ|~ŸÃëSyZ0¥äõ[0Z××ÑkÓçžwH˜`yô¼3ç:pç&’nÔP¶Öu=%ï'?%&Á2êÉ­âsìKí¿ªí"Ÿ"ì·9®àÛ!¸qî/H Š÷²ê-7·À)w³xŠû©T•_oáõOœžªÕF,ç¿cÕºíÊ­vá7ÞÏ/Sý(Þõoå9íh£Üuè¯Y˜;+€Ügóø¬õÜ¥jgg2/-@¹SÑv–®q$~0Ü¢%7XÖ±qbª¯r_âÙù–bÙÍqŠH‡šŽÆÆÿ‚'3ʺ踉ò£§ñGÕÎt^ÙãØc˜ò¿½.„#ÿxŠ Gœ_£ ¥\ÝîLŸOj+9Ÿb[lüžüÍU`«¿µööX·ÇMdÁâ©Ä¸»†ê yÿ…Ù[¥Ü!„B!„—/w¹†Ž»a!´þwó 7P°ó+ªÏ;¦è4SCmþû­&0l+­4›j¨Íÿ˜‚çZ&rlÔÆþ¸!¢ýÔ™¯éŒ†^äti£M¦ªþ“ÂÃî§ô¨î‡3K9ûûåœÊ?K£»çÝwÚI=˜)9®2yb6SYò¼ñ×N'\:¬‹ëÐøÖI^º«k’>g¿ÎåÈéJêMŽƒf2URüÕFפ€qÞ:H‰mú¡©ž’ܬ~áK.Z>âÄ›¼²õ[Êmu¨/ãØÎ5¬þÄ2IiRj8÷mÛI4–kþžM«^bÏÑ2jºò\¯ÊfÝ ùº¨{±¦zÊ~Ä«+$é#„B!„è}ºpÄÏÕøý÷Ëô‹‚ ù›ÉÖýÓ¼:Â>Чî+Ž=¾P¹Û…–X!D'%ßÇ’_]AÕ'KY½Ë}bS!„B!Ä¥Óu#~T<Â]Ñ»Œ7Óqr>”¤B!„BôD]8â§ûhÅ£%V!„B!„¢7ëº?B!„B!„¢G¹,Fü!„B!„BídÄB!„B!D/%‰!„B!„Bˆ^J?B!„B!„½”$~„B!„B!z)Iü!„B!„BôRaýþ[¹Q´màÌ žøÕtÆÄ׳÷ë"ånÑ È1B!„BÑÈãÜ5KàÎŒHõ(äý%/²Wr™8e!3ÆÆ¬×Û·ïLç•=.a=F××WÍ1žÈ‚¬©Ä8o*ÚNæšlç-]gÂC,»9®“íB!„BñS£bª×Dd­`Y+? &(ã/?Cg¦óä3 ¯ÜáV>9G+¨?ú¥›„Àå-jzó®KtI¢ôdÝSßÞ}Œ…B!„Büt¨ñãfdƒ“Þ0aüÂLŽmmdÇOI÷dþ’dŸjŽl}• _œVô0=¥¾ÖkDFü!„B!„èaTŒø±*ÚNæ’ô?rÚ›„`ðŠ>½„I-.·ú !„B!„—ú?*F3Ü–¾‚k‚M”ì\Áê=çìÛuIsxü¿®Â¿æ ¯¯XÏfÇkta#¹mÖ$®Œ A`ª§$ï]VoüÒd5|öcÜ’Ò½%®üÄ~Þý× 1gg2/ ýíE~¸æ>G|ý)m{M_[ëeAÑ–šÜ—xvc¾õ77#ŸÚêï&Ý5“Qñ–ÿ5åÇÙÿÖëì-vt€­¾_ÿm ×Ìi½¾ cæ/ejï\Å+N}Ý9*µÊ¶iŽÕL]}½ã¯å¶)ãIŽÁ2#¬µ:h<Ævêê¡æüµÇÎ|ˆ‰©ýìSØL&Ðë{Ç;!„B!„ЧzÅ3âÆ$Ï#ûËåNGóÌ$_3”è„Ì?§¨Ð aæo¦ëUÍ¡ køôl³=^—ô Y4™„@€È”4‚ò÷ð]w 7 ²ïVÓ™/ÙwØ–T±öƒs@k}¢ÂOÜÏ5ýüÿ?xù…‘8êŠ>#ÏZaK}}JCêe}Ðýçsò›leØLdê¬ÑêMög‡•êÅÏáÉ'ïåæ'sƒ­}IÜpãdÇÏ5dú%^CÛ4Ū¥µ¾$ðó~Ij„Ê:\ñÓ/9m¯‚†cì¢ýkDíù 0é¡,n‚£ÂöºWÿ°›¯ ±B!„B!D[Ô'~”7×7N憇`þð ì»n(àXóF ‰!>ÞÌ'_0èÎEL‰õ¢|ßJþïó:§r˜yÿtzA}ÑG¼ùò:6mÛÁþ¯Nc ¤oñ~°&~ âž!4ŸÞË¿^Z˦m;ØûŸ"úÆ%CdßÏÉ9nÉŽX)¼¼<¨/úˆ }…Í;vQ=šäð‚|ÊØ{è ~Aö‡»Éþp7æ¤É$òþ’?òšu[ö‡»’>|eßg&©ý¡÷<ʤzLåٶñŽS} DFö%û‹À^__ôzK}ÿþçÕ¼ýÉatI#‰ó³6›œBer¤Ïøñ$›)ùb 9Çkû5Ne܈(¼”Û5•Ú)ZÚ¦%V5õ…`†\AùÞ­¼øÚz²?ÜÍÞÿá™x%1AaøÖîæ«bÛ ÕcWm'~´œ¿†)1çÊ 8÷-ï¾¶†7Þ~ìwó÷0FÇHâG!„B!„&ê×øQ©jÏ‹ìÈ7¡ï7‰E³ç1#-SÑvþ²M1)u1C”¡],«ã}Àtœ/¬'§Ä©¾k6r¤è—Øâ b¦"K?4 §Ù}Ȳf!:Vi±íSd.yŠÕ»:¹¶Í‰õñ£¼¹^’Nf+OÁÊyíuÕè‰NŠ¿©Ýo´\÷ÄPz¨å>W DúDn_ªxœüÒ©DøG(_DUÉ÷®Ì…œ«,+ u#ë‚Ã%ß“Óâ^ý0ÇJL@0Qñ®{JóÚ뇞@KÛ´Äv/ïø[¸wq&O9Ÿ;í¬ïÔu´œ¿iø{5åì6º–"„B!„Bt„úĆ~{[ÿ­÷!Èͨ ƒÎÍÆŸ¹^DKÛ´Äv„.iüæ’º=å×yþ–d™B!„BÑEº%ñ3þ®IÄè«ùzë~ÎÁØæ0H‘ç)-·Lc ‹m9MËU>Æ þ[Þl1âÈú³b‹òE-E$Æhhm=‚¢•Û:¢³ ˆÂ(enK—FR´LåœtšÊvùÐÒ6-±ÝgøuÃðjò6»ž3;/ÖB9ZÎßÓ–QiÞþŒR”2>¥Ÿb‹B!„BѾ.Oü œ™ÉäX=5¹oòö[ØòU5ø_ÅŒ™Šµu§ðI™Í#³G3À`Éè CóËyÜâ4è@Q%ø\Ám oçÊÇŽ6èüÃíÿ6$Ý‚ù£JÜL©ª2Ö!$O­ÜÕŸóM± ô‰Ü²ðv†Y†>é C¸eáL’}ÀT|åË43)˲–²`B°rW7ÒÒ6-±ÝÇ[§LËíK3t쌻XS½´œ¿ù”[úlâlKBÔ;z4w.Îbrlϯ$„B!„¢çé3ôŠ‘?*7ºšÈ‚¬©Ä(7[ïLç•=–ë’æðø]…ÍA^_±žÌÖGz§Ï'ÕßDñ{Ëyå3Lj›  ñÈÍqn¦àTsèoËØd b/Cfå\‡³3™—  ëÊK×´Lüè®YÈ’;[Ô£&÷%žÝ˜oùeÂCm® ã4‘‹§£, ¾÷_x‘½Ö'–ÙêëÜpü.åÚ9“s9d®xS±¿£¬åm'ÓM?¶¶iŠíöë5-ǵ\ÊÆ¥ßµc-±Î_âÁ“¿…k¨‰ò£§ñG•ò<B!„B!ÚÐu#~ ×2÷—WáO5‡ÞÚhIú˜¿çí·Rƒž˜)÷1>Èñ’ª=/²ú­ŠÏ™0Ù6š*)þj‡#郥ŒM«^bÏÑ2jìê™L•µÑmÒÀüÅÖíüŽòúîNU6ë^ØÈ×E•Níª§üèG¼º¢³É€l¾É¯ê)9ô©rg÷ÒÒ6-±Ý¤tÛ ¶äžÂ~hM&Îåïeý[imÒ_—Órþžx“W¶~Ky½õ÷ú2Ží\ÃêOOÅB!„B!TP1âçòÒê!„B!„BˆŸ˜®ñ#„B!„B!zIü!„B!„BôR’øB!„B!„è¥zÝ?B!„B!„ÂBFü!„B!„BôR’øB!„B!„è¥$ñ#„B!„BÑKIâG!„B!„¢—’ÄB!„B!D/%‰ÑyAYñピ;„m83ƒeY+X2ÿZ实E®q!„B!.[½ïqîñsxò7WAîK<»1_¹Wt5ÝîLŸOªw!ï¿°†½gÍÊ!ºÙDdM%ÆySÑv2×d;o±ÒÛq§,dÆØX‚õzû¶âé¼²Ç9*;3 Õ ÷—¼È^çÝ=ˆ.ævÌO4…¼¿êEöV)#„B!„=•º?Þ Lúu:O>³‚eY+X–•Å’Åó¸%9\ ÀЙé<ùÌBÆ+wô½¹m1~þ}¤úWsèžô‘ã¦ôYÇDMÏ`Þu‰.I÷òÉ9Z @ýÑ//IÒGí16¿Ë+o¤FÇÄ»&*w !„B!z°ö?AYñGào¿Ñã>”±¿šíö†!<<½N¹¹WèÍmÓÊ0á!&Æê©ùj=›ŽõܤrÜ:äòé³l^Y’Næ’t2—l§X¹Û…–ØŽHã†Ô š#[WYÿËëh‹‚ÍËÉ\’NÖkŸ+w]ZޱùØz¶äV£Ä¢ ÁÊÝB!„BˆªÝÄϨY“ˆÑC}ÑG¬6Ó~³òïp¤¼žž}»/ºÏ0n¿.}ý·ìxG¦Ô a‚Á(ú” _œVî¼ìý°y7'Lz¢¯»¡ÊB!„Bˆ©Ý5~Æ/\ÁäØjýß26Sîu2á!–ݧÜê¢F±îŽwüµÜ6e<ÉÑ!XfE˜¨)?Îþ·^goqË”’.l$·ÍšÄ•±!XÂë)É{—Õ¿tµ²ÆÏø…YLŽÕc*ÚÎR­ëyhn›e‘ Ü—xönùõ FÅ êOç°eí›|×àxm·ôƒÕðÙqKJ|ô–¸òûy÷_;(púÿ;BwÍB–Ü‘ˆñ«U<·ÙÍ ®õ8TíÌâÈàÅLNðÁt.‡¿¯ÊåʇçpM¸õEÛÉr>Þ Lºk&£â#,õm§†Ï|ˆ‰©q[cëOfÇ¿6rÀ6åLóqÓBÛ1Fã±èêcg ìÖ>³PÛ6­×……u Uëöh‰UKM™ZÖêÀyÖÍ×EÔÌ áω­O±î‹ÖŽŒ™¿”© P¼s¯ì±ž_B!„Bˆ‹®Ý?Æ@ò”Ûè­ÜÛ Üv×tRcm7uzüÇ2yþ|F)fè’~Á#ÏæjÛÍ"€Þ‡è´[¸3Þ%´…á¿ÌìxÒ§tþãYñkÆZoÔ|úâ¿íÕ}ý0~a3Ò¬7øÖ¸ðÁ70oñ©›ÝѪᩱè©äØgn’>N ?[ÈäôÁC¸Ýšôð‰½ÖQgÝî\l™Rh¯¯­ÈàÎ$× Y˜ÅŒ¶›[K¬O¿«˜1ËÝäÃî£îk;ÝwŒ}Hþåb×xŸþ¤ÎºIEh7Qß6m×Å%?‡'³lëŸY:±S­¿[2æ(_¥‰Úóìb\¥Ÿ}Ï9ô H©Üåd"W&ø>Äül’r§B!„â"ò‹è÷ßÊÎJ j‰¿:…Ð8®¾n4IaÍœ/o¯y‘MŸ¶o1¿gÇ!ëç¢SÇ“ä¦ÓÛÉZ³ƒïª,sª³ÿWÙqÂ)ÖNÏ ™Ü—fIú¸L)ºXL…¼¿j›¬k}˜Ïîà›Ë.G󺧆§Ä¡7gÇšwíqfã÷ì^›M ŸæÖ,Ž` æ4¶TL«ê¿eÇæïi°­uîKVï9Gi¹ó`WÇû€é8;^XON‰S}×läH=Ð/Ñi!ñzÌ& (‘1ƒ‹Ì~÷ÙëüåµOí¿_*ޱ¦cÑÍÇØTd)· h8ÍnëyfˆŽU†v= mÓt]\j'Öó¬rÁè¢í. ;g._¯|•6*γ‹w]ärÎø»]ÜßfÿÚ§È\ò«wµ=*P!„BѽÚOüXØü"Ï>Î_ÿµ—5&ËôÇÓù¹b ŽÞñ·pïâLžržáfí CL>@é!õÉ]ì î§?º4I€’Ãìµ`°Ø»&Ì%/º<º¹ëû!¨`@ŸÈíKÊÌZÁ²¥S‰ðP¾H»Ö—÷°«9º—oœâjŠ:ï¶².ˆ[ò=9-Ê<̱L”}*ÐçlÙvœz}©³ždÙ3™,úå FÅwé\DuÚ=ÆÚŽEwãÒ¼öÊí>êÛf¡öºøIh÷<ã¢^Æzå!„B!DO¥:ñcSzè]Ö-_ÂúÜj „«§tlý]ÒùÍ $…;Ö¬hA§=¹d.úœ#5@¿1,èÁîî~èV>þÊ-ÝÊ9'TõŲ–®âÍ}ßRbô!:e ·ÿfKNtŠºüô¸cÜ…´´MËu!.Úu¡þP !„B!.1͉›ïÞù’ÿå.+‚¢•Û†_7  &o³ë”ˆ…ÊPû´ °Ø–SWZWȦ—¶S\ïCÌ͵X¸sÚn›ÝÓù,Ó¬Þt.ÓùgÅå‹4(ä\ `æJå®±NQ‰ÒrÑ^]IÑz0•sR9¥¯á4ßl{Õ+–ðLÖ|}΄OìDîIUÄÙuÝqSOÛ±è9ÇØ¦ëúL}Û´]B¡Û¯‹!Dù5ç\F. !„B!z¦v?×27c!·]3Œ0ƒãŽÜ6ŒI¿c™Nr®åú UÆz „ä)®Oœqæ­Ó&ŒåÖG¡cç°`œ›©‡ŽSø¤Ìæ‘Ù£`­‹Î0„1¿œÇ-­=Õ«*›W^ØN±)€ÔÿêÜ´45mÓ¢»úá@Q%ø\Ám oçʘ®™“OA¹ |ú1HÓ ck>ç›bè¹eáí ²LMцpË™$û€©ø 9¶ðø_°`áÆ$‡ÛK0sù¡¤ðÁàæ U]}Ü´Ðt,zÌ1î†>ÓÐ6M×…°¸X×Eô0büÁT~\¹ÇŘùKY–µ´G¸B!„â§ ÏÐ+Fþ¨Üè0‘¶Ç»c:Åž5ϳۺȨ,¹#±ÅšÜ—xvc>QÓÒyp\Ë5HlŠw¦óÊÇïAâ‘›ãZ” ÕúÛ26ÙFƒÄÏáÉß\Nÿ—.iÿ×Uø› yÕ‹-ÖÊÐBMÛìýV´ÌvÖê¶~Ð áÎôù¤¶’P–«•­Œ_­â¹Í-“-ŽÃ„‡Xvsœ£Ÿ¬¿Ûë4‘‹§Ó²aP_Èû/87kÙn›f:λËÖ´X+HÝqÓBý1Öz,ºãœÉ¼´€ÿW‹ãâ¤ëûL}Û4]Ö6´Æ¥¾Zb;DÅy¡©*ʳ¹H×EÔÌ áω­O±î‹‹rY9ýí8—CæŠ7•B!„Bˆ‹¤?Ùlø¿w8TTI½É±Õdª¦üè^Ö¿ðb‹¤€ù‹5¬ÛùåÎ/R(ݶ‚-¹§åšLœËßËú·âîÁ¿U{^dõ[9Ÿ3a/ÕTIñW;7­0[Ï+; 1éã˜üðCŒRF¨§¦mZt[?˜¿gÓª—Øs´Œš®©ª ó{ù¡‚Soo9=«#ª²Y÷ÂF¾.ªtjW=åG?âÕŠd݉õ¼ùÉw”;7ÌTm‰]Þòæ–n8nšh<=çw}Ÿ©m›ÖëB\¤ëB—Æ )!Pÿ=ŸµšôÈæ›üz ž’C]ùD1!„B!„VíŒø¢uAã‘›ûÓ»–g7~¯Ü-„èeÎÌdÞŠß[Î+ŸI N!„BˆËA;#~„h]ÕžçÉ>mÂ?í]¼x¶¢§Ñ%Íá#0í–¤B!„—IüˆNÙûÆnËâÙ¿|ˆ[b$ù#Do¤ ›ÈÜ_^…ÍA6¬mg­!!„B!D"‰Ñ9UÙ¼²j;Å fŒ5m­ù!„¸\™Í@C!ï¿´žä2B!„â²"kü!„B!„BôR2âG!„B!„¢—’ÄB!„B!D/%‰!„B!„Bˆ^J?B!„B!„½”$~„B!„B!z)Iüñ3pf˲V°dþµÊ]?YúE0÷ù(·Z´µOü4…Ü?„ù«‡ó« 宋¦'ÔA!„B\$ñÓKé¦o%!ãÂF+÷ô£×]úºõ„:\ Œ€OÂUŒWÆ âå_`ã¼XûÏŸ’”QÝ/ã1 ô½‡røú ÷ôgêÌŸFòÇóîDæ¯Na‚SN0äÁdæ¯δYΑ—wmë¨ä$_¼bÂPsʆÜˬ?§1õpû½?GD0{õpf?è¥xUÛ´Ö¡#º²ÏDçôæc¡©m³â{Åû‘Bq±õzÅÈ•Ý4;“ûb“¹b ·,^ÁXïƒd._¯ eøìǸ%¥?>zçò÷²å4(#5ð¸Ï[ç18 O@õ…û(ÙðGe¤6Zʽä±é„eL!@±c.ù/^KÍ7+‚Ý½Ž„‰±-Êî{årB¯MÃ/È“¾À³‘º¼]”o{ÉùÕ-´[_wÜÖA}ÿ^ÎÎÌ`Þˆên&ë55ÕõÒF†³$­e&¥`O¿=¦ÜÚ}î›ÚŸ©Ñf¾{†ååʽ¾áVÝ‚oÉžÙÞÈÊ€‹`èÌtnO­aÿÓkØ«ÜÙ…F-N'Y›á茓™1Ì›’°í-—ðËŠ»¶uTÈýC˜‘æK݉clXaTîv8w0wŽôSnvôçˆfÏë‡óØø×FeX«´Ô¡£º²ÏºCôýƒ™˜Ò@ΣE\Ä·K¢­cÑú¡3uh«m-ÌŠgþ A—ýûÑå)Ýõ’<Ÿ O,ß•‘¿|®2°CÚý,gýìÒ߀h6©UñQ!„…ê?ÑÁPsÎú[oàÜiEŒ_˜ÅŒ4[Ò@Op Ì[<‡AÖ$v#ñžó$Rl‰O|â&2ðá•®¡šh)·'Ä^ +‰H1(·:ø¥6o&ýI!Šð[Ÿ$ldšs´é„Ü: kÒÀCgÀ?u&±÷< ˆuÒ^}E ›—“¹$ý’%}¦õ9¼û$³_-²ÿ\̤O¿¤`nŠîKÕ·g[MúÔ•Ùp¤Ïèp í£Ü}Q„‡Gàï|Áu‡kû“e‡Î*÷\þº¸m•/ÏÚET%\ÒÒü€:Žný†µ‹Ø:{Óª¥ÒÅ}Ö !~øºª×Û´s,zB?t¸í´MôðÀËÄŽIÁ`Oút¡ö>Ë%¬¤ßý–Ïž¶¿„}Õ|FBa§2ñ“@X0Ô+­¿Çì&s½K”aÂCLŒÕC}!{þo)™KÒùãß>¢¸ð¿ŠÛ§õs‰W«ÏèEDö÷¤¹¡ˆ’O¿ü&ò×gSÝ} iDM™¬|‰*ZÊí ±6 y«-qÎ?]1ÅcþÓÒð6QqÜýÍDßkG ƒ†£ÿ¦ð/s,ÿ÷s¿£8§3ž¤´¬¯Z¦Š®zó±èÍmë=Fâ3s&aA¶ÏiO9}>ê‚Ñ>j>ËU”Òd,åìnÛç³û)Ü_„ÐÅSF !„p£©^Y5•åf¥¢íd®É¶Lÿ ¯æÐß–±é„Óþø9<ù›«ð?·ß>UL½4|¬$*ÔHùú;¨)rÚ»’~sÒð®Ê!ÿ¥ `2~:hn(¥âƒç©ùÆ©oœ^3 Åãî ªú?ãvª˜Ç´­Ä¥8¿sÎ8v>Cä¢qxn¦pƒÓ°[ëñ¼ù^BSbñÒY2Ìà¡ke(¯‚Ï܈ýü •»TÕ´ÔA}ÿ›Žÿõã²Oyk¢±¢ˆÊ÷ž¦þ´SßhULýk6i(Ì¡ôMåÔ?-Ü\ËÖëViàìLæ¥Á×[CÁ5sÓ6ëOqhÛëlúÚ6ò¯ãþ4/–%g˜½½í©-ó¯glœ7~º>Àj+Œ|–}žµç•‘Úb}‚xybu¹'¹ÿKuɧ´‘,IóäXöIþ¯Ü«wüµÜ6e<ÉÑ!è­Sbkʳÿ­×Ù[lMLxˆe7Ç)^éª&÷%žÝØÉÊDqûÓñŽ~džç]ú–©^: ¶~ËáÈL„?@]5G¶°/»eâlhú`®Ž÷ò ™ºŠ*¾ÞTÌw‡¬ÖéMÆq4)™ëbô˜ÏŸäÝ¥Õ$.I"-TOcq>ÿXîzðâLdܰk¹Tœ8Í'/WQYí檶L[=ƒušUêSÉ\åŽfêJO“ý|9%NeO[=œhçç³VQG¥i«‡ÝV\+S½’2®äºÅ'øÇò*ûvµu°·f¾}õ8%£ãýÖÆq³k§ÏH20êîþ ‹òµ~ßHÙáÞýk=ÄR8òêQ GdBuPkêÓ–E_j9Æ#=-Š„(_,«.¹9©è›.=;Ðh¬CÜýñŒJ Hßh¦®´„}ÿ,£Ð62³ƒu°k­mN\ëfšÑÑ×íT¯vÛÖÁ÷vßÏ:JŹ£úºÐëlÌü¥LM€â«xe›¿ïIaÀ¬úžÚEÑßW(÷¶ªÝr­T–k¡ÏÐB!ZP9âGk‰ ÎvIúxGæÎ;†Xn‚ûu`1ÙÉø†UG\ßÔ#Åÿæd<‚"­ß§vC65fðÿ˜#ÖcÓÒðÆHù¶g­µ”Ûb;Âß›þ@¿TKÒ ¯wá·þÿXE¨u˜­ùèfÎ~™«Øép!çu@àÍ/>íQ<üÒðH~†°¹ãðÃHå§Îs­'ã{ß“ °þÿŽ)\N!­ñ‰þúu„DBÃÑ÷•{U×·SuhÓ ü§O!ÜeÊ›'^¡ƒˆžóŒk¨¦Ø–Sÿúê ø&^ì©>$ÿr±ë´MŸþ¤ÎºImŒÆnUˆuN‹8ˆŽtYØyã/ ŒuzIÆ/b˜”ècMäxàȤY‘d„;jŒ˜”`À“z§.éûeeô%.ÁS¹K£n»k:©±¶¤€ÿð¡Lž?ŸQ>7µ ¼#’ê8ö¾û›/ø‘ЉØfKúø|ç@†)Þ¢Æ-εö›$¾¡a\»pã‹©û¤%r]Œ¥tá\»$‘´PËï^1ý7›”q%“ì7u^„ÆÇ3##‚ð–ËEÙµß6ð cÚŸ‡sM”·õ¬/¾Q˜rž˜e]¬Ù¶ˆs4@L‚ËÂÎóÿ7Bù*bMúä»$}´ó$áîd×~kå¸9k·ÏFG0kqiöV/"†Å¹3 ÌJáæuÄèNÅj£ö¾;d{ÒÇù;/–8åy¦¡ºóüUKK2®dRZ=ábé¯þLšèØ íµmØSÊ:€®•­ZÚ¦å}GËû™&Îm×…–X€‰\™àøó³IÊèRbñ¤’Šê“>jÊ-ŸåÚÒDsgÖBˆŸ°ˆ~ÿ­ÜèPÀWî&û?>\9>ŽÊéüïºÝd—÷cÌ”Ú~ÿ²¢GrÝÏbð)ÿÆò»w“~ý¿ºy8‘~¶»›¾œÏßÃwZ>ÇFÜFÀÕýÑW|˹ÜÏÀëV|f?Ë€‰WbðõÀr›ç¹è_4÷ÒÐ|=†Äþ Žáü½èn_NtOŒ9Oqî+ëWUZÊõê±çÆá;~~á£/ÁãïÅÔ <#c¨ûþ3K»¬ú½‹Àpô¾ž4Êæôk â“#Ô†_!Ô€—·žê#,Á3˜sM¹¾öÿœ^ïIcÁß©;éTpí‡O'ã9(Cÿ!ýl ACcÑ7QºéÔ9¾Õésý3ôÈUyœÞøÿ8»cçöçrÞ3 þ†–e3ÿ‡_`À÷<æ&c|høúeJ·)Fßh¨¯¶:¨ï_HÆsh$uÿÙÄ™7ŸäܧçÜ#MÓ0„ãÑðwêìK`©í3úèŸBó™}œúû©øàÏœ;`¤a@2†°XèTm«ÂÖËǶ•òµõ'zZ4þÕçøúSÅhkÛÚ|F—©†¯ß¯µüÛOy9‡s.þànMöu;òµu°7Oôú¾ÔŸàßË Ø·ÅýqSj¯ÏÆ=6˜=ÔŸ`û_ ùôŸ%äæž§:RGsQ5gJ,qŽsÇR‡íÏð雥T$’êK ¡šCŸ›´ÅæUÙÛ_“ÍÀÀ*>Yt„œúåpŽëÈ-Ç8j´ç¾(àÅ–ò¾<‡yh0ý xêK9žç(Wm?tËù«±´Öaì=ýñ¯8ɦÇÙÿ¦¥¼bÂ}øîó¦ÕA©Õ¶YŸôuë08_Æî׎‘½®„¯·•r$ÄÔ/Œ',ï hi›Æ÷Õïg öÜQ}]hŒu(À3~˹¸ˆàëbÑU}Kù'*÷ !„PpÿÕ‰RB0ª9gýCdˆ‰À‡jÊï|¼}Ц>;%O=À„„ ž’Ül?jùFGó—ÙÞ–Å~Í à9ý bŒè8}i˜·™ÓÇ­@œ4>—3…MxDN$|úË· §vQöÓô&-åö„ØVè¼ ø§Liu4HCÞjNýý˜kÏÀ…iÚº# ï—bÑÝz/açÑPí¨(¢®¶ ç[ÿ¾ú@ç‰D×ã›E_J)xéO[¿É¹K³±ývYxb¸zÑÓf¸lU_ßÎסõþÝ‚ñïs©ùò_ŽMµ[¨Í+µüÛåDWë58ó­{ÓùãöØúõ9Ô¾±_CI+SÑv²ÖXŸÆ×pšÝ‡,Ù)C´r¸˜ •Fæ:-â\PrÆeaçÙo±¥×ÆÆéÁ\Íö­FÞqšò¿vW)‡ë€olß!j‰µ òΛøD±½=åç-‹¶tîñÙù¼½æE6}vؾÅlüžÖþÕþ&ÙIÓcIð5SÛvF¾±8Ÿ,­¤Üšw<œgélCˆ#Å‘ï¦ öe•Q˜gE|¦‘ï–çq´ˆ ví»º2ö¬kä‚í^¤â4Ûþyó®×g\R:SûVTR~Ò©ÜçOSD lešÊ¶aªâ“U?°Ï¶ϱ*Ž–ôѾˆéWeltZĹët,§mkÿ¿2å«¿Ó‡µžôéˆÆâ|6,wLwqwÜ\´×gû“à æÒlX^e?NÖsìùb}¥|eºÏ†åUös§ðåRJ¯È–ÇMK¬&*qÎÒì{ËiÊ™FYûÌåÒÔÐÝ~þª µ& +Ò­.ëo?ïæ¾#Úi[ZJPÍþçOQxȱ"A}}Ë/´¶MíûŽæ÷3µ4œ;6Z® -±û×>Eæ’§X½«å[`ú@@Kä˜AøœÀámy¨Gèp÷ÃÛ.WËg9wFâ3sŒTìz^¹S!„m&~ÎÎdYÖ –Ýu>ú_+X–µ‚ߎ‹¸ú7+X–•Éñ@C=f@?x*3Ò"ðjò?âÕ¥O±zã—àmk«yyËK‚A—8…)QèÆÂlò—O¥ì—ÀÛý‚Æk9kÿ”Ax›‹ZÎKÖRnOˆ`g~îwmÍÁh¶-}½"šÎ(FË\8N£óÚyÉ!*Å@CÞß©ÉW1’$0°3 õ¤ñèf þò<%…Fš !Ìå©^‰è¼£Š2í¶Pó‚µmÏ>AáÎ\jÍà“:ßñÁBS}µÖAcÿÆþž[‰Íø€ÛÏÄV’"ªbm°9bl?ONÄèë¢xM÷)Ík¹þÏÅâç”×óOå ·¼𤟵+´Äºjcy³VÔµþåµ&Þñ·pïâLžÊ²¼§.ËZÑîz>ÝeXZ(ººJ¿£ÜãªòXû‰=Pzž|737 Ϙo"œ¦Ôœ8K‰SlM©û…=#B}(7¬tš.µz8óWÆ[¦Rù*ß+-Ô¶ÒJŽ)¾´>¶ükÐühêŽò0›Ó|1—žè²¤*›³öúÌs€7^@yžûvwŒg#’êÏSUا::h‰ÕDå1öÄÄeÉüÚùV3`¢å÷~Ó-£n¼¦M±<Õ+o5%›_¢¹ö=ê7ÜAQN)Íx0ê.kÉ!èmýßr¹pà ÎìüžxÅZžÚ ­¾¬C[ý›°’~s&êx´h«´Ä ÕL’0nc=ú¢õ9ƒüÜ €.iüæ’º¤¼NI '9 ªŽ±ŒLévÍŽoÙ»ÛEo[ç\8y†üó ‹À”YšÇu }æÙ‘Gv_.FG0k^<‰¡¶u€Z×íý âXt§úwŠØ°è;÷—Qv^GÄàLšw¿Êè‚5~T´Í¾¶\Ö±÷³n?wºT%Íf !Sž¦É6rºñ}ßùUFÀËýH¢¶hû,çÄãV|ç.¥_0f?ËY燸!„hS›‰Ÿ‚ËÈ\’Ϋ¹Õ@!ï/I'sI:ï5É\’NæÛ¼²)9PÉ‘­çÀY§±=Ñ¿ )8wšýŽ­*åP_Pɹ­OÐTé”Ù˜B@P¥ÌöÄ{úDtFÊwæPG”›©:ZÊí ±­óµ&¶Ì*¦/EŒÂ`×a?jÍÀ3 ’óû]G5kIüÙ£¶Ž,òös‰ƒ‘xŽRlkCm­Ë”2mº¦îú×ktÞXkïòØ÷ì–Y:õ±Ç¹Ðhù€åçücÂ[ïf2á>ܭܤ…÷s#¶ Zbmªj€@=¶ñij…5¦N~Ý0üš¼ÍÖ÷PëÏÎöVò'ÈåqN=9œ ª9²UóXL·M@T n«‹Ô©ž’< §ª0•±Íyº”óOf…ò%]Þ¶îWϾ—ó)6鉹aPçŽí 5}f¬¶¼p,w«YJ8Ѿ@}ëÿ]»±z‚:4ߦ¥¸ñ‘–kóè1þõ;§ó룖#3´ôÃÅ9ÛÔàä?NñnÆ7¬}æ;¾­0ãĉÊ(›¶ë`£¦mÆ:@ïrdRRË$CGÛÖžîz?Órî´ªÝ뉖Ø>Æ|ðŽÂÐbMú`ú´—!íJ^wá¿à1¢"¡:û)Î~þ±2B!DÚLüØ„û;OÓJ ,X`•s´áêù mjá}-sç"(9´[ù>¦!¿Ò2•hμ#-›#Ò 3 ÀxÄií@7íDõ÷´ !=ÁÙCF0¤)¾9ÐRnOˆuÃo2ž7­#*5Ä’ŒÉyOA_ƒã9I}ú=Jàlk¹G-‰ó;w´L.,¿‰"ëšÕÙ–ß-7·&&ÁÌ£xøY“<^#Ñß”fy‚„=Qµ…¦ Ë´¥~ÓçY6E<Šÿ‚¥ôëïf–’G}“~OØô4¼h˜בúv²mô¯íÃŽ©âˆå^#Ñ\Iø(åô--±¹4žªïúßû žý´¦%zçšAÀÔÛ|¹ÎéC÷Sû1ÌšNÕa›! %Ö&¯²¼¼¦ñËë~¾ÐTÙd_‹¨#¼uzÀ„±Ü‘ø:v Æ)oqªŒõ@ÉSº60,Þ ó‰2UÓ)ÔÈ?i}(ãÒC`éEêSW0ØOVjZLÛæäÉ:ÐG01#„©ê¦›tuÛ.Š‚óìZ•O‰É—ä{‡0ºkw»TõÙÞ*ÊßÁ‰üüA¶\¤IÅêfꋇc„ƒçè ¦Ì@P–×rqr-±ÆzàGÂ4†å^í˸`>áAMÖ¤ŒôB­ú ½bd» ]Œ_¸‚ÉÁÉ\¾HàÎŒH=·Ì5Š5@tC¸3}>©nž€k*ÚÎRe¼Z3ð_´ˆð–_ô`>µËuýž„•ô›†·1—üž°ló˜AÀ¢E„š¨Þáxd¤–r{BìèunÖ…hù‡P7}«}ȬR‹rݰ½¾:û&Î~î´cøboÔÊ0xEÿÆ®¤ßËhç˜Úã¥x$ÆÒä\v«mƒúC«)Q>ÙK¡ÕúvIZöoß›Þ$vTˆÛÌi3`t*WKl[çÖC.íSkÂCm®!S“ûÏnÌëÚ^óÒ(Þ™Î+{œ‚¬e8ÇvÔŸæÅ2°ä ³··|‘ÍŸ~Ë@wëÏ6Ö°}ó9^wZwAK,€oB/O  .÷$÷©nLYÚÈ–¤yr,û$èD󣦥óà¸_Úµèw@wÍB–Ü‘ØbjXgŽ…ç݉Ü;Þ‹£òØó©r¯CȃÉÌæMÉGØö–ÓŽYñÌ¿!ˆšÃylü«õ8 dÚ⢕0ã“U–uVFD0{^°½VY–õwçÿsÜòá$·r¥¬›Ú¶L[=œèâ|Ö¶·®ŽµN­qé'í–¯ì ,SŽfßÛSŸ¬:áX›FC47}æ3+žY7¹yrYG^=Â>ëâ´¶:¸£\ÀZK¬ÝäX~uGh‹z(ÛÖî1°2ÌÄ]#Ý|€±Rö¥Ú~ Ï_Pߪë`='Ýö„©‚Ý¿+¢P¹îÊ:¨n›Û:˜©8aÄ3>£¢ÏTµMy­)¯åûŽÚ÷3g.õ®â“VÖ S{îh¹.´Ä:LdAÖTbÎå¹âMe€õ‰«î?—47Q°j®r³Êr[jí³\[ŸipþÌ/„Â-w÷¢-¹üå#ØÀÍÄfó÷lZõ{ò+{M–§zu8épa 5¯X¶ Tm61æmvM`ø¥63 oŒœu~JÀ…-ToË¥O&>ê²]U¹=%V¡ÙÜDý©N½úp‹o?.|“KÅ#&³%¹pÁl¤æPûå¶éÀBŠÖgSUaılJ?P²þaGÒ è JwæQg]穹¡’ªìÕœùÜý·NΚÍMÔŸÉ£dýýí&}ÚÔÙ:´Ò¿Í<Ëé¼Rš¬­ÙÜD}á>ŠßËE9áNK¬ý|8^IcGFe÷"Ïl-!§¤ÁÑGfeÇËøó?[&r´ÄÔåWq¬‚†0]¹³Ó“¼¡î<w,ÏbWºm[rOQo{“4™8—¿—õotyä¼3ókX·ó;Êí/ê¼´Ô8_ÞöÍ—VçÙöâ1rKëœòo¤âÄ ¶dº¹IÒ`_Æ>=QK©ýD]·´íbú¼Œw?ª¢QÄu‹’4PÐõ´ôYý['x{ëIJΛÇÙTGIn¡K²Ã³©Ž’Üc­Ü„ºj7öý"ÞÝ{–ŠºöÏ 5Œë~`çájêì[LT²më·×¦–~èÖóWe?¨®ÃWeìÞ{–Š:§?B¦ëuì&éƒú:¨nÛWe¼»«ÌQ^]-Ç÷~ÏÛÕ*#AKÛ´èÈûÙWe©0ÍÔWºMú ñÜQj÷ºpÒ~l6ßä×õ”jí ¸ÿœZw<»•¤*ËBq1©ñ#„½Í ¡!<=΀éÈ)æ~ænõg‡±?‹äÑ+tdŸâ·Lüô×öçž{"0î=È»ÿìezsÛºK7õY«£ŽÜÐÛ«uÓ±èzsÛº‰–ëBK¬BˆŸU#~„¢·ùá»J>)iÆ/9ŒŒpå^ßp÷]áESÉYþÖ’>@˜|M|»µ÷Ý|õæ¶u鳞£7‹ÞÜ6!„¢§“Äâ'kíö“4zqÕ”Pp³>„¯Á›§§„TSÉÚíü  ¸L坸†µ‘ïnÊÆe®7·­»HŸõ½ùXôæ¶ !„=$~„?i¿}£ˆ‚¦fêZ,¸uÆhªaû¿|¢Ü)„B!„—YãG!„B!„¢—’?B!„B!„½”$~„B!„B!z)Iü!„B!„BôR’øB!„B!„è¥$ñ#„B!„BÑKy„EôûoåF!Dï5pfOüj:câëÙûu‘r÷emìÈþ÷–¦G4òŸã¨QÑŠû‡0g^,ÃRk9ôi“r÷EÑê „B!zñÓKé¦o%!ãÂF+÷ô£×]úºýdëÀ¨Á!ø$\ÅxåîËÜu Þ@<ø2I¹ó'ÊóîDæ¯NaµŽm!&3õp¦Ír޼ü¸k[G%'ùàF’r§!wÇ2ëÏiÌ_=ÜþcïÏÌ^=œÙz)^Õ6­u舮ì3Ñ9½ùXhjÛ¬ø^ñ~$„Bôdª?ƒfg²,}†ý÷[¯`YÆ—a“~Á’¬Öc4ó¸Ïéo0 ý2> !c;Ñ÷ü^¥–r/yl:a¶ÅÏÃ+•Á¦›¾•ÖrÝ&%BÀpïVbìõý€Ø‡ßÀÿÊë•‘šô½r9ál·ÿßqé[ Ÿö€2¬…vë«ÚÅéßK+Ÿœ£•Ôý’½ÊÝ—¹Oò€©=^ËnåÎfèÌtž|fa·'ßÒRà|9{>Uî¹üueÛŽ« îD9Ç”;çfÆøP‚ôªÿ„ª¢¥Õ•}Ö¢ïÌ=Ží¶ÄWOÒÖ±è ýЙ:´Õ6уøÍÀço£æsŽ–X-Ô–kýìký\6PågD!„ª?µF@Í9ëo ¼s§]btaøe~&K–ÌgÂà|\övÆH¼ç<É€”(C¯ÙÆ_æ‰,ÈšJŒr³RÑv2×dÃ5óx<õ4[ÞØAAƒõµ5É\¾^ù Òð]°’¨P#åëï Æy-ÚØ•ô›“†wUù/e“ñ{àI"ƒ &ûw”þ¥#6a%ýf§ám̵ÞÄk)·'ÄbŠ4ϼ՜~g‹SpKºé[‰M³ÿ‡†äE„¦Ä⣃æ†R*>xžšoœúÆé5R<1îΠªÿ3Ħ¨Î¾‰³NC<¦m%.ÕÀùs¨8pƱ#ð"ûp3…^rzÁõxÞ|/¡)±xé,™Æ fðÐÑ¢lw|æ~@tèä¯X¨Ü¥ª¾ ¥êû€Øtü¯GP¤môSET¾÷4õ§ú-uÐX®jn®eÛu«0pv&óÒàë¿­¡àš9Ü’Ò=PŠCÛ^gÓ×¶‘„X÷óÊ÷“—ÅÔ:¨ªäÏ›yG$ãB=h:YÆ3»ùÁú’À/$%Ü O]àµF>Ë>ÏÚó®Åÿi^,7”œaööFç-ŒÉ£Éóvdž†0=É?]¨«åàžJ–Ÿjãí°Þñ×rÛ”ñ$G‡ ×˜¨)?Îþ·^go±5A0á!–ݧx¥«šÜ—xvc¾r³v‘AÜþt<†£ß±áy×$}ȃÉ̦£`ë·ŽÈ„1AøÔUsd[û²/¸Ä MÌÕñ~XV¡1SWQÅ×›Šùî5`D³çõÇøÑ!Ž&%s]Œóù“¼»´šÄ%I¤…êi,ÎçË]^܃‰Œ`-·‘Чùäå**«]Â\µÑ6€i«‡c8œÇÆ¿6’úT2WGy££™ºÒÓd?_N‰SÙÓV'ÚùÅÅù¬UÔQiÚêáD·gí ¬u°Iʸ’ëbt4ŸàË«ìÛÕÖÁrÜšùöÕ㔌Žsô[ÇÍ®> ÉÀ¨»û3,Ê×ú­z#e‡ x÷¯–)hØëG^=JáèLh£ªcgÅ3ÿ† ûëÜ©Qô¥–cì1"ÑÓ¢HˆòŲ꒛ó×™Š~°éÒó·ý€Æ:ÄÝϨ”ë4ÅfêJKØ÷Ï2 ms ;X»Ö򾀵`¦})ù¨å¨¹vÛÖÁ÷vßÏ:JŹ£úºÐëlÌü¥LM€â«xe›¿ÙÃÿBTòJ7üÑþÙ'Àþù¸±Nº´nµñZ!D *Gü¨ôÅ«<·v Ê1ßP êˆë›zÄ£øßœŒ'@P¤uãûÔnÈ¦Æ þãsÄzÌ `ZÞ)ßö¬u£–r{BlGxâ{Óè—jIúôõŽ"üÖ?૵N™2ÝÌÙ/s;.䡼ùe§=Ї_ÉÏ6w~©üÔ)éÃd|ï{’Öÿß1…Ë)¤5^#Ñ_¿ŽHh8ú¾r¯êúvªmšÿô)„»LyóÄ+tÑsžq ÕT-åv'’¹˜iÖ¤€ORgÝǤN̬ J cê=Ђü¹û¶Æ…ê¬ 2q§eÝiÝÉUÑÞÖ¤€~¡Lº-ˆéްГ2¥÷$ûY’>¾~\usó=•±j%pÛ]ÓIµ%}ôø‡eòüùŒr{¬»Wà‘DPDZ÷Ýß|Á„NÆ4[ÒÀ7€ä;2Lñ¶3nùp®µß$èð ãÚ…C§XWË'-‘ëb,  çÚ%‰¤…Z~÷ŠéǸŽØ¤Œ+™d¿©ð"4>ž„·1W¸ý¶g@Óþ<œk¢¼­7`}ñÀ”ûí­UϺX³mçh€˜—…çÿo„òU.<6ÄšôÉwIúhçIÂÝÉ®ýÖÊqsÖnŸŽ`Öâ$Òì7¬^D ‹s9f ˜•ÂÍ-ê0ˆÑŠÕFí1}wÉö¤Žów^,qÊóLC?tçù«––:$d\ɤ´ §µ©úâÕŸI³];¡½¶ {JYеòQTKÛ´¼ïhy?ÓDù£íºÐ 0‘+|b~ÖÊc=Ý5ÀÎßP¦SÙýeùÏþ'iÆ@ШyŽ0[b̘ËÉͯ:ÐRY¥s¨m0àŸzq¬$îŽqÌE”¬Â%Õçú{‰ˆô¤¹*S¯[§±=û9¥Î%:™ÿÃÖÅ”ÿÄŒ‰Âôµ›Ñ7꫽êû÷ÇóE”ï^ë˜ö—Õœ9ÓºA„tÄi­ƒÚrµ±^ËKÒÉ\²båîôøøè©/úˆW—¦“¹t{N›€þ$]Ýñ,FP7UGN±`Ÿð$"ƒSûŠÉÊm¼ uÄšŒFî9Í“¯1ûUËÏÇeÀËŸ± Î¥Âo­ûg¿ZDë.7úâçÛ—Ú“eüÙZöÇ%Í€)Šrµ0Ÿ+äÐ{¯Yû8g²ÖòÙiè~5hÏ‹öýïò¾ý¸X~ºd´–ä ¥gÈÉSî±Ñ㨧±´wž±¬Q³¿Ô 0ðGTôc)$BcÅ)v¿xвžÍ3y|ZÜø’<Ùut@P¨5‡óø¿½U–›žPJöbSnàMP¼%ÎsV<ãbt–ÿ?KQn`ÆÜaKøµÔ~ÛÀ+&‚hj8b[‡gÕ JL ‹Åù0osZ£§«¦'+…?8„›ûºy€æ:èðõÕQW|‚-¿ký¸)µ×gãfõ'¨+>a?ÿ—õŸ5º™‚¡Ç?ÐRÛ¹³ûhàKÒxÇí´êØ·NØÛÿI1@Ÿ8õÉÚEÜŽ0Q{Œ›ª«8òÑwŽòžÉã‹R3èC:Í)PC?tËù«±´Ö!9F'Ùd=kàʨ¨ovi¬ƒR«mÃò¤¯1Q:8_Æî5Öú.:Àúý-‡&im›Ú÷­ïgZ¨=w,T\ŠÈæ›üz ž’C—juí‹P‡ÀB¡¹ê8µeÊB!”ÚIüX%c šsÖO¤†˜|¨¦¼ý»ÇÎó¶$@Ì à9ý bŒè8}i˜·™ÓÇ›”¯ ùó¹œ)lÂ#r"áÓ×XF†œÚEÙNÓ›´”Ûb[¡ó6àŸ2¥Õ… òVsêïÄ\{.|LÓÖ}}¿{Œ%1æ<ªEÔÕ6áôQ‘¾ú@ç‰D×ã›E_J)xéO[Gå\È¥ÙØ~»,<1\½ˆèiާɡ©¾¯Cëý»ãßçRóå¿›j·P›gMæØs#Zë ¶Üîg*ÚNÖ뾆Óì>dY.Ù­.¦AÝy¶~vó¬Sªª*yý»É=oRFò¿Û+Y~ÌL¡ÓHö—¾«³ŒêÀz£ÎšJÊÈÚÕÀgÖ²_:V @PHG Îçí5/²é³Ãö-fã÷ì°öÙÅYõûlëð«âh)@í§ÏWeltºñ-Á:Ëù†øÿsç~ÿ¦k=éÓÅùlXî˜îâh¯Ï&ö'ÁÌ¥'ذ¼Ê~<.œ¬çØóÅúJùËtŸ Ë«ìçNáË¥”^‘-›–XMT㜥'Ø÷–Ó”3²ö™Ë¥©¡ºýüUAkM@@ W¤9Z]þÖ)Þ~¾eZ¢CÚi[ZJPÍþçOQxÈ1­¶Þ9ñd¥µmjßw4¿Ÿ©¥áܱÑr]h‰Ø¿ö)2—<Åê]—î1Ý[‡‘øÌ‡#»žWîBáF›‰Ÿ³3Y–µ‚ew]¤þ× –e­à·ã"€®þÍ –eer§õ›”nÑ`I0è§0 % =ÐX˜Mþò©”½óx»Ÿ›Ñ¸q-gàŸ2osE_ᠥܞ À Î:/8üÜï(ÚšƒÑl[ºåãÔ›Î(FË\8N£ó:ÈÉ!*Å@CÞߣ¡Ú˜NØ‚™„‡zÒxt3yž’B#Í„æòT¯DtÞ€QE™v[¨yÁÚ¶gŸ pg.µfðIOèpëMõÕZýû{B8-šñ •I­uP[n÷+Íëú‘|µ…µìpþ½¤‘Ö&ê†xó?wDñy±l´ýLèÀ47Ns¬%ÔU¼ãoáÞÅ™<•eyŸ\–µ¢Ýõ|ºË°´Ptu•~G¹ÇUå±öþz ô<ùnfnž1ÞD8M7¨9q–§ØšR÷ ¯G„úPnXé4]jõp毌·L¥òU¾ÿY¨m¥•S ÿ:¶ükè¶G¥+y ÈÍi¾˜KOtYÒ•ÇÍY{}æ9À/ <Ïý »;Æ3ŠQõ穪wæÑ«‰Êcì1"ˆ‰Ë’ùµóyæf-ýÐíç¯ Zë°ï£ êôþ$ßq%óW'sûcáÄh}d’VíµÍ'¨«á°Š?‡ZÛ¦ö}Gëû™ZZÎ-×…–ØÞo$Þ÷.%2²‰êl§‘üB!ÚÔfâ§G(«ÄöÜSE.'_žÃ)ûœàëñð¨l¹¨›!Ooë¿un>€i)·'ÄºÓø%æ#”íµŒéëí´@ŠJºÁ±x*¦7%d|@¬õé-¿÷›nuã5mŠå©^y«)Ùü͵ïQ¿áŠrJiÆ“€QwYKAoëÿŽ¸Ë…Opfç˜ðÄ+ÖòÔmõídÚêß„•ô›3‘ PÇ£EÝÓXÕåön¾ážýyI¡ž–µ®z8]ÒùÍ $…\úÏá)á$GAÕ±3*¦ u…fÇ·ìÝí¢·­s.œóìÈ#»/£#˜5/žÄPÛ:@­ëö~Pq,ºSý;ElXt€ûË(;¯#bð&Í»Š_etÁ?*Úf_/®GëØûY·Ÿ;ÂÂãV|ç.¥_0f?ËY燸!„hS›‰Ÿ‚ËÈ\’Ϋ¹Õ.kQ¼_Ô´®I±ŒM'”¯ìJ9ÔWTrnë4U:eö#¦T)³ý#ñž>‘‘ò9ÔåfªŽ–r{BlëÖ¯ŽMG,ÿð‰~äJÂG)§di«ƒúr{· ϾÀÔVšÉµ®Ãs]’熷¼Aè ¼uzÀ„±Ü‘ :v Æ)oqªŒõ@ÉSº60,Þ ó‰2UÓ)ÔÈ?iYw\zá¬ÓB"½H}ê ûBãÉJÚKo¹sòdè#˜˜€TuÓMººmEÁyv­Ê§ÄäKò½Cݵ‡»]ªúloe€ïàD~þ @[þ>Ò‹¤ÇbHu3õÅÃÇ1ÂÁstSæ (ËkùØf-±ÆzàGÂ4†å^í@à‚ù„5Y“>2ÒG!´ê3ôŠ‘ŽöZ1~á &$sùz ;3 õÜö–OóšðPÛkZÔØÊÐÈcþ‹îæžÏ|j—ëú= +é7Ûòħüž°ló˜AÀ¢E„š¨Þáxü·–r{Bìèu­¬õÒò¡núVûô'¥åºa{}uöMœýÜiÇð5ÄÞ<¨•!óŠþ]I¿9–,Î1µÇKñHŒ¥É¹ìVÛõ‡VS²M±V‘B«õí’:´ìß¾7½I쨷™ÓfÀè\®†:h*W‹v®ÍšÜ—ìO“8;“yiïLç•=NAÖ2œcU 1°îç!päs?»IÁlœàO­â÷‚=Eüö¤¤†ñô(wßœþô±Çö×¶ÆþcÇFòh²—ëëq”á«EÔ´t×úã¼[ô% »f!KîHl15¬Cýkåyw"÷Ž÷âè†<ö´ñ •“™1Ì›’°í-§³â™C5‡óOîÈ´Å D++ `:Ç'« ,묌ˆ`ö¼þ`{­²,ëïÎÿç¸åÃInå>JY7µm˜¶z8ÑÅù¬mo]kZãÒNÚ-_ÙX¦;·?þ¦*>Yu±6†:h:nûÌgV<³nrzä¹MG^=Â>ëâ´¶:¸£\ÀZK¬ÝäX~uGh‹z(ÛÖî1°2ÌÄ]#[Pö¥Ú~ Ï_Pߪë`='Ýö„©‚Ý¿+¢P¹îÊ:¨n›Û:˜©8aÄ3>£¢ÏTµMy­)¯åûŽÚ÷3g.õ®â“VÖ S{îh¹.´Ä:LdAÖTbÎå¹âMe@Ÿw¬œ?Ck‰µëÚ:´õ™Z«ƒBgîî/[rù+G°?@&AwÔ…-Ô¼bYDØ6¨µÙlĘ·Ù5á—NØÌ4¼1rÖù‰O¶P½-—< ˜ø¨ËvUåö”X…fsõ§r8õêÃ-¾ý¸ðM.gŒ˜Ì–„À³‘šCí—Û¦ )ZŸMU…Çíq?P²þaGÒ è JwæQ×`ùµ¹¡’ªìÕœùÜý7TΚÍMÔŸÉ£dýýí&}ÚÔÙ:´Ò¿Í<Ëé¼Rš¬­ÙÜD}á>Šß˥ń; uÐTn/–wè,¯©¥ÖlÍK›ÍT<ËßvWayþVÏRºm[rOQo{[4™8—¿—õotó_ ókX·ó;Êí/ê¼´Ô8_ÞöÍ—VçÙöâ1rKëìïQÐHʼnlÉts“¤Á¾Œ|z¢–:Sû:»¥mÓçe¼ûQú ®[<¤Ê€®§¥Ïêß:ÁÛ[ORrÞä8Φ:Jr ]’î˜Mu”äkå&ÔU»±ïñîÞ³TÔµN¨a\÷;WSgßb¢ª¸m[ϸ½6µôC·ž¿*ûAu¾*c÷Þ³TÔ9M 25X¯c7IÔ×AuÛ¾*ãÝ]eŽòêj9¾÷{ÞþÈý»ºê¶iÑ‘÷³¯Ê8Raš©+®t›ôAã¹£Ôîuá¤ýØ‹ð(õvõ„:!„p¦jÄBˆîÚþÜsOƽy÷Ÿ½ìm½7·­»tSŸµ:êÈ -±½Z7‹¡7·­›h¹.´Ä !„mQ5âG!DÏ–0&_Sßní}7_½¹mÝEú¬çèÍÇ¢7·M!„èM$ñ#„½@þŠoXûhùî¦l\æzsÛº‹ôYÏÑ›Eon›BÑ›HâG!„B!„¢—’5~„B!„B!z)ñ#„B!„BÑKIâG!„B!„¢—’ÄB!„B!D/%‰!„B!„Bˆ^J?B!„B!„½”GXD¿ÿVnBô^gfðį¦3&¾ž½_)wÿdŒÁÿÞÂôˆFþsü5Ê!z 9×…?E!÷aμX†¥ÖrèÓ&ån!z 9×…ò8w!ºÒèu$LŒ¥:û&Î~®Ü©U"ºëOø¨X¼tŽáy+;;3 Õ ÷—¼È^eÈeèOóbè¼¡ä ³·7:oi!ã±\åPÃöWÏñºbÿر‘<šìEÁž"~{L±SôZÎ-±=I{çºh›ç݉Ü;Þ‹£òØó©roÏÓêÛê É¬xæßDÉGØö–rgÏÓ“ûwÚêáD;o(ÎgíòóÎ[ÚÔZÛ:Rî¸ÿN²/@Ÿ,:òOyȃÉÌæ}ÙwÑ1ZÎ-±=I{çzw‘kèò¢:ñ3hv&÷Å&sÅnY¼‚±ÞÉ\¾Þ%N6’ÛfMâÊØô¦JŠíæ•Í_ºÄiæq=ž·Î'bpž:€&ê ÷Q²áÊHm´”{Icgàÿð" N›”Œ¹ä¿ð„ãwUåZ…<€áÖ)„ô7 šÍFjóvQ¾í%E`:aSPl7ÿ¿F}¯\NèµiøyÒ¸`6Rç¶©nø†é®±uGwQöŽ›XwZKüXëi@¯³l2K9÷ñZj¾ùØ)ÐÁcÊ›Ä\Òb>f‹²583ƒy#B¨?º™¬×:QPÒ‘ò±##x4Í‹ÚãgøÃÇMœVî¿L?7 çža&>y½ê’Ýà÷„:¨¥åÜÑÛ“´w®÷$Cg¦s{j ûŸ^Óc’Ò£–'“¬Í(Wîê‘zB}{B4¹Ì?—¢£ïÌÄ”r-j󦲳7έµ­#å†Ü?„i¾Ô8ƆFåîËö¦Uí±èN=¡ji9w´Äö$íëÝ¥û®!÷›¸ÏRÍo>Óî",1½»ÿÛE"ºë%$y>AžxPDþò¹ÊÀKByOÙªèà¨9gý-ƒ7pÎõã£.i?>›«mI}1#fóÔüÑ.±ÚŒÄ{Γ H±%0<ñ‰›ÈÀ‡Wº†j¢¥Üž«…†rVÒïþ™DX“}uüSg{Ï®±Ý&[GáoMúxtE´´-0°-c )3‰½7Ý5V ¿tÂæYʵ½è Q„ßú$a#Óœ£­æaH ¡/F*v>Kþò›ì?Iúl^Næ’ô^“ôøí«E̶þ(w¶â³/˘ýj1s{ø°Vý#|ðÓõQn¾¨zBÔÒrîh‰íI.§s=<<ç7ÊKíÚþ$BÙ¡³Ê==SO¨oO¨Cov‰ú×⇯ÞrÓ–m‹°ÖúS¢ÜÙž6ÚÖ‘r+_þžµ‹\Ôá‹Aí±èN=¡ji9w´Äö$½õ\W¥C÷Y*„ÌÃïž­Ä=²ˆèD§ÜFk à—‰“‚ÁžôéYT&~ †zc¥õ÷8‚ýÁd®w‰2—Ÿ¦ª¦Œ#ï­%sI:™KÒùë'…˜}Âh&µ5Z¥ }F/"²¿'Í E”l|Âr¼>›êèkH#jÊdåKTÑRî¥ÝBÍ Ž›—Ÿ?`Šöu \ ¢”&c)gw¯¶–y?…û‹0º¸qŽ8' y¶X§Ÿ63 í3Uäqú­ßÙëpbk.ufÐÅR†ª§¡m^Ó¦ ƒ†£ÿv´éÕSa]ÿ‰$¹„«Ö÷ÚQör ÿ2ÇRîs¿£8§3ž¤¸;CÐ{§öqþÀûÊB!.‚„1!øš*øv«ªÁÑ—\O¨oO¨CoÖ›û·7·M¡šûÍŽÝgµÏëæ»‰Œ3àa®¤ü½}í¬8Ÿ™3 ²ÝÏ>åTçž1Ú‡ö§zMdAÖTb”›•ж“¹&[¹Õî–Å+^Í¡¿-cÓ åÞö¤á»`%Q¡FÊ×ßAóZ´±+é7' ïªò_Ê&ã÷À“DAMöï(ÿÜizYÂJúÍNÃÛ>DKK¹=!¶5¶6—’¿ü—Öm]QnkeX†Þyæ­æô;–imŠMÇÿúqÙ‡Þ5ÑXQDå{OSúŒ2ºŸ¹úù+ºîèT¹îÚö¡é3 4ºékŸõÉ[Í)ç6{\çÍ÷šâXƒç‚¸Ð±:h¥¶µ´ÍY{çŽ3-±­ 1°îç!”ï)&/!Š©tPUÉŸ770òŽHÆ…zÐt²Œgv5ÚÛ§¥mÚÎuÈy»ŒcCC˜žäk±Õƹ®–wüµÜ6e<ÉÑ!è-ó¿©)?Îþ·^go±Ù4á!–ݧx¥«šÜ—xvc¾rs÷Š âö§ã1ýŽ Ï»~Ñe—d`ÔÝýåk)ÚHÙáÞýk2’¸ûã•@¾/ÐL]i ûþYF¡Ó<‰V‡Í[§"ÕÎcã_[CPYßíöCJ8³@PEk3+¯†aO gLT¹/~ONžc{»å:q3Íèè۲߸7,_©8qšO^®¢²Úc9npäÕ£ŽÈ[|]5G¶°/»åûž–úڵѿ–:4óí«Ç)稳›:¨®¯õ¼kK[çä´ÕÉV;U¦¶)µW®Úé:–~èÑ¿OaZ,£mÇCÙÎ4\óZ´{>tàXxŒdô´(¢|ñÀL]E_o*æ»CN/´ö™ÁúúÔ§’¹:ÊÍÔ•ž&ûùrJª;VÍTö¯–¶9kïÜq¦%¶U#"˜=¯?Æq4)™ëbô˜ÏŸäÝ¥Õ$.I"-TOcq>ÿpú?´´MÛ¹®îýA K¹: ¶~ËáÈL„?í—;fþR¦&@ñÎU¼²ÇÝ}…úûÍŽÝg©¨Ãð¿•|†Ò tÜ—µ6Õ+é/ ˜•BßS»(ê’û¶î¡rÄOW0ÓРܦÆd|Cª#® ŒˆGñ¿9Ùòá?(Òºñ}j7dScÿñ9b=f0- oŒ”o{ÖºQK¹=!¶ÃgMGßsÚØåÚ5ÑÜ¡ã†e]¢éSwzç‰Wè ¢ç<ãªä5ýõ뉄†£Ê/(×…sÛ ôÑܬ„_TJà˜è´q2¾÷=É€ÔX|œ^öp3CáBÎê€À›_&|Ú£xø¥á‘ü asÇᇑÊO­oF±+é—ñ `›×ÚŠõwërš^·ñ!ù—‹™‘fMúøô'uÖ}¹wyêK¿q‘ü:Í`Mº¾~\usÿ_ˆk¤o¸U³£eKJèôôOáNE¬÷MéÏ=Îÿ?}ñ‹áž –µè¦H®Šö¶&<ð dÒmALWÄèýýøÓ/c­I€>øE„rßD{k»•–þÕÚ¶K-(5Œ©ô@òçîÛ"ªúà9 È¥}Ý×6=)SúqO²Ÿcšž¯WÝÂüân»k:©±¶¤€ÿð¡Lž?ŸQnÞ/{’À;"‰ Žcï·r::‚Y‹“H³ß x1,Žq#\"Iȸ’IiAöDôÅ7ª?“fºvB»õ½Ú«ƒª~È+§° cTŠc3X’‰Q@é—¤ªr­†=¥Œ]+‡“2®d’=éàEh|<32"÷q <0+…›ã}H¾s£»è|h¯Á“„»“]ëì@òÖâcŸúú^ í·­»ô%zÚnv>­õƒ†k^‹Žží}wÉöä€ßÐ0®K\‹ó<˜öçá\åmm__|£0åþ¶¿Ôé2úWkÛ.5Ÿ´D®‹±ü!Ô†sí’DÒB-¿{Åôsi_÷µMËûƒ?:qÓlIÚ+w"W&ø>Äül’r§fªï³\¨¨ÃG¬IŸöéRbñ¤’Š=7éCû‰Ÿl^Y’NfÖ^Êâ–é[™ÿú–zçßÝŒ° º¤pàÜ÷ü»#“%#B,­uš™×­øÜ³•ón#<Ô¶Lþ±Öøó¤bof]qs€îÖ{ 3€1ç¨É·fµ”ÛbÝšŒßèAè(åäæ96wº\ p†Ph®:Nm™r'x§,²'#bo%|ºû5p~<_DùánYÍ™3M DØHeô ü¶&8ÿÄŒ‰ÂôµûL¯¶rܶí8F t7ͰnK¤Oì£î‡ 0Ê^DŸëï%"ғ檽É?ûÕ¢#mLF#÷œæÉ×1—]/Æ&¸„à9 ˆÔqp÷If¿ZÄm,ã”<ãü¸ŽÕA -ý«µm—ZP7UGN±`Ÿð$"ƒSûŠÉÊm¼ uÄji›¶s½/~¾}©=YÆŸ­e\Ò ø‘Ò‰>3Ÿ+äÐ{¯Ù§?“µ–ÏN›@ŸÈðk¬A{^´ï¿ëmïW–Ÿ‹>ÚHKòm‘`p6nV‚€ºâ¼“uµ‹ðYßñÉQc‹!áÉ1:¨8ɦgëG¼óQõÍŠÈŽk¯¾C{uPÛ¹yÕ€/qã]ßg='‡™‚Ü*—íjËõ¼;‘1Q:8_Æî5–c¶vÑÖïw¾c‹ϸ¥…öã»ö™<>-n„ÀþŒ¹C¹Ž™ÿ@å|°Öc÷Ñ:À—¤ñŽÔê«Ô^ÿ‚__K¶üÎRîþR3ÀÀ”±*êûÖ {ý>)ÆúÄ G×.:йÑNÚo›zÚÖiÑêÕv?Xi¹æµPu>tàX4UWqä£ï1ÏäñE©ô¡ æ €WLÑÔpdë7–øU'(1.>”:V-´ô¯Ö¶]jA¡~ÔÎãÿöVY’Y¡”ì=ĦÜ:À› xG¬–¶i;×µ¼?h¡Ç?Poy¯´žÃm—›Í7ùõ@=%‡Ú~,¡ªûÍÝg©¯ƒº@˜²†¨‡·3ÐZç¸ÅoàåõÊðK¦ÄUB0ª9g=£ 1øPMyûwŒÿåh©æÐÖw•»Ôñ¶$*Ì à9ý bŒè8}i˜·™ÓÇ[ŽÐhþ|.g ›ðˆœHøô5D¤0ŸÚEÙNS¿´”ÛbݱöQ$Þ:[.#ñ™9F*v=¯ÜÙ‚Îۀʔ–‹F³ãßçRó¥SRªv µyÖäH»ùO W/"zš-cÓ™r[kÛjsŠ0ã‰a”íMæeâçÜFDƒåB1Ûúíz|“£èK)/=Bãé\Ëæ ¹4[éÛŠ"êj›pþH××`@è4ùªè NÛçƒî¢àÔ®6çµv'SÑv²Öì  h8ÍîC–ea Ñme {ŸÚ#§™»«O¬‡ö½g(<£½±ç£Hñ‡¦’r²v5ð‰õ Ëóõf^ß{ž¿Ø–Gë“0ø0)Êq“ñɱþ¸ã®þw{%Ë™)tÊżô]eÔ‰»ékؾù,Ë‹,gq±Ü‹õ@ý«¹m—ZÝy¶~vó¬Sªª*yý»É=oRFvkÛšJÊÈÚÕ€-?÷Ò1KJ-(¤£çóöšÙôÙaû³ñ{vXßKÚ~¯¾Ä¦Ç’àÛ2Á`7±? ¾`.=Á†åU”Ÿ´» 'ë9ö|1‡¾r o4\‘æhtù[§xûyåíLµWß‹AEÔöCÓ?S`‚ $×á|Iƒ ®’Ãï¸lV]nZJPÍþçOQxÈ1…±ÞMÂ%.)©‚}+*íÇ—3|÷üiJ€ˆ-‡¿ÖγœÖï _.¥ðŠtU[_*ú ±8Ÿ ËSÑçYæBZŽU[ßn§²mÝEU?h¼æµèÐù BÎÒì{ËiÕ™FYÏ·o¿¦*>YõûÞ·NÃ=VÅÑR€>ýóÒ>ý«¹m—Z]{Ö5rÁög½â4Ûþyó-ﺳmZÞ´h,ÎçK+í×P{åî_û™Kžbõ.õœhý~Så}–BGêàÞ ô€.–È1ƒð58=¨ÈÛ²Àtèp·CŸ.º6?gg²,kËîºHý¯,ËZÁoÇE\ý›,ËÊäN§,¥³ñ —2¹ï|‘MǬo"Z5X¢.q R¢Ð…Ùä/ŸjyĶ·û1è×rÖþ)ƒð6µœo§¥ÜžÛ‚óhŸW]wuªÜ‘xß»”ÈÈ&ª³FHÙ­à¬s"â¹ßQ´5£Ù¶h´"«û{Bl%ÖyºÒÄÖN X?û…;s©5ƒOêü–Œ¦rmÚn[óçs9•ýu Øß8ÌUE”ïÎňÓ*ÑyFeß´Âú´°ðPOn¦à/ÏSRh¤™Â:³Ú|7+Íkc$ßOHyyËQ*UÖÏb¶+©_¨Oàô±†N­åãÎk{«©õòåªI1l¼/’çÆû1½SÇlC¼ùŸ;¢øÇ¼X6Ú~ÚZ£§²Ž×#ð_ß^ÄìWÏuû#Ûµö¯æ¶]bµ…µìpþ½¤k:¹…îlÛéc޵„ºŠwü-Ü»8“§²,Ÿ–e­hw=Ÿž`XZ(:7 ÏÞxåyênT÷}TAÞŸä;®dþêdn,œ¸Ê#×^}/5uÐÒ‡O4‚o¨c DR(É¡PuìL‹o¶Õ–ëÔÕpXÅŸïˆP@Ê +‡3µÓÏÊxËš¾-?KÏ(òõ穪åã`ÔÖ×™šþ¨<Ör}Ö¨­owS۶´^óZtä|PÃcD—%ókçó·­5zJ+9¦zlùÖ.:Ñíl×Ú¿šÛv‰Õœ8K‰Óg¨šÒÖŸ¾ÕmÓòþ E×—«á~³Ýg5îã¤m&ÈsÏr*¯3žŽZ¤ ½$ÚLüt˜.–[Êbr¬®õ“Ô*«Ä–22Uäròå9œ²Ï·»?€Ê–ø ‰xz[ÿ­kùÇYS¹=!VÉ:ÚÇ\èx’—]GËõ¸ß¹Ké׌ÙÏrÖyqìÖ4~‰ùHe{-£múz;Ý‘&¬¤ßœ‰…:®Ú…\.x‚3;À„'^±NOàêH¹*Ûváó…”®º‰ëMÑKs©)³dØ*ŽX£¬OÜRÉþ´°¼Õ”l~‰æÚ÷¨ßpE9¥4ãIÀ¨»”/—™ Ÿîy+ø!¿Š¹¯ñZN§Œ:ú垟Çð)^ R«änàÙŸGêéX/§ÓÒ¿—[Û´¸ÜÚ¦KšÃ#¿¹¤ð€‹}Ù9)á$G¹O0Øxj|Œqý;ElXt€ûË(;¯#bð&Í»Š_etn PWßn§²Zú¡äËóÔáMÜxË¢7„D5G¶¶ü"Qm¹ö5ë.1µõµSÙ¿—¥Ë¤mZ¯y-4ŸjŒŽ`Ö¼xCmëõôlšú÷2k›&½¹mÑÆý楿Ϫ¤Ù 4äqjÃÓ4Ùf‚4¾Oã;ÿ¢Êx]äQ”­hóÓtÁÆed.IçÕÜj—9÷ï5­sïOêòÆ‹fl?(Þ¹‚Wötv.@õU•œÛúM•N_ÓDL! ¨R~u3ïé Ð)ß™CQn†…i)·'Ä:³ö©¤âw Vu \¯»ð_ðQ‘Pýg?ÿØu;pXÞþ VØäžÅ¿&5¸ÞšôéÄH»iȯ´ ך³ïë“•"Ò 3 ÀxÄi­@7íDõ÷¤!ïïÔÈàì!#Òè7Ýy­-åö„X'ÖÑ> ÷¹]xYs¹¸`>áAMÖĈûÑ0nùMÆó¦uD¥†X.ºÇÓÅl'ºÉ6RÆk$ú‘+ ÕÞ”,À#¾I¿'lz^4aÌs\ÌšÊíhÛüÆ¢K~†–Ò¯¿'æSûœFHm¡©Ð ¢ßôy–Mâ¿Àëê8±,¦=æQ<ü¬o>^#Ñß”fYU_™„=ŠÞ§ý¬ÿîèÉSS,‹ ŸÊ¯Ç>+8ÿ§¿Á‘¼0Ö“‘N§Á}ãy¤£S³BüxnªûÂu8\f²<ÍÉÍ磲3àÃU#=Ikå ´ Ͼ–DZWšÉµÎd».ÉÀsÃ[Þüt„š:h¢¡»»m—ÒåÖ6o0a,w$凎ÂqÊÛn‡*c=Bò”ÑÊ]–ºì æ¯Nãžv¾EïÕn‚½U”¾ƒùùƒmŸ+#½Hz,†Tç'ÐŒgÚS$vºÎÔs²ÂlYHßiñîÊ3–GM†§…`Ù64#…âݯ“€Úú:QÛhˆUU ý`“{¨|Cv äïw“äÔPîÉ3–RG?hù\ë‘b`ܲ+¹.¦å'ó“'ë@ÁÄŒ¤ª›vãá㨃çè ¦Ì@P–çôyXC}mTõo¨ª¯•±Þø‘0Í€Áznv…îj›.ýj`¯Üôƒ–k^‹œjŽ…§N˜1V4`´®ç>+‚i£[yFjê ‰†þíî¶]J=­mjÿh5fþR–e-eÁ„Ö’ ­Þovü>KsÚÐxä.BÈËñ´=Úo>÷ÞF°74ÎQ¾ä’è3ôŠ‘ŽÕíZ1~á &$sùz ;3 õÜöOó8;“yimœ˜5¶24ò˜ÿ¢E„»ùlk>µËuýž„•ô›†·1×±®Ç -"ÌÐDõî Î~i‚¥¥Üž –Ñ>Vu´ôƒO ÓV&XÖÔ©;ÅÚÿÏ͇-åº5Sqˆg|ưí-Çžqˇ“ÜÊýN‰SlȃÉ̦üô`ÑXœÏ?–;­á¶VÊújè_[œëÀ¬xæßDÓ¹£©¾6“cùÕ¡-ÎwçrmÿWk\b5´MS¹bµöƒêk^ ç¨;†¹ƒ¸k¤ÛRAqþL[=œèâ|Öº;öƒVjûWSÛ4œšbÕ²_l¯U^Ößmuî®¶iy°Sñ7 Cå2‘YS‰8—CæŠ7ûÛº/ly¿Ù±û¬ÎÔÁÊù’ø?ìþ¾»¹¡ˆ‚Us•›/ w÷Î-¹œqû´|ÚH·¹°…šW, 5Ù]6›ó6»&FüÒ ›™†7FÎn{Ö±ýª·åÒ€'uÙ®ªÜž‹ëhŸV“>t Üj67Q*‡S¯>Üb4MóÏr:¯”&kšÍMÔî£ø½\Z®aïªÙÜDý™¼Ú¤¥Àùò¶oB­êß:ÁÛ[ORrÞdÿ;‹©Ž’ÜB×À¯Êؽ÷,uN箩Š'Ø’©¸©;SÅîM§œ®FÊãíKq÷6¢¥¾ ¾@}¬ê:hé›úó9a‰¯:æ®4–ûUïî*£ÂÖ¿uµßû=oäþ Ú—q€OOÔRgÒþ^f6ÕQ’{¬Eò@S}µôo'µZ_›÷‹xwïYGßu‹Õ¶Ö¿:ÃÑÒ:ޝ¥¾wÛª¯y-4ž îX×ýÀÎÃÕÔÙ·˜¨*.dÛÖ3­¾ÿj¢¢Z©íßnoÛ%Ô£Ú¦òo€vÚ¥ÞÖýfÇסmîï»ëŽg÷˜¤jGü!„Bôj×öçž{"0î=È»ÿ¼ >õ„ú^„: [6œ1¡Õìæø%Ô–V¿õî¬nêßn«¯ÝÔ6!„î©ñ#„BÑ›%Œ Á×TÁ·[/›ÐžPßn­C¤©O]Á˜P¨9|²Ç&}ºS·öï%Ö›Û&„=‘ŒøBt»?Í‹e r£[5à¯:vû«çx]¹¹ÓÒ—[Û´èÍýЛÛ&ÄE¡\·âü)ÞYZF¹»)/=DA£ÁåVßËÉ´ÕÃ-kRµËýš)½Eoî‡ÞÜ6ѻɈ!„BÑØ¨*>Á–å=;é#„B\dÄB!„B!D/%#~„B!„B!z)Iü!„B!„BôR’øB!„B!„è¥$ñ#„B!„BÑKIâG!„B!„¢—’Ä?1gf°,kKæ_«Üõ“2vdçÅð)^ôSî?9=á|è uBtNÈýC˜¿z8¿ÊðWîB!.IüÑc%¢»~Ñé00ã¬?a£•qZ$0jp> W1^¹û2õ§y±ltþ™ê¥ iáºo ž|™¤Ü ŒÉÆy±ü)I¹GôFíÃ¥¬CG®¡Ÿ Ï»™¿:… —I®¼'Ô·'ÔA‹Öê;mõpæ;ÿdº¸‘œä €WLîþ|„<˜ÌüÕÙ6K¹G!„è>}†^1òGåFwÍÎ侨Ãd®ØÀ-‹W0Öû ™Ë׻ą]ý fÜx1ÁzL¦JJíæ•Í_ºÄiæq=ž·Î'bpž:€&ê ÷Q²áÊHm´”{Icgàÿð" N›”Œ¹ä¿ð„ãwUåZ…<€áÖ)„ô7 šÍFjóvQ¾í%E`:aSPl7ÿ¿F}¯\NèµiøyÒ¸`6Rç¶©nø†é®±uGwQöŽ›X :Ò6)osuH‹ìluöMœý\±Qƒ33˜7"„ú£›Éz­õ šË@ç %g˜½½ÑyK cGFðhšµÇÏð‡›8­Ü?6’G“½(ØSÄo)vö`7 çža&>y½Š×•;/’žP­Ú;.†KY‡Ž\CÝaèÌtnO­aÿÓkØ«Üy‰ŒZ>œ4N²6£\¹«Gê õí uТµúN[=œhç Åù¬]~ÞyK !÷aFš/u'ޱa…Q¹›“™1Ì›’°í-åÞž+úþÁLLi çÑ".ÕŸÄžPµ4}îÓòÙSËgZ 4ÕWqYRÞS¶*:8jÎYKÀà œS~,ÈŒY£ìI½>„˜³yj~g†)ŒÄ{Γ H±%0øÿÛ;÷ð(ªlÑÿ Ϋ“4y‡@BQF" ^AñCpŒN™…98¨8Ž:Þ8œèEæ+|Ì#3Œ‚Ñ# ŸQÔ;Q!Œ%HBÈ‹„NÒyv'Þ?ªº»ºÒÝ©JÒ1aöïûúûÒ{¯ÚY{Õ®ê]«Ö^0’2InvÕ…žvG‚¬t´›¶™ñ÷ÞIœü#0Ö`"|Ú$/¹ß]ÖoäuëLÂ倀¡ÐAOß"óˆYÕWÖ”y'É÷ä¹Ëêb }[)3бXiÜ»‘ò‚ùÎÏ`œ>gv¿6ï¢qúür[%¹òçŒºÒ Ÿ~QOî¶*–ó¶¿IŠ !Ì0F]<¬Œô2ÆÃ÷©Ã@®!Gx óGëûçº$Ò#¡þèyuÍÈd$è;tЃ}w¯>ÌVùS£®ôBÓKß²uõaNŸÑŒ)*ŒÐÀuñ°2tІŽyŸž¹§ž9­.tè+F-?iÄŒƒk“ü=…qá`³w¨ält6|Íž76¿6üµylþs1l˜6ƒkTÒZ3k5ñIFz;+©Ùñ¨ôüf-0Ö”EÂÂêC4¡§Ýï_v­¿u=ü»}öžÄtV@»@c-ÝÖZÎïÛ"·y/ŸUb )³]r :K²ŠÏ ¢}l¥œ{ûq§§ Kh·ƒ!e¦ZT;:ú”½tžøWŸ¶ýV0$Í#ÂS̶Fô÷-ŠÀ` ú͇?TW `H»&ŠP[#_j ŽþÞ úŽô0ÚôŒ´ÎûtÍ=uÌiõ¢U_@0zég©×„,ßObôIÊ7Ýç^1¨v=õí~¢óî$ÒêÁ.²ÍÆ”n¡ZÙgÕRº^»•Ίbjÿâa)¼ö \Kêª? üõMêÊàáZörÝNÊÍgE|õ‡9sõR~˜™DH ÐQÍÑݯñÖWŽÈ¿‘ÉÓ+’™äc™ŠÖ%-ÒR¯1y¯Ž#)ã¸}jfÃhoãÈ& ªûÞ:CM¬ž;ެÄ`ŒvÕeçÙò©“jaä̈âÇÿ§—¶z ïYy×ñB9};æúN$Úv¼šåŸö8¿GF±zV$™±A c€Ú­|ZÔÌVÕJ†§W$+ÿ-ñÜ|G[}Û÷´±¿g`:h"ÊÄ+wDÑp ŠÒ´n™`KÏïìdÆíñÌŽ ûl=ë>èrÚXoß´(~§ž²Ë¢ÈI•¢š|Œ­hÒA§úy§ž#—Eñcúöw é!8õ:n[8‡ŒÄ(l´6œâ³·_ã`•]šû3Ößœ¢:ÒÖ’ظ£\]ì_âÍüèÉTL'¾aûsê]2é&fÞÄÔ„Pù |õÇÎðÞïÛÕ’¤Ü›ÊÌÌÌc^Úkk8ô§z*kV¼.ZœÊÊÍ´+eÇ|-úú-:ŒR›eo™N¢¥^Z—…IúŽáøëßR‘•Ì,GÿÚ[8¾û ‡Š<Ü#uØLýÚW¶¡/Ôö ¸*’YÙ ¤%„"e ³ÓÞhá«·ªøæ¨â@Ùf&ùøiOdpeB0zi¯=GÑs Ô´ LÝè´ï5+Ÿâ–4¨Úû,/Ð>'ê;ïÀܳžæ´î ¾`4£1âgà\¶ðgÌ­¥Epú, 4°w¿™Å=DøÍÒÕ9^.ü¶íE´Ú!|ÎÃ.Ù€EDdgŒ•†ÝåB=íŽY/L_D´ºO¼¯(‚vtÓÛ©.ÓÊ"Âs›äpÎ ŠžBâÒuî¢j‚fxÃ+DÅCç uÄË ÚuCÙ7c @O·»@e-Ý@PädEaߥtc &B'{XJ§Æ[ß’73Þ™ÄYΣ”´Ð™Ô9mÍ~Òúk{È!ã'°(Kvú„$1mñ2nò•gê¢c,ãgÇó¯Y&Ù鄆qÅÍqüo)G¶“ÐXÏæ&2Óáô0’”ÅU²zX¶0‰%ÊÿÏXÂâ¢X2wpÉwWÏçŠÄ`Ù1@Xt$7Ýf&G% ÆÓ?I–>c‹‹fÙúö¿þ!ÛîÊaZ²ÃéHxìe,X¹’™#hE—'"o'ŽvÊ>ôâ˜ÇâGÒÉr>,75…ÙW¹I’¶ærnÊ2ËØc MHâ¦Åý' ÖJ¿úýê l&3–ÄìK¹YÙ¿Ð2~<…Y*;è±™üeßYw§‘átúŽáºɤ„¸‰`Œˆ!ûùé\,÷o,¡ Xx¯ï C†nûÎãò´ „‰ÿKc~oó>ÝsO_x›¯¥¾`4Ó㧈—׿‘¿á @Õ^iùVþŸ¿¦CùÝ-j ¯ÙÄú Ògéõã±ü¿­lÜñ­BFqQÒ¸M^ft+!K ™´â6b£kQ£O–å›ÿ“ƃ•Ø ¤,ÿ†[ï!ÆÖâ_ÓZ.Gƒèiw$Èzda³¦` –³;ÿì*t»@ä"LÑÐk9E[½º‚3W;É›ã9ÎwÍ•4ìÛê _ýÍêêºÁ0…˜jéE„?(;8~ñ™xM¶¯HGåGl{*ü§žåÀ9Dú•#üi­ôå21HÛÙz¶¼!³ýDDÖTw;<4/ 3ßI²Û%ÙUÛϱçD'mn’úÈœl’ÿî¶J¶¸@½òEdÙgÝž€Vö(ú™»­²O¤ÍjåÈs<öªKæãú çÚ47QŒÌL¢#ûÎ’»­’ÛQOµŒ)a,c`:èÁlÆr¼šU‡¬€‘¸¸ªU±¡¤Âí’ÕÓ7}ãa,a¡ci;[ÏórÛ×ôadz°™Vôè Ç`À.éÛßøõö }ÿUçòïu¶òé9NfúÕ²Ðß9ë?¬¨àCçýJú {´• µu—ªk$f/N ´WæÝ Gغú0ÿ½á>9a¥U%›1Ñgyk+gÌ»ÕÓØÑ«’8ýé;ô§ÃÅl3}ù€1GIvû·ïD;JúiW0zl¦Mö}û´³î“* Ÿ(ú¹uõá>‘6Ý-ŽôKf])¯µC`4—e»‰41ŽDZ9^øIþÙÓÔØÀMÓAúí[Ä?Ê;€jŽþM]©@˼OßÜÓ#ç´J†R_@0šéÇñ#“6-\ÉLã¡…†þŸ@ÿ×J~qç¥ê mKŽ {'sþÈÄ_w1]¹šÄlÇ¡ƒÁ´ë­o»h+®ÄŽÓL‡Së%R—ÞF\’IºPì.»]’L€ý$•¯<‰­ù”Tض‹Ž7‹iB“ûË;å¡o•rÎáÈ*ø€€ê\έ‚ÁçQÒƒ­r^ü+g:Îsì;*¥›5%úò^|´?Çò:ùD>ýï¬ã `L ÆécLˆ 3ºkØðA'ŸÈ/›;ì¼v°™ß8Ò£ ›0…pS‚+Yò'e­<øñÀ'ºÿµ§‰‚2; _Ì ß´K‘$žrgvµ²gçy *¥‡‚vk'%ùaO{3…ŸöÐÜ#/Q²4ñÚ7ßQÒlSKêkêÙðA'Ö e’[Ï5Ȇµ¢Ãh¿~£œw^üo}zÌYb·~Ë_å{‰ï{õ÷LN2i¡vΔXÔ5ó’H {íi¶Xh8+ž³”=WÅÑ/ÝÅ»l@D$?ÈruºáíjÞyÎó£¥núÓw8èOa37Z•JvßGV¼TK ¯«Õi3ÇVñêOî}#Dýeßâ§NsèmEU]GK¥uÉ/y›…Ož=É¡奟eNÔŒìíºtÚ×Ág[Ÿ ílù@O~ó>sϾx›Óº3tú ‚ÑŒOÇϤÜ|)rç®BÓþMŠâùåì8 ‚+ÿ}ë7äóãTåQå¼UàzK÷Rál0îªe,¿Úã-ß7Ýô†É ™™@ ÐUQDyÁ-Ò6‡Ážãë»vlå¼Â3§l¯¤R'EO»#A¶ÊhŸmîUƒjwÁ÷ƒU:Ÿ|åè©m¢LêXVp˜­«Oû}ËvÚWë¼OûÜSï9­~´é+F/>?CÁÙ¿¿É–w¿¡ƒ@bRû{0÷@}ò;l%œ}i)ÕÛÉso  ©o23ÓdŒÁòßÓ[=íŽY5r´½Âµ“—“¶p+¡ËŸb|X‹6r^™Û]_`?¾†úƒR´ÍØ`ÅiÚfÆ/‡9Úµí¤fzJè9ü(u{ObÃHP²b·‚´«±o=ŸßGí³ó9#;¶*_XNk½ô–ª³ñ¸Z|`øê›`Tbñß­ôd¹…åÛ*yµØBµÕ@Ò%Ñ,¹c"o, bŠZX#¡±&6ÞGz´qXþ‡“‹¹o£ CúR~þï7’¡|–ùdÆ’‘–²:¯KvŒ:·”îx·’í«³÷³zê› Ä]2›V\ÁO× .Ÿ hÓ×ïhÐAØL?zmÖôûãnK |-…ò‹}gűxE*“£ùzF6zí;`ú™÷éž{jœÓ˜~ô£ŸO+gv¬'mÛJZÜÖÜX ´‘£zÖóÖiõ‘îØ[;ŽýÓahâBá£t7)<Úq ‰0µ—{Á9óˆ0XiØ[L; –!éiw$È*qDû4ѸÿuåÀÚ º‹ðU“-EOpþóÝëû! Z~ÄR.‡š•E0ª-* æS^¤ö8ù ­ÍùÄîvÕ·ßA0VZÿáH }Šž. ³Ôýÿ+?êÝ<á¡o‚Q@d“ÂŽg>–Ò&é›à¿©î_¶ð‹Õän«¤ØòÆ Ñ,ñºôß@¤$Ü7e™1ó–µlPäÁÉ=0¸w|ëà/†§o£ã×_L¿~*á@kéN·|=ù{+Ô¢*Â1»m4¼$.ˆÅL Ç ½Ï\¬-Òo]ä÷|,ýqöjÞ[ó¶®û†¯íMœÈÜyj©¾¤¥zÈŠ+£E_£Ea³~ÈŒ%1èpé4P›éAŸ}1+·W‘2'^ºæO”ñçÇΧ†2¢Æ·zûº¡yÞçiî)3¨9­N4ë+F>?bÃ¥Oé§(˜q*ÄdüUw]A8œ=:äŒÓYÞD/QÄ,}‘à89»}\ÑKgX+r½†ìÿ !ÉHgéë´^Ãù£RÂÝñ9ÊuªzÚ ² ähŸžŠC^¹él7ò~"W­$ÖÜ-ÿˆèxs¶ãüWH˜4Ñ\ìú#?Ûo+‚f8c3±35D~d16ýWÄädD7ÖRWr9]í´oa×bÈXGÔýO1>Ɉ½ú"Bª„®ê&Î$éžuÇ«9÷ƒ¾ F!c/ÿ=>ÒÈ ¥$ÎÕå8WË—_ »$žß^kd†"ÔdÙœH~>Ð¥YQai÷ùœ{|Në7}Á¨bÌe?˜!gˆôΜû6±`Üò Þ”wíºŸiö¨vóæþÌëÚý _nå™ÜÙ+`á«WëaÞn¯þÀ=OÚfÆçfl-q% XDÄêÕĘºiÙ·†ó_Èô´;dAŠö¹ÿ1âÍMÔoû¬?úÚ5ä’œéAÐÒ–³^ñ’K§ïÐØù!yf”Gïb/`-šÏùÏå¯íBÇÑ-ÔìvýèèiWWß’73~©M¤ÄÞXLå˪öh`ß\ä³f!ÕPÞg ×&@kÉ Î]s&åæ³"+‚ª½y¼|@!$·¡”¤ó™¿¥íxµk7)²×^ÏCžºkêX·Ç=×Ë”ôq<97ÜÃò¢.޼SG·eú¾ˆ2ñÊQ„©ËºZØþG êô¡Éf^º)¢ʾeN‹áÉ™¼F|ŒáÌJ~©H®ðôŠd&ÕÔ‘»§ïrOhÑA3² p+ŸCg[òw‡Îºú6€ñ ¶M}ô¢UvÐ5~µê “„ì<˜§.vÒ箾µ·Oî³4lÐ÷«âÈ]‘„ÔK ŸxÉb¼{2÷Ì âÄöRøÚYœÊâÍŠm«tr|Ûq9úýo¶Fö=^I…#V¼™=™ŠÚj]í]„Ñq¬ÔmÙŽ}A­‹w;€vY=:Œ*›-Nõ™›¦Uٮ٨2X4U=Óèª*ç‚f·2Í6Óƒû:XÌOo²o¦åS¸k†ÇV¨ùè0»ßv}ÏÞ2Īr¶ªúì :8Ñ8~õÛw«6ÜÂD€ Åäoú‹Z@ß¼OÇÜSלÖÉë+F-žžû`v»‡§0.Àó"nØl\8÷5þ?p§@Ï.Z_~Žš «sÉX¯Ýеt§»c$,˜;³ÆÊùÝ]å=»hÙ]B'F"æ=äV®©Ý‘"‹{´W§hw€ôÚ»é¨.¦zÛƒ}Þ<ôîßȹÒZºezíÝtT¢êý|íQ€C¶®”š7ïíóƒ3˜vµÒk嶺ñ$u…ûüð‚¾§šèÒ)î«o‚‘Ù²ŽÕwÒÝåtþº:©.©éãô8Yv‚}T[ÂÞIuIo ÄéÐdeë¡fêÛÝönêOÕóüŸú:}Ú+-<¨™úvïÚ¥GÏóêñ6Úì²ßßnÇrö<ØgÔÖó´èà/üÝ·Ñ‹küx¿þ v÷&v•TÓá˜.Øl\(?È›oñ²M2Øÿþ"¯ìý†çACÄ—õo´½´W5y|ÈšÍ ¾2oŸæÂ³Ô4Û\ËÙmíÔ”T¸?,~YϾƒçilWÜl4ž>Í®|Õv…}oU+®.ê•ñÎïjñtÑ£/h·h—գè´Ùcý²ŽµítÙ\÷G»­š’oû8}Ðc3=豯ƒ+yïày}Ü×­¯œdï±Ú%6,Uì.¬ózÍëBƒN4Ž_ýöÕº=º ­ó¾~çžÂú ‚Ñ…¦ˆ@ £¯Jï\—Ä’%qXá½?‚©ÑHÐw$蠇Ѧ¯@ C„¦ˆ@ ‚‹™´k¢µ5òuáèpŒ}G‚zmú @0Tˆˆ@àwž^‘Ì$u¡GZ9C¸fÙ=Û.ðšºx£Ç£­oz¸˜í0Rú&"~@ DÄ@ @ ÁEŠˆø@ @ ¸H?@ @ )Âñ#@ @p‘"?@ @ )Âñ#@ @p‘"?@ @ )1qãÿºP \¼Lºs þ4‡kR;8øU¥ºZ  !Q÷^ÊÒÉLÖÆÑ¿u««‡…‘ ƒàâGŒ3@ F."âç"ÅSHÚšýÄÌR׌f½òýë6tø^Hcæ%Q„¤]Áuµ@ ãÝ“Y¹%“¹×¹Ê¢È`å–éd/VJŽ><õm d¤‡41†tu¥¢îNfñóY¬Ü2ÝùqÚóª8r·L'÷ ÕQ¾Ñ«Ã@J› ‡§s1צ–q–­×+·LgåšHµˆo§jî‡';xEG»@ ŒF4;~¦äæ³>o‘óûÙÄú5KÝdÔLÉÍgý†M¬ß°‰UsÕµ: ¸cΙ·Ÿ´5ûI[³‡Ä%¿RKéGO»ß»l1k2ªÏƒ›ÕÂÆSÈ$¹]Ž‘¨û1ÝSÈD§¾ûI~ð„_~ƒZRc// öþ=Îÿ’WHlöýj±>ô«¯ftÚ7R²C²,3)¯¸œþõÕÃÐõÍA9Å'šè8ñÕÕ£˜ù3bye™™eêŠad$èpÙy<¶î>¿;õ²¦E@sþ¦®ý eߎ—µÐ~º2u¥ŠÈå—°hN4æ@Í?͚УÃ@J›ùƒÄ{/aÉóÉ^ß×¹Žq¦‡ïË@ ŒD4Ï.ÇE@ëù[¦`àÂ9•” CúReE¨‹È ‚—>Ƅ̌G™‘”yLòô@®=íŽÙa m3q™&u©‹°c ‹‹bÉ\}‰mÕ¬žÏ‰Á²Ó €°èHnºÍLŽJ 0<Œ§’,;}ÆͲyF•¤?Hã¶»r˜–ìpú{ V®dæ ÆñÀˆ¼=ž8Ú)ûÐó|Gô¼©d;œ>¡düxSU·³ÙÓ¹Îùà` 4:†ëî»”ÙªœV!Y“¹~¢dCd,×­LV´ô=hâxf_å’M_s979Ô‚ˆNMeÑš8bC\rjúï#bÈ~~:W'Ëïc M˜ÀÂ{};ÿ<"'kv$ºM˜˜æžüö¿âÔG¹1ááKe§O¹›ÓG?FÒîÎp·›—ó¦¤_›ÍŠcñ#éd9AÄMMq;gLXœÉÍ}t˜Â¬AÉêCë9žuwN§®ñ»"™õ8Óaÿß±$f_ÊÍYfW©!²™?˜úÄåܤÔ0h˜¶ög=íê9zîQzî}@  žéœñòÚ<ò7¤¨Ú›GþÚ<òÿü5ÊǯÖ#üåÏCôÀ%M’ÚäefA·²¤I+n#6Ú‘&ÊåÈhþOVb7$²\Jl¸õbL`-þ5­årt…žvG‚¬‚àÌÕ®¤Ê›ã-§Œ‘Ðh¶ê"*³”ò¿æì +½˜0Ï\ás8Ƭ%œÝ¹MÙ@_ê×P»£˜¶NáÓn#åç›I¹}6&{%5o>êæÀsÃ=ÄÅéµ”Rýš¼Œm㣜)®U¶¨`áÊÉ”ñ™xM¶¯«µLºÑ%•øp&‘ÐÕX;ß‘ò~¬+åoU]@( Ü#5ÌÑa´+å¿Z¤æèjå­’v sª$g\œÊì‰éÿoPµ™Ä5·{ÏÁÔß hb‰´rÜ‘‡çÙÓÔØÀrø G.“Ø.åæKB=F [¡¡Ú«N³ëqïçMM6›½8 3Ð^uÚy>þ{Ã7|rÂJßWC„GJ:8ÆÎ¾í@(és\Èšeß>íìÿ'U>QØdëêÃn‘+´žãî Ç?úÆÕÞºRþ^k‡Àh.ËVê°ƒÇo æè ß6úÆ™6ŒwOæš4׳ïEÙ«óæg¾Bž$|ÙAO»zÏ…Ö{”Þ{Ÿ@ CA?Ž™´q˜há‚ü‹nšG-4¨ž§Üù/L oáèÛ;89T) ‚%ˆ½Œ9dâ/&1ÅÄXº±–îäÜ©¾Q½Ÿ/§®¢›€øyÄæ¼(EOT@ý~Åò&=íŽY/‚M„g.ôšº³t Õ¯ÿ'ö¶:èù˜îÂCXÀñ™NÉ1¦Œ†ê‡ÆJÚÛºéU5™ŒT.$ºÐŒÆRË™~N×9ÙØSB¯µÿ~I1]¹šÄl×nrèÒwð:x¶ï.ÚŠ+±cÄ4Óá$z‰Ô¥·—d’.*»²ý]X__Nëvµí¢­Tv>)ü(Úûæl•{Øðâ_9Ó tžcßQ)™»)Q.ælvÀÂM ®Éõ'e­<øqß5=üמ& ÊìT(|1/|ÓŒO¹_»ZÙ³ó<•Òˆo·vRÒ òå¼óâïxëÓcλõ[þ*Ÿ åØr’I µs¦ÄwdIWU9o<ÕDƒìg?V*­Í3E¹b#ÒSƒÀÖÈ¡ õT”Ê+Žëºø¦ ”í@Â8÷˜Úë9ðJ=6ù{ã9vÿ©‡æF÷k9%ÝŒÁÖÈ¡MM4œU´ûÜ9j€¸I^BÖ4ö ›…Ož=É!Gž2 'jÆx>>ù²žê‡æªr—#aõa¶þïzõQÄÞ{)9S½;}BWU9Û \KX<77ú³Ù¼$ÒBÁ^{šíçùè9ÛAÙsUýR}€´ôj{Å9v*^ª¥Šï{ÞôÈêBã9.~ê4‡ÞVD•ÔuqT¶™Û¥©Ãþ¿~³Ù“•´ðÙsÕTue$èèPÎ<<Ðô´«û\h¼Gé¾÷ @0øtüLrlÇ~×!‚iÿ&mÍþËÙq@Wþû&ÖoÈçÇ©`˜¶ŒÉŠ µä/¼U6T^ Sr0&/dBf@WEå·Pÿî ìy¹E׎­œ·Bxæ‚í•T¾¾É]@O»#A€MœW&~æq* ‹±Ú‰ ûn§Þ]§Š–é‘£Udü†„L¥¯»¢¡|!ïfm¤ëÄNÎüæ9j*¬ôEŒÛ®^“1V m:ÙEëoå¾m|”н%´Ù!dÚJ¢§Ëk té«Wíöíý|9ÕE'iïÄé³[*iØW‚E—ƒä_µÊµõ{Úšý¤ÍS9PtõÍÿÔ–öÍÿ3œ¼z°…¶ P®¸i";–ÅóÌœ0r¢ÔRú‰Œ æ×·'ðÆŠdv8>¾rô4µóšjÅÀk{*ÉÝv×Ü‹ýBpê¹ç‘|žØ Ý×oØÔo>15+C{ÇÞU׸ÓTÖ¿#"<¨m¦ÜÃjŒŠ:;LœbiDëéóÔ(d[k='=‹£¹q³b¹Ô–é¬Üœ*-¥ UßW%´öÚ&Êθ•fëêÓö…uÀ„IÜœнöô9}ÐxÞ”ôg3ã„`‚€†RÏáž°Ö©»ÍXÚçRGzdu¡ñ\efÞú þU9Î<äÒc_¿Ùlˆ ‰Ú[9¦ó§°?;èiWï¹ÐzÒ{ï`(ðéøÑÄŒdB€ð¬•®‡“ ›X!oé>ñféûc¹ÖRø¢¾ ‡ÉÖXÂÙ—–R½ý?å’hrÏ‘`šŒ1XþÛàa¢¤§Ý‘ 뉮/°_CýA)rdl°þ§bÃ%ÉUË›ÒÖì'YÞFEORÂék" LvòÍ p~–´;…ÓQµ‹îFÀ0…ñ9r"鸇_õã“4ÄZd16ýWÄädD7ÖÒè;H4Ø€°k1d¬#ê~©]{õ!÷$×òë`›# (h36;Ó}©—¾¾]äD…ñÌ-&–ÅŽa¼¢øX½MÚ…ËÃóS}«áŠF²¼¼`7ÇJÛ±7Ù)‘sü\Ÿnâ™é}'ýA‹z66¬ ®uÙµKY5[ýØâÂbí¢ÈX8´Þ€©©AØO×kZ"¡…ò³RÜÙyQÄNó8Å1í‰pI(tm¢?÷–'Ξm‡À8æ­‰bÂ4ï‰p• u߆…3Í|ðl95¶P2î¹”YC{ºûE“ÍZ¨B/™Ì˜ˆt¼ˆ"ýá‰Ló°œ% Äuág™Y¸|f ¾ô‚›:e­6 Œ´l&)yP À޵±«œ)vqÙ³<4®Ãþ¿n6›fbîOûÚ¬©®€Ø¬h¢äî\¶&“S½äzògë¤ûì¤ùg@¦‰Ùë/çú‰Þã«´ØAO»9Zð×½O _Œ¹ì3\Ùí¼0ç¾M,w„ü‚74~¼æ~¦]ØãÚÍË“róY‘AÕÞ<^> ®ÕHÀ"ÂW¯&Öó™½ú÷ü=i›Ÿ+íŠTþÛG¥²€ED¬^MŒ©›–}k\[dëiw$ÈÎz¥o^ºi)z‚óŸ»’Wr K„Ôôi׎ã[ŠæsþsEÅôI¾yŠ—Ðv•}“73~©í¢”i;UKÀädº•m{ítÝBÍnßίú‰}íë¹]°7Sùò·²±óÿBòÌ(^Ö^ÀªÖY…×¾éaîÏ|æ…i-yÁé”õzÍÊm(eýF”‰WîˆB«@W ÛÿhAÂ!4ÙÌK7EôYÂÕv¼Ú¹«Væ´žœéÁkÄwÀΨ䗊$O¯HfRM¹{´%”Ö¢ƒ²óx`¶÷í¼ûœ#Àpõ}¬½}rŸ¥aƒ9oÆ»'sÏœ Nl/åÀßÔµ.¢È`ÑÔ`j>:Ìî·‹SYy£™Öc¥®]”&E’ýH‰jEløäÙ3Rž•«âÈ]‘ŽcÕmÉß•ÿsvÁt2"ÕJ¨uÓÚ7€ì-ÓI¬*gkyud¼áfý¶¯¶Ò’£Ü{’·YøäÙÓ®Ü4:tÐuÞtÚ,dq*‹o4+¶í•ž?ÔL}»÷Å¥GÏóêñ6Ú첿ÛnÇrö<Øg¡M-<´è ‡ÚÝ›ØURM‡ãvk³q¡ü o¾}ÄÃVØö¿¿È+{¿¡ÁyÐàÉšÍ ¾¨ôr¦™Ý¿+£¤¶Ýyïƒ.OŸfWþà|­9ÌßN·Ñnëÿ<ø¥oÃÉçõ¼÷‘…®@3×?2‰ôIj¡GÍ:Þ>Í;…g©i¶¹Î³­š’ 7g‡'ì¶vjJÊ<;rTô+ûa%ï'Uçyö³÷þwÿþ{ño`†a†a†a†IßQÿ3 Ã0 Ã0 Ã0 ÃŒ40 Ã0 Ã0 Ã0 “68ÐÀŒµ?Ç‹¿ù9îPE†a†a†aæÚç†E­>'ÍäÿϧðÝ¿ƒü/%I¿Fþ¦ Èõ áÒ'ÿ¢dcÌü‡ðWÿ±G·+qø«Ÿ`ùâ˜ú§ÏÐy…dsðÃGð‹‹m¾×8‡ÎÿÕ2s“û¾r?ž°žc•gÒ†µxâ{gq¨­ Oüæ^Lùðt©} qË÷qçŸ Ÿ76½€‡êëpç¿‹ouõQ€âGSqà§•À bO@Išã†a†a†a˜Ì“zFÃ_lB^1<ò3%¸ŽÈ¯Ç£õç8ùß>B·œÅ‡Ûþ+þÛ3¶.ý{<úÃ9ÀÉ߆erû ¸t^<Õ¿ø@ÉÿŸ3ņ<£,C]eGvt¨r |k_x©"Ðwš×Y‹£Ý! ö0 Ã0 Ã0 Ã0£NÊ«NP6ܼÏÐþ_Ì@ÃO0ý¿þST±ûŸÐþAæ<6_ ¯2§Ø#ÅÁoÃùÎM®òáy¢ÎbYŒp…tô ÓÄÍݧâRL&9ÕÿwÂ'òy 1„þ-wââ賋=÷72@2ç±EF¶ãc@Žÿ¶mkQÊj¸ÿçÿ‹)n`ƒ²VÁ{ô¿bçI%’|ðOÎs,Çã!üÕüÿþÛÎO]Zývþò}ßø»þ§ŒÊX…íxI:öËðÄo*8áÅ‚Êi¢| _l~ïtr¿L Ì‚ùmX»é lz ä­=ƒ½¶`á8ð­ÆóWÁx]-¢žªà´G ;Þ&7twÓ¹ÞPr <]Ÿã o•°MÙE6ßs‹<ÚÝV†a†a†a&4 4|áS”mÚéAerÚ?Eþƒr§à×ȶe3(§Ÿ¡ýg·9õròNIYgÛr~øŽVþÕßѹÿ„ ØûUÛËF=á@0Yèróïòc– ƒæøiùêƒ8eé¿Çtõ9ûÿ¨xâ{X¢Ê©3·Í:?±fÜŸpô×¾°k÷°à'«•Üd3–á‰Çç¢s3CõŸQr§=ºãIîÅ’I9P×dØé)ŽlþC%Uðz_ yñ%îöºÏ0 Ã0 Ã0 ä†k áÆg~€Êøp@:â“1“¦ á›NËþ+Ÿá¼é£ÿÅà™¢FaÊ¿ýr@ü›'?N¹ÔÐóUÛ?Þyí?32n|f?ÊdPÄ’ ¡³çÁÿ ³r~hï«÷‡ÇGuH„O±Ó ;ðWܦäÄü°ºÝ'U¦‚Äåø“¿•Ã(þJ þ æþˆ‹êèXœiúmÃùøáë3”$Ejë± ð1ÞQ÷ÛàLdØÂÁ6t{¼ÆçtP[â.§> N{tÇ“· î7ëñ"máL`èD‹‘©0ô9ö”" Ø OW[$ƒA]O^É0 Ã0 Ã0 “:®†›¦yðÕ¥SÒ¡ïlËÞÐý¿Õƒ_Ã+ô¯:ÿ·ïýV²Xм/þf-ô)á[  }ÐeJ¿?ž@*y1Kž¿ OX2’¢;€¡’ •Ià¨ÇÍÝñ”‘Pr»ýzba«‡Úãvxކa†a†a˜ädÆÜžÎ—í Íë`™˜1<¤9ñ"qÚÈ °MÈH(¹‚†C„‡5X'ƒlsêUs1È O£¿-¾Sj^{(Xòà;(ûa© dýîC>ÂP A®ñ[É4¯Â°Í[yñøoÛÎÛä¿xÛ>‚Ûh.†ß½ˆO¿oÚOðd Ã0 Ã0 Ã0#%¹@ƒtÒ‘ØpBhòÅð|„NÎŒ-49¢pÄ_/à ƛ= Ã0 Ã0 Ã0L\R^Þ2!8ÐÀ0 Ã0 Ã0 Ã0×£h`†a†a†aæºÂu2H†a†a†a†a˜Tà@Ã0 Ã0 Ã0 Ã0iƒ Ã0 Ã0 Ã0 ä 4ŒZúñç¸C•˜ -‰9Z÷êþÙ2U`†a†a†¹ö¹aFAÑ_«ÏvÈAú?ˆ;X'·²«¿Çggh9Ö÷bʇŸ K˜ ©Ô‘½éf.lú)&ø;¼+Û&ŒÇëuc4í¤ö}ÕÓ3ø]|Ö¡“ǃl}?–Ït5ftÄÉ ßò}ÜùçÀŸFt šv8ó ¾ý‹§ñ“?ûµ”a†a†aæÚÅ=£Á·Ïßì}a Öªíƒj¦´a5Z¸mF¼ÓLÏ_ º•Ä@'MiÃíÈ9ñºz¦_Ä;jÇÁWEùU|¤Šéæ£M-V®Æƒ>%`†a†a†¹†q_Þ’ {qÄá|Ýñ³õ¨+Q“®¬ÝtÀ±ï ö†Ï¥Þç'±À# ŽÞK;nÁ¿©€_£öãs¬mÞnä@¯÷Œ8wµð;XPy‹õw$iO‡ºæ*bëñ:¨¡ûw‡µ´a-Vy@É-ðt}Ž/¼UBª‹2Dî1l´Õo“_›_Ä—Ëõí쎲ťôוL;,ÃMEÄõH»Rµ“p׫¿_e ˜Ï‹ÜÒQ~Ò4³ÚâlM{¦x¿ä³íF ’5V×Ê0 Ã0 Ã0 3±q4ÂŽ_”㘀sGÎØü6yÕ³8 œ´0‘:@zÏ9%Üô*G1`ž¯±- {äyt|M@øCþà%Ë1aÇqs‹…³Üó:5«ñÎï:ì›pýG ‡Ö¬ˆ@;‡qo‡7^hÓ^WríÐ#äõ2€Ž—×éý8v›kp×ëÀr¿ t:ÓmÜ—iª¤°=×ÎztíÙ’úýry¦8ÐÀ0 Ã0 Ã0̵ˆv2È6C&ö¢/þf=ž¨U;4ÓDÇÉ-Üã» å%gpÄÕ©¼uâØÄ‚ ±¸„/>0Ï? œAÃiKÊžÚ +{ìÇ'ÏЉÃiú{Ížób/<]mgò`º=^Ü|˜†ÇoßøD·ƒþºRi‡Èñ§{Sk@ÿ<¸ß¯ôpzNjƳÜtï1žëøÏœK{Žä~u0¤>švqa†a†a˜k‹¸«NȀÞ3(®Y­$.øVcU%ðÅfåĉããs_œ¸x‹T9¤bõpÓ±æ+›!˜ÿ|ÃÉyÀÁA²×5íÒó36¤|¿(ØèQ†a†a†a˜k—„–·,-ôªO&^ÌŠšØ.€/e:ù\ãxž£a†a†a˜k ídL"ƒÿ$¼‡xùOF8âN€É0 Ã0 Ã0 sÐÐ FGÞinîáôwFCíÏå„§d`†a†aæz3†a†a†a†IœÑÀ0 Ã0 Ã0 Ã0LÚà@Ã0 Ã0 Ã0 Ã0iƒ Ã0 Ã0 Ã0 ä 40#‡–»üÍÄŸs]£¨Ï(óbGýͪÀ¸ò€mô¡LS&]õ0 Ã0 Ã0 3.ˆ1ä2<ÑT„7š·ËåùÊOª%}«ñüãUðÐ!CŸc­ØŸ%/£hu%²èóð t¼ò )Ö¢;>-ò•ð<óò³IhpéÀ]¸pÔø|ã}ï¡d¾±3tr#zÞß•d=¹Õ‰¹¿ Í*á”ǫljîø5˜ÑTŽ 6]Ök'àüöc¨KuP á`ï ¯â#%JrôOn àMUÎ$M«Js¼ ¿lWÁ#õ%X<‡Gÿ«’¤‚ø;úMü)´MîÓ叨ñÚ×X!ñSOuÂbÞ¨aÕ Ü ´ºØµbãBÊO!ø·ú‘ÓÃþõÄ"a;“$“mí†óºÒeO¼z´íI÷§l[š/*Á¦zæ.+1>ÇýþèŽO¶†a†a˜ëŒ¸ ¥˜‹YÞKtSI8MÏEçæ5XûÂl혋¶L—ÂÑ]ýgÚ~—p ïBWןaîÃkÔ>7tǧKNSmÈi³¦ Ëe!ézva蕈¬çb%Šî[)&n¼ïaä_ìÂ%U6‰–Ç®'š$/yóÃÇwDþÌëŠÁÁWÅó0² ÃX²dÉLT ÷Ù‚ Ä›-]8_>M¹JaW„p,ÍNt"$¬wg'¶¸8®ä°f·ûŽ‹ÍCŸ©:4õÄc¬Úg´÷÷}¢BÁañse~¿Ñ÷vìïÏ•ðÜf¿硾?“®‡a†aæúÃ5£¡´a-+§©R„µ{ÎàÅùmX»é€,S¦C]É™ä{²©7hžo­—Ŭ‡÷¡hv—pl¹ËrýèFïºp|I®;>òæÿÏ3ï;{ï׸÷üëì×ÖcÅÈ2¸ùS„™^ 6û‘mÕ¥“‡q«GeX€l‰u¼q]_ŸÌE¾Ì^03H¾PÙt]¹U‡+sñ`Ó“X Ó[¬Ï‘ Taìô»„/6¿ˆw:åÎ((s ÞèþŽÐÛ‡†–¯Ñ´j&pöT•K%¸ì72 ìç ¡Å’ AÙ UÆááã‘›m÷çbª”::öõ¡yP ,Pâ1\ˆ›Õ`ü-¨‚j ØdŠ®–ðßO,ÜzµƒmY(¯ ;ld H^fäÈc€ Ì  s§…€âxºÏÁŸ7åSŒ}í‹ ÐÐ8ÛÈHRÇ[|§Þš å@gHè5t µ”ûj6,u’$ºŽ²¦…˜×~»w*ÀÚ›.ÏÅ9ly®_[ÏŠ »‹©d¹^…[ï{¤®ˆºzÜäýu ±T–-twÈž|];еFα_ƒ›=H¢ýíu+”=ÚzlÏCüë2‰jO[ý‚zc>o.ϧõzÝé}4Ÿãw,ù^r|oËïÌû€•u¼ú=*À[èé»7f= Ã0 Ã0Œ&£áôޱvóçÂñNÑ -è–ŽÓ”z18#Ž s=>û’çÆ™¹]ü“øDÎ/9³'Œ^û£"Ò‹¾9²wýQíñé’d#õ>ÌmÛ3/¢’Y˜4€G8ÜRÞô<ÂqLº 9ñ$ ù?P™âüû*ñÍGÅ+²œp«GÈV{Ñ£zØd@%¬Ûíx¢D8 o©ãE›ßGǯDžæøú6ÃþìSf‡Ž¼ÓLÙ-ôŒ8ðTÁxÝÈ|9,X®Ï|¡Ì†­]8-Æg 2ÜŒªœ+†Llç˧ÉyÌsäv¨Wó)P¢ìì¹ð>3@°îþ›Ðj=~U6–ÐŽÜl”á2Z]‚ Äáóß`êœÉƱ1øh“‘å#·=@ÝÏ–)YäïGîK È@”û€³G#N4ƒòi=2K`W›Ø_7ÝpúäÊÛ~`©p¼p¼Žm=‡¡âÙÈ9zþ+Y(X$³Æ©8ær¼I´Þ,¡wÐ8^lÃEr.…CÏå^ã 9«m4œÚÂ;ÊÏ5 ;-ó\> œÖçúeÙ­ɔ٘sÉÈŠ_¯§TïœN3‹âxÄa¶Ô³eå©ëu‘·7›¶Cl|Ž8ãîí`ž#7jOu½:{’iÿXöèê©©Îζ’«l’Ø×eàlÏy8kÖ³?¨¤z½nÏ› jižOé¸1™>K}wôf`6VbrI6¾éS߇)×Ã0 Ã0 s} :QìdPA0°ôðÜ"{±½‡Öà¥=J– &œt£‡½çý/•LpôQô`¹pt©gÝÚ+¯9>-òè!r(D‘YÙ•¸Y8Üv‡œH¢ 9ñJN×'äÆÐ«óo “D׃êrLC‰ ÈÍ;,q9^Ò%ìV/ÍGý¸”-î· œ<O×FqüÀ²}#H >ƒ#;:ä§Ó½ùj|Ï-Ð/U&eìh,1¶Úp¿+æ¡Õ™}PæE)<¨w9>.WqY}Œõ†¾ø›õÆvÏ-Jš"ø„3ÞcëÁ—½ÁÊ9ìSá'ßTxº#=Ä;;Ñ;Eål´õò+çpDeòùPˆ,¥mážf…«Þü{#Néî=Òƒ¯ù¥Ã×Ú ôî7ňCkè] {Oºž`8?|½&.vÎ+ŽoÇ"WícL:©“ëpo3¸âlOW{„ÝÉ·¿ 1êéÈByãB¬x@ Á©—êïv±#†^·çM÷|ÆjçtÞG0·f'Ðo }'«ïÉì‹'lܨã-d=l ÔÆ©‡a†a†Ñ([œ%Oå“ÂqªG±§J®*@£§²^Œ‰!‹Äkgò|Û7ˆ¬ùË¥“._Ü(sÀإƥsÃê³þøtÉ„N©q=„†OÇÊ!Oº$ïxëO²‡ ³)¨B/¬Ëe°Àì9sÊéóŒjã|Y¿Ù³vîLo.i¼¶ã­ýàú^äã„ @¬Ç…í'„ç~¯qÌx"7•Ÿ¿kf( ©1 ¡fFmoã°Ú“¼›0uøjìc}«±ªøBÍ]BCŒF‚ì•¶8µi…Rà)`n*³€U½Â!ô· w,oä+y¤l碘ӜÚÐÉã!λ»‘ KÏ¿–tµ¿¦3{áT™ H$à½e÷1ôVä;òBŸY­Ahwäиé'"C%©ÔÃ0 Ã0 s=áh tø­'.¡{ršäŠWƒm2 ÜOAÁË«àéjsíŠ 9ìè ;ð2õôœ_~¦q°E8„ o–C´Ç§Knc%<·•C#º¾Ä7ÙfØ@Pæ9äÉÖã k^‰ÓkÍ~  ªÓ™aÊå ­ÒgBõ„ºD;‘=³k"vjïàÆ…f¼(S`Ef6((£ƒ®7cLBQÂ/~ƒ5Ü¡ia$C!8äÁÒ%7¨’¢=€Ó…9î“:£S±T£wIþ$\~«J±àK9Å\;ˆ‘[gïaWDËa&‰B¿²¥¦:vuµ‡zÜSj‡=±êQPÀaW[Ù3­6¸\—›^ÇsµÂÌ\H@¯ Íói¶³‘ Rn«/÷ÑÈ2Ò-»»3h> ÿ ¸/(È !º†a†a&Æò–á%-ñs<_Ø‚—T¼±”¡r¤F²¼¥Ûò`RF³y«!²¬&‡,r9žp«‡HJNóPæ€Ax KB7ÉbRõĨ? ã6é£U£«=cߟ%dµ}Œ 8Í~A·I%­ÐÒõó°™Èg¢Ç¾¤#=3–IDuÐpˆgË•“a Òe¢ÆÈd_ãsÿ7Æ<j^‡u%(•Ÿ,“A–ymC&ÂrÔ›´Ì a]ãLßuŸ(ÒJd2ÈKøâD ¼‘ë5&™S­g2H£îœH‘°-Mø@d¹ArÖVÊ " c²=99Mìwt:~ÿÜE”o˜ üÖ/‡OØSÞ#“ºé•“ Òy8²|¥âŠ1¹#áV—Õ~Ú¿´Ø°už¦Ýõêì$¬6…'´Öo±Q''lmª&AÔµƒq-ô)$36ä<jhˆ›=òZ’hÂÍ]=öûb´±éx»Õ£Ó¹.QÍ­`.oé¢÷÷˜«}ÞüÕÑϧÝʱ·kºîcôäŽÆÐ0c¹ßè¥{£Ž·}÷›Ðw"Mú«¯‡a†a†‰h`˜ë Z©"çx—m‰K f, F£Î¢é¨í°8Ka¬ô&K vÚtò1ï{\&Ä}d†a†aÂè'ƒd˜ëˆæ·»€Z¯\ÑBRæE=ú2d >ë§o¬ô&ËD±3Yø¾3 Ã0 Ã0לÑÀ0 Ã0 Ã0 Ã0LÚàŒ†a†a†a†aÒ†a†a†a†Ih`†a†a†a&mp aÔ ¥k¸Ó2ÚuÝ“ ]õ¸Auÿl™*$ÈhÚ3Ö5Z&w$h‰K§,Y¨ŽúØëü_÷Ј}(SÅ”IW= Ã0 Ã0 Ãd”á(7­–ŸîøÙzµ1mpà­Á…ƒ¯b/êñ|Ã\£œaÖ5–`‡Üf¢)W S€–«ÄÁÞTåTˆ Tí´`&¶-¹A RÅ%• ¹O—£ái#ØAËûeÊ·êM•â1¹•£fÑèØŸ;ÝÈd[»á¼®tÙ¯m{R ¨iº*LtÖ`FÓ>ÌmzñÕa°žgHÙfT«]ZÜê11ëÛ¦Ê Ã0 Ã0×.q3J…Ó;Ë{ nQð­Æó¿©€ÿ…P‘q§´a5ZðÆA%0NüÚ >tàæ5âXM;'\Oj|´©ÁÊÕ‰’ÒdÏ’%3‘ï?‡†­]bëCó Ú‘$TOÕp~é\\¿= êYðx³¥ çËgŒ(2W„p쵯U)s$¬wg'¶<Õ gó“ÚÝvRì;.6?}¦vèÐÔ±jŸÑfÜß÷‰JÉË(j*Çpó¸¤D†q~û]èh6¶ G•ؘõ7Þ÷0ò/v¹îc†a†¹Öp]Þ²´a-+§©R„î=k”óL½±·#°ùE¼Ó)w1a(a5ð;kÛìI,ðÐç3ØvÊ)k¤@ÈÞ¯KøÂÖ¦ÔÎØ1O¡ =^yŠõ›\ÐÕ‚µ›¨‚ñ ¬Âv¼´£CIÜHõºÜy¤¾óÛ»ìªPkZ9„3P “ šVÍöÙ”áP%·»nU6‚ù¨*¤Ò×øüÝ>ôT— ^–-ôö¡¡%âlQ0ã1\À£‡ÿUIÜ¡l ºpϦÑF°ÉŽö×AÎúÝèÀåøQot°- åY¢‚«rà©·ùÎy D«pØèÜi! 8žîsðçÍFùc_û¢44ÎVσ’ÉÏN½5ÊÎÐkèj;)÷ÕlX(ê$IteM 1¯ý8vïTÙJ'ÏÅ9ly®_[ÏŠ »‹©d¹^…ÓN"RWÄN]=nòþº…X*˺;°¥ù¢¶èZ#çØ¯ÁÍ$ÑþöºÊm=¶ç!þu™Dµ§­~A ½1Ÿ7—çÓz½nŒô>šÏ‰ñû°|_™P6B ¾Þþc uQ™2Þ7Ëv¯G@AˆÕ^ 6û‘ÝTŽ Íª Ã0 Ã0×&® §w¼ˆµ›?/IÂù‘½êä ™A&&¾Åð‰s»S#CÁS_àu±o ¶ž,5?B²õ‡ÿq/ŽÉ×îêäp’ÏEçfS~FVaåtož¹‹UIGz®‹œv.AÎ}i­:Aó äfc[-Ð"3Ä&ž¿úUÙZùªLì+Ãe´:²!šß6Ž=­Êa<¹( Y着o– Ʊ”P:,Aâðùo0uÎdCg >Ú¤ÚØlÿŸ-S²Èߕܗ@(÷gZmÉAù´™%°«M쯛n8}wB8o”9 ¶ýÀRáxáxÛzCų‘sô$üW²P@C§â˜Ëñ&Ñz³„ÞAãx± WÉôûCÏå^ã 9«4\‚œÚÂ;ÕÐ GÊ=9ÏåÂi}®_–Ýê‘L™9—Œ¬ˆðõZpÚIõÎé4³(ŽGfK=[ö‡Pþº^y{³i 9ÄÆçˆ3îÞæ9r£öT׫³'™öe®žšê,ál+¹Ê&‰}]Îö\ј‡³f=ûƒJª×ëö¼É –æùÔ‘Žû˜ÙÈ_­†N<ó²’%ËJxî«Ä7EHI†a†a®uôC'н@@9 CG “>ÎàˆÊ ç>u\ê©­@1nAœëAl÷Ü‘w};à; Üì‘øu>Ü'ùáUž>hqìónÂÔÞ+‘¡íœöܤ•§6^}­*+‚ 3p—ÕÇXP¯§1׆¥ýSåŸpÆ{l=ø²7X9‡ƒ}ÊñM…§{0ÒC¼³½S¦ÊCm=†üÊ9Q™‡|>"KÍùÂ=Í W½!ø÷FœÒÝ1z¤_óK‡¯µèݯœË°Ckè] {Oºž`8?|½&.vÎ+ŽoÇ"WícÁ+9¸5Î ¹ÕyövE´Üf’!ô+[jª#=ü®öP{Jíï°'V= 8ìj !{¦Õ—ërÓëx®V˜™ 赡y>Ív6²AŒUILÒy,£dç+á¹­¡‹RåDë±gEtȉ"»ägÎp`†aæZF;t¢Ø™ 2âp §T¦×£Ó°àqñY-ɘtàÓçœÖvSCb¶›îødë9€7öŒû$Ï[o,[Ù¹GºÌ!ðGõ´/C]¥8ì“XAÉÚ“$ƒÃØâŸ„zš³AlÏ–ƒ–·‡µr3» õ¬1ׂsÙÌRxŒóÌ9bð>ÍÙp¿qžœ3ÂÂ}åÂiûcì‰ íí,Ú¥ÃÚΰ—æ®0ï¹¼¨†²ºÙ>š`/ñgýø}[V8•}eE­qz˜wÓ8öF•ú.6s9äô Ìå+éô hxEëÀlq®±Ôb²õè줹†+æG]—9dƒd+}QC9¢åÀšÂl£XË:Šö?ÖmÖC“EF2tö¤Òþnöèê‰,+j<Öanõ¸êµ]—§,™:½nÔ ùç3÷1sIJÊ6Ps2ÈùL9mOÁÓµ=ïï2NqEWÃ0 Ã0Ìõ‡ëªÌH1Vcðš˜hÒJ ‹¯ÇYqb|C«Läw¬`‘&huŒ¥ÁsqWœH‹ Ðð°#Óéèc¥7YR°ÓºÚ…|Làû— q†a†a®Cô“A2#€VchóZÔþuh™ÐA‚V™@­7<¼"m”yQ¾ÌˆÏúÇÆé+½É2QìL¾ï Ã0 Ã0Ì…3†a†a†a†IœÑÀ0 Ã0 Ã0 Ã0LÚà@Ã0 Ã0 Ã0 Ã0iƒ Ã0 Ã0 Ã0 ä 4Œ´ô£c2ÈÚŸ#ùõÛ]HW=nPÝq–ZŒb4í‰E™;GaÂÇt“!;×9uŒUû¤C/ÕáXR”qð€ær #"]õ0 Ã0 Ã0Œ"F A8ÊM«å'Zîð‰ZùQ9•jíµŸqBË[Ö{^ÅGJ’t¾jãL8ðÖàÂÁW±õx¾a®QžÈ$ᬮk,Î1m3Ñ”«„cDTÀ h9O àMUN…Tô¦W½í´`&¶-¹A RÅ%à— ¹O—£áiãù¡å3åˆ[õ¦ÊŠ ñ˜ÜÊQ³htìO‡nd²­Ýp^Wºì‰W¶=)Ô4]&8ÕÛ0·iŸ±=󲮄ç%³ÉõÜxß{‘ã›ÞƒG|ý1 Ã0 3¾ˆ›ÑP*œÞYÞKtSI8À5ÀÞÖ`í ¯ã T]iš)mX¼qP L„¿ö…D‚´<&µq d³;I¸žÔøhS ‚•«ñ O â1ÊöhÎhÃÖ‘9×Ä’%3‘ï?'êê[šÕŽiZåV¤ÁÎGêK´N7Ù_5܇_¶+IšÚ'iÒ¤÷Í–.œ/Ÿ1fŸÅ!{íkUÊ ëÝÙ‰-OuÂyÛÉaÍn;)ö›‡>S;thê‰ÇXµÏh3îïû„e%<·‰_·æ»Ä¶çQ‰¢ûV ù. ½B2cë¹hÊ5”¼Œ‚ùƒáã; "ÿkÔN†a†aÆ ®Ë[–6¬Ecå4UŠÐ½gÍy¦ãVa;^ÚÑ¡$Œ‘°øÝ‹x§S‰¤ìI,ðÐç3ØvÊ)k¤@È™Køb³õ<ê­€?|| õøVãùÇ« O±žc“ ºZ°vÓUHôÞ¦z]vÈQ~6'ˆ†–ÈK6õrŸ$g•²jM+‡Ð¢XêÁ¯’âˆL’›m÷çbª*¢·ÏV¯äÄÏoï²;ê½Z¹‚2#(hñèá•eëVe#8œ‹ªB*}ÏßíCOu êeÙ‚Å~ª«ìl¤n l`Ÿ=@’N½Én½ôl<† Q×섲®ê½šÆ³›LáxÎu³~7:°C9~ÔlËByE–(…àߪxêm¾3GÑ*ö:wZ(Χûüy³Q>Ũ׾¨ ³Õß’ÉÏN½5ÊÎÐkèj;)÷ÕlX(ê$IteM 1¯ý8vïTÙJ'ÏÅ9ly®_[ÏŠ »‹©d¹^…ÓN"RWÄN]=nòþº…X*˺;°¥ù¢¶èZ#çØ¯ÁÍ$ÑþöºÊm=¶ç!þu™Dµ§­~A ½1Ÿ7—çÓz½nŒô>šÏ‰ñ°ü.Ø¡¬„¼…ž÷w) AÙ OáæOïÂ…£†$ºž5˜Ñ´8`“õð>ä^Ü訇a†a˜±Æ5£áôޱvó碗rÙ«N/îö 9™·Í†`/løÃ'^íNuŒ O|מ5ØzX°<ÖüÉÖ#þǽ8"3P͍“Ã]H>›MùY…•Ó½xæ.V%鹮ÇûpºpJ$}^8ó¥½Wð& j!VÊ4›xþê…úDÒü¶!;mœfÝýSÑþ®yü’ºCN, — g·´V  ¡½:9ÙCP»lN±ÎNxrQ4²(þÞTUß,{ðcÉIW:,N7ÕÕš3Û>DØT†Ëhuda¤So2¤[ïáóß`êœÉá6ÖñÑ&õ,›ÏùÏ–)YäûKîK È@”û€³G­¶ä |ZÌØÕ&ö×M7œ¾;!œ7ÊÛ~`©p¼p¼Žm=‡¡âÙÈ9zþ+Y( ! SqÌåx“h½YBï q¼Ø†+Šdúý¡çŒr¯q„œU.ANmájè„#åžœçòá´>×/ËnõH¦ÌÆœKFVDøz-8í¤zçtšYÇ#³¥ž-ûC(H]¯‹¼½Ù´…bãsÄwoó¹Q{ªëÕÙ“LûDzGWOMu–p¶•\e“ľ.g{®hÌÃY³žýA%Õëu{ÞdPKó|êHÇ}LŒ•˜\’oúÌàhÄSÈ¿øA8ÈàÎz\hÞˆ¯o3†NdŸº‹ƒ Ã0 3Ñ(ö倢z$îøÙ“îØ$9ƒ#*k€œûÔq©§¶Ÿuæœ÷Ü‘w}¬Í0t„û7’»®“½ÌW˜)óàt»xÁÍ» S)à`ˆeJþiÏMúqÎ2@Lxè8ȹm^ÖéƒGW£W'?LˆÆ™¾ëÈŠˆÉZU@‚œéD!çœæ.Øa p$GjzGNŠz®â²ú êõ Ïc>ç©ò€O8ã=¶|Ù¬œÃÁ¾ü¾©ðtFzˆwv¢wŠ‘K3ÔÖcȯœÃ•YpÈçC!r°Ôœ?!ÜÓ¬pÕ‚oÄ)Ý£Gzð5¿tøZ»Þý†£qh ½KaïI× §ñ‡¯×ÄÅÎyÅ‘ãíXäª}Œ¿_\‡{;˜Ág{ºÚ#ìN¾ý]ˆQOÿ@ÊbÅJN½T·‹1ôº=oºç3V;§ó>Ê M6CÖÃ΀ÔÐ ,Ç܇×(¹[=FÖƒ§k£8þ`Ù¾ØC-†a†\ r"Bñ²î©|R¼¸×£ØSe›”P¦){?O¸wCdVŠêÍ¥­y»Ú‡b/<U}Þ<:ˆü2£§~~ö Þ×ySãG‡ñèÖ>äÜ_‚u±½¥CÃ<êч†·‡qXÉ®i(¸3|5öµúVcU%ðEŒ d½Ò§6­P <ÌMe£ªW8„þ¶hO—É“$e; [}´¡“ÇCœww"–ž-éjM=fö©2#HÀ!z3Êï# u(š~o­W;¡S]ÀôYªäBõ½ÈÇ •۶Ÿ?÷û†a†7¸(~ë‰KrNùÒ.U£Gze!Q‡õz£ó:iîŸ*%ÛÐ]r{´-”­PR¡GËð„Kpi¡CfFK&Îz{öÿ¢¦³½ÅErˆ(’Ñ›"ý¯ù±«3ÅIŸ‰ÆÎà•ÜgŠÜê<{»"Zn 3IŒú•-5Õ‘~W{¨Ç=¥öwØ«vµ…=ÓjƒËu¹éu:lϳxþ:¬Ïóì¥9BÌ¿sW eu³1|4Á^âÏúñû¶¬p*ûÊŠZãô0ï¦qì*õ]lær†Ié˜ËW†Sé ÐðŠÖÙâ\c©ÅdëÑÙyè9š7a~Ôu™C6H¶Ò75”#Z¡9!Ì6е¬£hÿcÝf=4Yd$£AgO*íïf®žÈ²¢Æó`†àV«^ÛuùpÊ’©¡ÓëF ’>Óy£«EdÙ•–¥)·‰æü ÆwbÇ®_ ÿdn¤ñ'+pÁ0 Ã0ÌØàºê3RŒÕ¼‡œhN hhÌâÀ뼚È‚²rŽ'3GDÐr’Í‘/ý™ \¡ Žx+N¤EhxØ‘étô±Ò›,)Øi]íŠN>&ð}Ë„¸ Ã0 ÃŒ+ô“A2#€VchÙµ?GZ8È0Á ÕPë¬Ü‘&ÈÙß1A𨓿¢ÈXø¬lœ¾±Ò›,ÅÎdáûÎ0 Ã0 “v8£a†a†a†a˜´Á Ã0 Ã0 Ã0 ä 40 Ã0 Ã0 Ã0 “68ÐÀ0 Ã0 Ã0 Ã0LÚà@èAK?:&ƒ¬ý9Ò²hºêqƒê޳`£iO,ʼ e,Ó=bÚ+;3¤wSÇ5~½Q¤C/ÕáXÚ“qð€沜#"]õ0 Ã0 Ã0Zb„£Ü´Z~¢åŸ¨¥O´l£Zž6µŸqBíTìy)IrXÛ9¼5¸pðUìE=žo˜k”'2I8oëK„³HÛL4å*áå¸gˆTôÒ²š8À›ªœ ézÓ«ÞöZ0Û–Ü ©âàLܧËÑð´ñ÷BËfÊ·êM•â1¹•£fÑèØŸ;ÝÈd[»á¼®tÙ¯m{R ¨iº*Lpª·anÓ>c{æe%\ Ï3Jf“ë¹ñ¾÷"Ç7½øÚe†a˜ñNÜŒ†RáôÎò^B ›J´lã¬}ÁØöª® ‡4Í”6¬Æ‚@ Þ8¨&‰_ûB"Á³[ ›ÝIÂõ¤ÆG›Z¬\}JQ¶G‹pζŽÌÉ%–,™‰|ÿ9QW—ØF¾ŒcÓ*G°"Mv&MôÒò–:ç—Ú­j¸¿t.¢?¯7%Ò¤÷Í–.œ/Ÿ1f®Å!{íkUÊ ëÝÙ‰-OuÂù¸‘ÚÝvRì;.6?}¦vèÐÔ±jŸÑfÜß÷ ËJxn¿æÍw‰m#ΣE÷­ò]z…dÆÖsÑ”k(yóÃÇwDþÖ¨ Ã0 3~q]Þ²´a-+§©R„î=k,Î3õº? ï!«Œ1Úe5ð»ñN§©¶Zà¡Ïg°7ì”SÖH*,=—ðÅfëyÔ;Yøøêñ­ÆóWAžb=Ç&tµ`í¦ª`<«°/íèP7R½.;ä°>›DCK䥓z}O’óFY µ¦•ChQõ¤WIqD&ÉÍÆ¶ûs1UÑÛg«× r¦ç·wÙf^­\A™´xôð¿Ê²ÎÎu«²ÎEU!•¾Æçïö¡§ºõ²l!ûÝH§^ª«ìläšL( ‚}öÀ̵p½Én½ô·ð.DµµÊ2« ÷j{°ÉŽ¿kä¬ßìPŽõFÛ²P^‘%J!ø·*žz›ïÌ‘ÇA´ ‡}€ÎŠsàé>Þl”O1öµ/*@Cãlõ=£dò³So͆r 3$ô:†ÚNÊ}5Š:I]GYÓBÌk?ŽÝ;•@@öŸRÇÉsq[žë×Ö³bC‚Âîb*Y®Wá´“ˆÔ±SW›¼¿n!–ʲ…îli¾¨mºÖÈ9ökp³I´¿½n…²G[íyˆ]&Qíi«_CoÌçÍåù´^¯#½æsb¼7,¿ƒv(+¡o¡çý]JBPvÃS¸ùÓ»pá¨!‰®g f4-Çd=¼¹7:êa†a˜ñÇ 3 ŠþZ}l;ˆ¸-ÿþv];Ê~8 ÿ煿ûgh/9¿OãÇ?¼ 3ûZ°áOËs…¯÷~/€ÍÿôÏJ@pòãßãÀ‡ßж,À…?A—”û°¨þž~ûßßF猥øÑ÷nÀ?˜m*öÛŽO¶žx¾ÇŽû‡ÅyW¿ÇZ†ÏòJônþ^ýG’w柷èÏ@ÞÜS9Ck½'©^—îî˸­vfá „3ÿø¤+øÕÀdl»çìÛÚ‡_¿ˆÃ“ñÂ]7¢§íü¶M”…ì¶['ã¼yž`ÝO½èy·Ï¦ã¿ƒó®bg»»ÓFN݆º<”‰·Øœ[¦ãÁ[ÅVÂNÞ½“´öPæ p° ÏœˆÄî>ÖØywõ, çý?ÿÏ z&gãÇþ-~Õ2¨Žý›:4¶Ç#z©®œÿPŒÊDÛ˜òÜl<:÷*Þýƒqí&×Âõ&CºõvgMÂO¿ûð3¥£ëôw§6ú»þÑTüæ7Ôߢñ}-ÿæ5sNnmÈÇ¥=_¢»×(Ï[áƒïë.li:Ó¥ù¨ǾšŽ†ÿ4 GŸòcßî/ql²+p ªÄõÝ:€¼ÌÆw>:‰³3ó1uà4à_ÇÛ{ÙP"pê-©+Fùÿí‘z‰sJÿRüMíâó½â|Q.\áÅ€(Sl‹œÕÕYŒÒé€ÇWˆ[WˆmÁW8öñ×Â~ã¸<á<ÿÅWÂiýµ ëv©‡˜÷ãy˜qö$þÇ_Ÿ\¯¨ÇÄi'9åßí3ާúÚ>5žc“=Xþ#ãzÝäû~ý'eËd3ÛHéÔµCûdžýr£ö¼ïßä9:{V¬M¼ýUÝnöèê©y´Þúg¼¿…ν·g{®X[‚þ­ªžÉ“qkžqÝôꞷϾãú|Z¯×I:î£YÎ÷–âÖY!ü)ü;he%¦ÖÕàßj•³T¦àÁß`Ö·aêùpn÷ay]Ïa\ùx7Üÿ æüðaüÛgw¡ïC¿ÜÃ0 Ã0ãýЉb¯ð#ed X"ôð†9tõÉOÈ88ƒ#*kàto@þŸ.õÔV · ÎœSãž["ò®µ’î†ÔÇÔHîºNöz0_ è}Dxþ§ÛÅ‹iÞM˜Ú{%ÜKL)ê§=7éÇý–yQÚLxèÃáÃ} á-âe÷ôA6!6ê]ÖèÕÉSEãLßudEÄd­ª×úðùoäÿ™!5½”ÖOsìX•%J–ëzGNŠz®BïE ^Ïð\9æßuª<àCù@­_ö«èÁ¾ü¾©ðtFzˆwv¢wŠ‘;4ÔÖcȯœÃ•YpÈçC!r°Ôœ?!ÜÓ¬pÕ‚o¤ç{wŒéÁ×ür¸Dk7лŸ†Mˆ-Üknè] {Oºž`8?|½&.vÎ+ŽoÇ"Wíc|_éä:ÜÛ‚+Æ\öötµGØ|û»£žþ,”7.ÄŠ” œz©þn;bèu{ÞtÏg¬vNç}<½ãEí¬‡ŸBþÅÂY Àz\0‡N`9æ>¼FÉÝê1²<]ÅñËöÅjÁ0 Ã0ã×@ƒœˆP¼¼z*Ÿ/²õ(öT‰ÿ£'ûèäÀ[¤J̸„R§U`HnÍÛÕŽ8{á ô¨ÂèóæÑAä—ƒÍÏÄû:ïbòÈà0ÝÚ‡œûK°.¶÷0a¡á%õèCÃÛÈô½1i‡‚YÃWc·±o5VU_lVÓ{T@8Ejª³lNmZ¡x9w‚ÚžëW;FY¯pým!Ñž#Ÿ¼1e; [}´¡“ÇCœwwàߪÚrPíˆAºÚ_SO{³Q>Uf 8¤CoFá}¤¡EÓO ã­õJb'tª ˜>K•\¨¾ù8¡†J¬Ç…í'Ä佯>†a†Ǹh"­'.É9äK¬tV£#õwÌ¿CGT‰‘tA'Í]èDŠ£ÉÁ6t—Üm e+”T¨ÀÑ2<áÒ#ZZèřђ „³Þž=”y‘ö+ÃÑ¢ÞÝB!“CJ‡®j{8ǯ Ï¥$½ñìùåVñÂX«Ÿ<1q&¡ÈeB@Ò7º«b¸ë¥y–ÏÙç0 û…©Xš[Æ×õŽ•^bIþ$\~«J±àK™‘$¾kjœ¿^ÌJôûG8Ks0¿W›è¼Œ¡âÜp.õ®^Ñç_Ôtv¢·¸H®E2zS¤ÿ5?vuæá± J’;ƒWrpkœ(r«óì=ìŠhy ÜÚÈ•ú•-5Õ‘~W{¨Ç=¥öwØ«vµ…=ÓjƒËu¹éuœ²djèôºQƒäŸÏtÞÇ(äjÙ@v¥eiÊmbÍÏ`–˜Ø±ëè?™©CüIÇ \0 Ã0ÌxÁuÕ f¤«1LÔ9hF{"CäŠSÑþîÈ—àL”ís<™¹)’`¬®wŒôRP‡2Gâ­8‘6 á!`G¦ÓÑÇJo²¤`§uµ +:ù˜À÷=.â>2 Ã0Ì8G?$3:ðNs pOô¼ãžÚŸ£-dgºcœˆæ·i¸ˆ7<œ$]ŒÕõŽY;—yå 2ŸõÓ7Vz“e¢Ø™,|߆a†ÉœÑÀ0 Ã0 Ã0 Ã0LÚàŒ†a†a†a†aÒ†a†a†a†Ih`†a†a†a&mp aÔ ¥“AÖþÎu¶S"]õ¸AuÇY/ŠÑ´'e^Ð2–鞈0팕׸ÞuNÜΙ!z©ÇÒžŒƒ|0—å骇a†a& b„£Ü´Z~¢åŸ¨•´|#­Ó>WUÈÔ>õÀžWñ‘’$‡Ù¾jckpáà«Ø‹z<ß0×(Od’pfÖ5–牶™hÊUÂ1"ÊÎI/-«‰ƒ¼©Ê©Àíœ\õ¶Ђ™Ø¶ä%H—€n‚ä>]ކ§ïZ¶0SޏUoª¬Ø¸É­5‹FÇþtØéF&ÛÚ çu¥ËžxõhÛ“AMÓUa‚S½ s›öÛ3/+áJxžQ2›\Ï÷½9¾é=xÄ×=Ã0 3ñˆ›ÑP*œÞYÞKt+ ´a5ÎÀ"b,íÓ‚7*‰pâ×¾Hð–Ç\#Žmqoã„ëI6µ X¹ú” £lá¬4l™³I,Y2ùþs¢®.±|YæUŽ`EšìLš ¬—–™Ô9¡t¿ª†ûðKçböÜΙ!MzßléÂùòcØ[\±׾V¥Ì‘°ÞØòT'œ99¬Ùm'žãbóãÐgj‡M=ñ«ömÆý}Ÿ°¬„ç6ñöÒ|—Ø6â<*QtßJ!ß…¡WHfl=M¹†’—Q00||ÇAäÿ`ÚÉ0 ÃL$\—·,mX‹ÆÊiª¡{ϼѽÏ?îÅ‘ÚPþ› øÇÂÁ×P6Âjàw/âN%’²'±ÀCŸÏ`o¸Í(k¤@Ȉý%|±ÙzõÖYÛ8…z|t¿ª O±žc“ ºZ°vÓU0žUØŽ—vt(‰©^—rŸÍ ¢¡%òF½ 'É™¡¬„ZÓÊ!´(‡z´«¤8"“äfcÛý¹˜ªŠèí³Õë9µóۻ쎫F¯V® Ì Z@çN Å9ðtŸƒ?o6ʧûÚ ¡q¶ú^U2ùÙÀ©·fC9Ðz Cm'徚 E$‰®£¬i!æµÇîJ ûO©ãä¹8‡-ÏõkëY±¡Áaw1•,׫pÚIDêŠØ©«ÇMÞ_·KeÙBw¶4_Ô¶]käû5¸Ùƒ$Úß^·BÙ£­Çö<Ä¿.“¨ö´Õ/ˆ¡7æóæò|Z¯×‘ÞGó91Þ–ß};”•P€·Ðóþ.%!(»á)Üüé]¸pÔD׳3š–Œc²އ܋õ0 Ã0׌†Ó;^ÄÚÍŸcˆ^Úd¯:½Ø­Á…cù“*Spà[ Ÿx¡°;Õ12í¹I?V(‚ } 9?-â-üôA‹#¤Ñ«“¦DãLßudEÄd­* AÎVæ˜XzÉY¥±ü;,äàvÎ )긊Ëêc,¨×Ó˜³Æò=–*ø„3ÞcëÁ—½ÁÊ9ì Éÿá› O÷`¤‡xg'z§¹RCm=†üÊ9Q™‡|>"KÍùÂ=Í W½!ø÷FœÒÝ1z¤_óK‡¯µèÝo8ЇÖлöžt=ÁpøzM\ìœW9ÞŽE®ÚÇø~ÖÉu¸·ƒ\q¶§«=ÂîäÛß…õôd¡¼q!V< ‰àÔKõw»ØC¯Ûó¦{>cµs:ï£ìÒd3d=ì (Pð@ ÀrÌ}82"º#ëÁÓµQÿ°l_ì¡ Ã0̸Å5Ð '"/sžÊ'Å‹]=Š=UB:·Í”Ô«>!WN¬}¢Hf\!³RTomÍÛÕŽ8{á ô¨ÂèóæÑAä—=×ó³ñ¾îm{òÈà0ÝÚ‡œûK°.öÛ4“$4¬¥}hx{‡•Œ¹† àÝðÕØ÷Ö·«*/bd`%ƒì•¶8µi…Rà)`n*³€U½Â!ô·…D{ºL6˜$)Û¹¨Ùê£ <â¼»+É °ôükIWûkê1³N•Á€DéЛQFxi¨CÑôèxk½’Ø ê¦ÏR%ªïE>N¨,†õ¸°ý„x)¸×ØÇ0 ÃL(\ ”¿õÄ%9'ƒ|©Îj$MÞܬC*ŒóAçtÒÜ>UK¶¡»äöh[([¡¤BÍÔ¾ O¸ô–z1df´dᬷgOÁ#e^äŸýÊp<¨·Ó2¤‚†:”]Õöø9_žK!I4zãÙóË­âªV?‰aâLB‘ËyÆPÑ\c|é¥ù–R:¾5ÝžžLÅÒ´ØÂíLŒ•^bIþ$\~«J±àK™%¾[kœßW^ÌJôûV8Ks0¿W›è¼Œ¡âÜp.õ®^Ñç_Ôtv¢·¸H®E2zS¤ÿ5?vuæá±8éó1ÑØ¼’ƒ[ã¬@‘[gïaWDËa&‰B¿²¥¦:ÒÃïjõ¸§Ôþ{bÕ£ €Ã®¶²gZmp¹.7½Žçj…™¹€^šçÓlg#ÄX•Ä$÷ÑÈ2²¯øBó2È Ã+¿P’h²æ• ÔuH•\êé ”íUA‘YÃ#Éöd†aÆ íЉbod2ÈŒ:œž|Úᜓ@8óŽ,ÕÒ¡îèŽO¶žxcO §sŒM.[Ù¹GºÌ!ðGõ.C]¥8ì“XAÉÚ›Ö³“P_; í4R°É©Üâ2š;AlÏ–ƒÕ³m.GY ±ŸRëÅñ­½ªL“IÆ™£A‹Fo,{L(ØÐ>gFØisµÓØ¥å}ã~¿qžu~#%ýfääåXLt½”Éà6$ÑzÖ˜À ·óÄÓKÜW.œ6óï]‡íûJ|¿tX¿¯`/Íc~Ç™Ëôj(«›á£ öÖß·e…SÙWV„Ч‡y7coT©ïb3—3LJ¯À\¾2œJŸ`ð€†W´ÌçK-&[ÎÎCÏѼ ó£®Ë²A²•¾¨¡ÑràÍ a¶Q¬eEûë6ë¡É"# :{Ri7{tõD–5žë0·z\õڮˇS–L ^7jüó™Îû…\-"È®´,M¹Mì0çg0¶¸;výý's#uˆ?éX †afüâºê3RŒÕ¼‡&f¶Íð¾8ðzœ'˜ŒCsAÈÉ.G¾gRŒ•ÞP¶CÎñdæÄH‚ë­ÇH/“(c%ÞŠicQvd:}¬ô&K vZW»°¢“ |ßã2!î#Ã0 3áÐOÉŒfÒÜcO+œÔþuhá Ã8ƒœ²cä Ž…ÞxÐê¨õFV*I×[;Ùý-óʹ72d >ë§o¬ô&ËD±3Yø¾3 Ã0̘À Ã0 Ã0 Ã0 ä Îh`†a†a†a&mp a†a†a†a˜´Á†a†a†a†aÒF ZúÑ1díÏá\w:%ÒUTwœ%â¢M{bQæ-c™î ÓÎXÙÉzG…uNÜΙa"ë¥:K{2ðÁ\–sD¤«†a†aFDŒ@ƒp”›VËO´Üáµô‰–mTë•«Í3v¨ê=¯â#%Ik;gÀ·¾Š½¨Çó sòD&‰—ûu´Æ¿Üf¢)W Lj(G6C°ÞøÐ²š8À›ªœ ÜΙa\ém 3±mÉ J*.ìÉ}º O߇´la¦q«ÞTY±q!“[9jŽýé°ÓL¶µÎëJ—=ñêѶ'‚š¦«Â§zæ6í3¶g^V•ð<£d6¹žï{/r|Ó{ðˆŸ†a˜k¸ ¥Âé彄@·à¾Ø¼k_0¶7*1¦´a5Z¢ÛF8ñk_H$ø@ËcRû¶ ÜìV®'5>ÚÔ‚`åj<èS‚xŒ²=ZÄË{ÃÖ‘9}Ä’%3‘ï?'êêÛÈ—ùkZåV¤ÉΤa½ICË=êœAzNª†ûðKç¢òÜΙa‚ë}³¥ çËgŒY sqEÇ^ûZ•2GÂzwvbËSpþy‘ÚÝvRì;.6?}¦vèÐÔ±jŸÑfÜß÷ ËJxnokÍw‰m#ΣE÷­ò]z…dÆÖsÑ”k(yóÃÇwDþÖ¨ Ã0×å-KÖ¢±rš*EXûÂëx²~÷"ÞéTBÆe#8ÛˆdOb‡>ŸÁÞ°SNY#Eª°@F°)ˆc=z¯*àŸB=¾Õxþñ*ÈS¬çØä‚®¬Ýt@Œg`¶ã¥JâFª×e‡¸gs‚hh‰¼”P¯àIz¹§¬„ZÓÊ!´¨~êY®’âˆL’›m÷çbª*¢·ÏV¯ä\Îoï²;½Z¹‚2#(hñèá•eëVe#8œ‹ªB*}ÏßíCOu êeÙBö»Áz RÑKu•ÜC aŸ=ÅílÀzÓKßuáBԳ儲뽚Æw+l2…ã{[9ëw£;”ãG½ÑÁ¶,”Wd‰Rþ­Ê§Þæ;sä1@­Âa s§…€âxºÏÁŸ7åSŒ}í‹ ÐÐ8[ýŽ(™ülàÔ[³¡è ½†Ž¡¶“r_͆…¢N’D×QÖ´óÚc÷N%ý§Ôqò\œÃ–çúµõ¬ØP€à€°»˜J–ëU8í$"uEìÔÕã&ï¯[ˆ¥²l¡»[š/jÛ®5rŽýÜìAío¯[¡ìÑÖc{â_—IT{ÚêÄÐóysy>­×ëÆHï£ùœïÅË{ŽÊJ(À[èy—’”Ýðnþô.\8jH¢ëYƒMËÆ1YïCîÅŽz†a&&7Ì((úkõ9L°í tà¶ücøÛuí(ûá$üŸþ]ð¢âŽÛ±`qîü¡Ø¾_€ÿ³:‹‘øêqï÷ØüOÖv àäǿǿmY€ ~"Ú’ðaQý(<ý:þö¿¿ÎKñ£ïÝ€8-÷Êý¶ã“­Ç‡'ž/ÁqqïþáCqÞÕïãñŸ–‰{6CÈ+Ñ»ùWxõIþ]ܙޢW<y pOåŒ8÷7Õë²ÓÝ}·ÕNÃìã!œ pæŸt¿˜Œm÷Ü€}[ûð«ã±sx2^¸ëFô´}ƒß¶‰²Ývëdœ7Ϭû©=ïöàÙÃtüwð`ÞUìlw©§—þ uy(o99·Lǃ·Š­0„½;{'ií¡Ì 2à`ž9‰Ý}¬±óîêY(ÎìþŸAôLÎÆÿü[üªePû6uhlë5HE/Õ•óŠñB™xLyn6{ïþÁ¸×&Üά71½ÝY“ðÓï~'ü¡£ëô½ª6úÞþÑTüæ7Ôw­ñ{,¿Ó5ß©NnmÈÇ¥=_¢»×(Ï[áƒïë.li:Ó¥ù¨ǾšŽ†ÿ4 GŸòcßî/ql²+p ªÄõÝ:€¼ÌÆw>:‰³3ó1uà4à_ÇÛ{ÙP"pê-©+Fùÿí‘z‰sJÿR|gîâó½â|Q.\áÅ€(S,œÕÕYŒÒé€ÇWˆ[WˆmÁW8öñ×Â~ã¸<á<ÿÅWÂiýµýëv©‡˜÷ãy˜qö$þÇ_Ÿ\¯¨ÇÄi'9åßí3ާúÚ>5žc“=Xþ#ãzÝäû~ý'eËd3ÛHéÔµCûdžýr£ö¼ïßä9:{V¬M¼ýUÝnöèê©y´Þúg¼¿…ν·g{®X[‚þ­ªžÉ“qkžqÝôꞷϾãú|Z¯×I:î£YÎ÷–âÖY!ü)üžce%¦ÖÕàßj•³T¦àÁß`Ö·aêùpn÷ay]Ïa\ùx7Üÿ æüðaüÛgw¡ïC¿ÜÃ0 3ÑÑ(ö ?òŒñy( "¯fJ¿±í T%?q ãà Ž¨¬Ó½ùj¸ÔS[bÜ‚:sN{n‰È»>Ž•ÒÀú˜É]×É^櫟Ïÿt»xqÉ» S{¯„{)…ù´ç&ý¸Ð2/J{ƒ }8|¸4\¢E¼ >HÃ&ÄF½½:ùaÊ¢hœ‰à»Ž¬ˆ˜ ¡Uõj>ÿü?3°ÞD 4wS¿cU6–(Yrp;g† ¦wà*ônQêõ Ï…d~o§Ê>”ôØzðeo°êì Éÿá› O÷`¤‡xg'z§¹aCm=†üÊ9Q™‡|>"KÍùÂ=Í W½!ø÷Fz¾wÇè‘|Í/‡K´v½ûiØ„ØÂ½æ†Þ¥âÄÚ“®'Nã_¯‰‹óŠ#ÇÛ±ÈUû¿G:¹÷v àŠ1…½=]ív'ßþ.Ĩ§ å ±â%H§^ª¿ÛÅŽzÝž7Ýó«ÓyOïxQ;D4ëá§ñƒpÖ°Ì¡X޹G†BD×cd=xº6Šã?–í‹=Ô‚afáhŠ—Oå“âE§Åž*ñôTTfüB©µ*0$·æíjGнðzTaôyóè òËŒ‰£ægâ}ÝÛç8ä‘Áa<ºµ9÷—`]ì·Kf‚@Ãiêч†·‡é‹b˜BÁÊ᫱Ÿ)ßj¬ªDd.¤=#û­©Î²9µi…RàåÜ j{®_íe½Â!ô·…D{Ž|òÆ”í\T€lõцNqÞÝ€«jËýAµ#éjM=íÍFùT™ H$à½e„÷‘†:M?Ž·Ö+‰Ð©.`ú,Ur¡ú^äã„*±¶Ÿ/A÷û†a&8®ÊZØzâº÷¨—é¬:#¹sñ`Í-2³ƒÎ#褶It"ÅÑä`ºKn¶…²J*Tàhžpé1+-ôföÞ g½={ )ó"ÿìWÆ‹8õþ ™<ÀêP:tUÛæ<~]x.…$ÑègÏ/·ŠŠZýd‚‰3 E.Æ‘¾Ñ]ƒõ4.iðœ}L?=Ÿ˜Š¥i±…Û™¸ÞôKò'árð[UŠE_ÊŒ3ãwÖŽ³ý}ÎÒ ÄïÕ&:/c¨87܃K½ë…Wôù5è-.’«@D‘ŒÞéÍ]yxlC’¤€ÆÎà•ÜgŠÜê<{»"Zž…·6r%„~eKMu¤‡ßÕêqO©ýöĪGA‡]m!dÏ´Úàr]nzÏÕ 3s!½64ϧÙÎF6ˆ±*‰I:edïp£ydá•_(I4YóJê:¤J.õôÊöª‚ È‹¬á‘d·2 ÃŒ´C'н‘É #'MN¨Ò9ó$|¯Ç™,ðz¤ŸŠ&Y°Ü:¤Äl·úÈPµt¨;ºã“­çÞØÀ‚Çéc“ËVvnÇ‘.sHEüQ=fËPW)û$Þ½M֞شž„úÚIhÿ£‘’LÎÝ¿5KO>[þ ZT³¹e)<Æ~JqÇ·öª2M&y0ÅÁ½±ì1¡`CûœÈÌò®v»´¼ïªî7γ.Ïi¤hߌœ<£ Ö›š^Êdp›’h=+ޝ¶¿´r;³^':½Ä}åÂi3¿ßtؾŸÅ÷i‡õûùöž¿/æwzœ¡‹eu³1|4Á^âÏúñû¶¬p*ûÊŠZãô0ïÞ’©õ2õ]lær†Ié˜ËW†Sé ÐðŠÖÙâ\c©ÅdëÑÙyè¹ã®˜u]æ ’­ô D 刖GÚi£XË:Šö?ÖmÖC“EF2tö¤Òþnöèê‰,+j<Öanõ¸êµ]—§,™:½nÔ ùç3÷1 ¹ZD6]iYšr›ØAó3˜å&vìúúOæFêÒ± Ã0 ×U'˜‘b¬Æà=41—ÿ¤Ï8ˆ4î+jLEû»#_‚3)®7½1 l‡œãÉÌÅ‘|3Ãé¥ eÊÄ[q"m,*@ÃCÀŽL§£•ÞdIÁNëjVtò1ï{\&Ä}d†¹ÐOÉŒš4³¸'z^‹qOíÏQ‡2Œ3ÈIÙ1FÎÑõ¤7ÍoÓðoxøLºàû›Æì¹*óÊ9?2d >ë§o¬ô&ËD±3Yø¾3 Ã0ãÎh`†a†a†a&mpFÃ0 Ã0 Ã0 Ã0iƒ Ã0 Ã0 Ã0 ä 40 Ã0 Ã0 Ã0 “68Ð0jÐÒŽÉ kç:Ì)‘®zÜ ºã,™ÅhÚ‹2/hËtOÌ—vÆÊNÖ›2¤wS·sf`½ÉCu8–öd<àƒ¹,çˆHW= Ã0 “fb„£Ü´Z~¢åŸ¨•%¥ k5¼Åö|Ã\%e"Ðò–õÀžWñ‘’$¯ÖIÏ„o .|{QmÜ×$^vÍõòw4ÎDS®ŽQe†`½™!½´¬&ð¦*§·sf`½‚öZ0Û–Ü ©â°OܧËÑð´ñýOËfÊ·êM•â1¹•£fÑèØŸ;ÝÈd[»á¼®tÙ¯m{R ¨iº*Lpª·anÓ>c{æe%\ Ï3Jf“ë¹ñ¾÷"Ç7½øycæÚ$nFC©pzgy/!Ð­Ê kÑèýk_X#7^1šÒ†ÕXhÁ•ÀD8ñk_H$ø@ËcRû¶@5»„ëI6µ X¹ú” £lñ2Û°udαdÉLäûωººÄ6òeïšV9‚i²3iXofHƒ^ZvQç”ÑóY5܇_:wçvÎ ¬7%ÞléÂùòc¸]\±׾V¥Ì‘°ÞØòT'œÖä°f·ûŽ‹ÍCŸ©:4õÄc¬Úg´÷÷}²žÛÄÛió]bÛˆó¨DÑ}+…|†^!™±õ\4åJ^FÁüÁðñ‘ÿƒ5j'Ã0×®Ë[Ê`Bå4UŠ@Žï¿©€,œÊ e#¬~÷"ÞéT"){ <ôù ö†Û²FŠTaŒè^›­çQo޵½S¨Ç·Ï?^yŠõ›\ÐÕ‚µ›¨‚ñ ¬Âö8¤T¯Ë9RÏæÑÐù‘¦^²“ô²KY µ¦•ChQ/ÀÔÃ[%Å™$7ÛîÏÅTUDoŸ­^7ÈÉ›ßÞewä4zµreFPÐÂ\/_gçºUÙ碪J_ãówûÐS]‚zY¶€ýn°^ƒ‰¤—ê*;yvL(p…}ö·³ëßzé»ý1\ˆz¦PÖd]¸WÓø-M¦püNé gýnt`‡rü¨7:Ø–…òŠ,Q Á¿U9ðÔÛ|gŽ<¢U8ìtî´PœO÷9øóf£|б¯}Qg«ßM%“Ÿ œzk6”!¡×Ð1ÔvRî«Ù°PÔI’è:Êšb^ûqìÞ©²ÿ”:Nž‹sØò\¿¶ž vSÉr½ §D¤®ˆºzÜäýu ±T–-tw`KóEm;еFα_ƒ›=H¢ýíu+”=ÚzlÏCüë2‰jO[ý‚zc>o.ϧõzÝé}4ŸÃXÞëìPVBÞBÏû»”„ ì†§pó§wáÂQC]ÏÌhZ0ŽÉzxr/ntÔÃ0̵ 3 ŠþZ}l;ˆ¸-ÿþv];Ê~8 ÿç…¿C—¯?œ·ß{­¬Ã?¬ÆŒŽƒ8T'2Â¯Ç½ß `ó?ý³œüø÷8ðá·¢- páÃOÐ%å>,ª¿…§_Çßþ÷·Ñ9c)~ô½pà§å^¹ßv|²õøðÄó%8.îÝ?|(λú}<þÓ2øx†W¢wó¯ðê?’ü»¸3ÿ¼E¯xòàžÊâXëu8Iõºìtw_ÆmµÓ0ûx'H œùÇ']Á¯&cÛ=7`ßÖ>üêøE잌îº=mßà·m¢,d·Ý:çÍóë~êEÏ»=xö0ÿ<˜w;ÛÝ_ré%xC]Êį~Î-Óñà­b+ a§FïÎÞIZ{(ó„‚ 8Ø…gNDbwkì¼»zŠ…SùŸÿg=“³ñã?ÿ¿jTÇ~‡MÛãÁz &’^ª+ç?ã…2ñ šòÜl<:÷*ÞýƒñŒ™p;°Þñ­·;k~úÝï„¿#utý~GÔF¿S?šŠØô†úm1Þ?äo˜æ7ÄÉ­ ù¸´çKt÷åy+|ð}Ý…-M§qº4µó¯âØWÓÑðŸ&áèS~ìÛý%ŽMöb壓àNA•¸¾£[÷ƒÙøÎG'qvf>¦\À‚Æü‹ãøc{/JN½%uÅ(ÿ¿=Rï1qNé_ŠßˆÝA|¾Wœ/Ê…+¼eŠ!’³ºú/‹Q:ðø që ±-ø Ç>þZØo—'œç¿øJ8­¿6¢ŽÝ.õó~<3ΞÄÿøë³‘ëõ˜8í$§ü»}ÆñT_Û§Æ}´Ösl²Ëd\¯›|߯ÿ¤l™Œcf)ºvhÿذ_nÔž÷ý›,"¢ë9Œ+ã†û_Áœ>Œûì.ô}è—{†¹öÐ(ö ?òŒñy(`D"…Ìã©‚÷¤1lbížüĘÇI•38¢²N÷äÿ©áROmŠq êÔ|/ÞsKDÞõ±6Ã@ÒÀú˜É]×É^æ«Ïÿt»ø!Ï» S{¯„{Õ(¥÷´ç&ý8É2/J{ƒ }8|¸4\¢E¼œ>HÃ&ÄF½q½:ùaÊ¢hœ‰à»Ž¬ˆ˜ ¡Uõò>ÿü?3°ÞÌš^J7§±í;Vec‰’%·sf`½ 1pz·(õzšó>…§RåÊzl=ø²7Xõ@ö…äÿðM…§{0ÒC¼³½SŒ\¸¡¶C~厨̂C> ‘ƒ¥æü ážf…«Þü{#=ß»côH¾æ—Ã%Z»Þý4lBlá^sCïRØ{ÒõÃiüáë5q±s^qäx;¹jã÷W'×áÞ\1械·§«=ÂîäÛß…õôd¡¼q!V< ‰àÔKõw»ØC¯Ûó¦{>cµs:ïãé/j‡Äf=üò/~ÎZÖã‚9tË1÷áÈPˆèzŒ¬O×FqüÀ²}±‡Z0 3¡q 4ȉާòIñÃ_bO•øÿç†ó9ôydîƒmèöx3¿Ú“8”jªæÓ[óvµ#T ô¨ÂèóæÑAä—)ÍÏÄûº·±qÈ#ƒÃxtkrî/ÁºØo[ ÆS>4¼=ŒHŸÃLP(8;|5ö³ì[U•À›Í ÕÁ‘"5ÕY6§6­P ¼œ;AmÏõ«£¬W8„þ¶hÏ‘OÞ˜²‹ ­>ÚÐÉã!λ»ðoUm¹?ÔØtµ¿¦žöf£|ªÌ$pH‡ÞŒ2ÂûHCЦŸ@Ç[ë•ÄNèT0}–*¹P}/òqB •X ÛOˆ—¾{} Ã\s¸h"­'.¡{úÑ—Îê«@g‚ž¹‘I©gÜÌv` : “æ.Ht"ÅÑ„A%·GÛB£’  Z†'\zJ ½23Z2pÖÛ³§à‘2/òÏ~e¼˜RoX¡ÉŒ¡¥CWµ=BÎã×…çRHÞxöür«ø­ÕOê—8“Pä2éÝU1X/1Vzi|üÒà9ûØzú»ÀT,M‹-ÜÎëM7îz‰%ù“p9ø­*Å"€/e†øí¬qþy1+ÑßSá,ÍÁ@ü^m¢ó2†ŠsÃ=¸Ô»^xEŸQÓÙ‰Þâ"¹ DÉèM‘þ×üØÕ™‡Ç6(I hì ^ÉÁ­qV È­Î³÷°+¢åY(pk#WBèW¶ÔTGzø]í¡÷”ÚßaO¬zpØÕBöL« .×å¦×ñ\­03ÐkCó|šíldƒ«’˜¤ó>YFö_h^dxåJMÖ¼„º©’K==„²½ª (ò"kx$Ù¼ ÃŒg´C'н‘É #ç¼±¹¾ÇÍG`o¢=ä× ø´X°\-)¡I©Íê#CÔÒ¡îèŽO¶q¿hx‹y¿Ä&—­ìÜŽ#]æŠ ø£z–¡®RöI¬‰ ‰dí‰MëÙI¨¯„ö?)ºädmñ Y£±ôä³åß EõôšËQ–Âcì§Tsq|k¯*Ód’Sü¡ÑË 6´Ï‰Ì´îj§±KËû~ ê~ã<ëòœFÊòÍÈÉ3ʱ`½K/e2¸MI´žÇWGì#¸Y¯“ñ¦—¸¯\8mæ÷¹Ûï‘øýè°þÀÞâ÷Ôü 3—aÖPV7ÃGì%þ¬¿oË §²¯¬¡5Nóîý!™Z/SßÅf.g˜”^¹|e8•>Áà ¯h˜-Î5–ZL¶‡ž;ŽáŠùQ×eÙ ÙJß@ÔPŽh9p¤ ‘6е¬£hÿcÝf=4Yd$£AgO*íïf®žÈ²¢Æó`†àV«^ÛuùpÊ’©¡ÓëF ’>Óy£«EdÙ•–¥)·‰4?ƒYN`bÇ®_ ÿdn¤ñ'+pÁ0ÌÄÆuÕ f¤«1x­‰^âr@3€/¼ÎK—Ž7äŠSÑþîÈ—àL Ö;æP¶CÎñdæI¾¿™á:ÓKÁ3ÊЉ·âDÚXT€†‡€™NG+½É’‚ÖÕ.¬èäcß÷¸LˆûÈ0Ì5‰~2Hftàæà{ÚÙ„ öç¨C ÆôÒ¾cŒœÖ;ö4¿MÃr¼áa;é‚ïof¸îžç2¯œk$cAâ³þ±qúÆJo²L;“…ï;Ã0̸…3†a†a†a†IœÑÀ0 Ã0 Ã0 Ã0LÚà@Ã0 Ã0 Ã0 Ã0iƒ Ã0 Ã0 Ã0 ä 4Œ´ô£c2Èڟù.qJ¤«7¨î8KˆE1šöÄ¢Ì ZÆ2Ý䥱²“õf†k\ï:§nçÌÀz3C:ôRŽ¥=ø`.Ë9"ÒUÃ0 3êÄ4G¹iµüDË>Q+>H‡R­gmnêÆ -oYìy)IrÐùfgÀ·¾Š½¨Çó sòD&‰—?sýø3Ñ”«„cD”c—!Xof˜HziYM àMUNnçÌÀz3ƒ«ÞöZ0Û–Ü ©âÒA‘ ¹O—£áiã÷Ž–-Ì”#nÕ›*+6.Äcr+G͢ѱ?vº‘ɶvÃy]é²'^=Úö¤@PÓtU˜àToÃܦ}ÆöÌËJ¸žg”Ì&×sã}ïEŽozñ³Ê0× q3J…Ó;Ë{ nQNèÚÖD¶=g€@q ¦´a5ZðÆA%0‘í—Hð–Ǥ6n5{ דmjA°r5ô)Ao.ϧõzÝé}4ŸÃï XÞcíPVBÞBÏû»”„ ì†§pó§wáÂQC]ÏÌhZ0ŽÉzxr/ntÔÃ0×.® 9¢Ë{ðÒ&8œ]¥ÛÏoKèGðº‚Úí'ÀKÍÛ•ÀŠ3p@åzäœx]:ôò Êû±¥MÇ›$ZãÞÑ=« `msߎ€éü»ÝËð±n×á$ÙëŠ&X 9óeWÐpô&l»ÿ&´Zå ¯âï߯a* lçÉòLÅK¡tŽÌz4/ 2ÀQîHý£GÞ†}ˆipаPDÛ H8-ÎcGëe½‰è¥`[=,Nìî¶XþÖ¬LôëMÖËzÒëòÛï¯î7?6Ò±ýmÄi¢¡…ÊÙ#§på´lÙ{³pú¦â˜Õɬ¾Œ]yXY¿k—q«p ‡÷ŸD°z®¬/§1âð›Ç“ÃoâÔ+¾ˆSj 8ËÒ.± l69Ï¡µ:¹Dt½ eV9™á뵜㴓œò[/)gÞ‚µëõêäÆ9v[ˆxí ¡zÊå1z{,ç%Ðþ„›.]=n相ڬˆ~ÞÊ4vËu¹éÕ=o‡~;àú|Z¯×I:ïcì@ƒ3 `dwì¹ÐñÖzú$q¯Ç8??¸¤ s½ :QìgŒÏCÇÞ\RæÁévñCw¦ö^‰¼ÐµpÚs“~Ü xÉ+í &<ôáðaáXmíBK¯°ñ ›½Ljôêä‡É)“÷ ƒ;Ch/±ÄáóßÈÿ3ëÍ K/¥}Óó«²±DÉ’ƒÛ93°ÞÌ¢Þ«¸¬>Æ‚œ‘ðÆû†N®Ã½(bÌEaoOW{„ÝÉ·¿ 1êéÈByãB¬x@ Á©—êïv±#†^·çM÷|ÆjçtÞÇÓ;^ÔÎzø)ä_üÀ X æÐ ,Ç܇×(¹[=FÁÓµQÿ°l_ì¡ sáhŠ?Oå“⇰Åž*û¤„µõX€Ï£ç `Æ”ziW#¡ A±ž οñæÑAä—Ùó³ñ¾îídòÈà0ÝÚ‡œûK°.öÛÃŒK ÉôÄ2 c‡‚ÑÃWcÿ ùVcU%ðÅfõ›Ls]€šê,›S›V(Ã@Π6koúhê¡¿-$Úsä“7¦lç¢d«6tòxˆóî®ü[U[îª1HWûkêio6ʧʌ`@"‡tèÍ(#¼4Ô¡hú [Ö‚•Щ.`ú,Ur¡ú^äã„*±¶Ÿ/¹÷ûæ:À5Ð@n=qIÎÉ ¥³jFèT6áÖëÎ#è¤6Jt"ÅÑä`ºKn¶…²J*TàhžpéQ)-ôbÈÌhÉÂYoÏž‚GʼÈ?û•ñ¢F½C…B&0†:”]Õö8_žK!I4zãÙóË­â§V?¹^âLB‘Ë„b¤otWÅ`½Äõ¦—Æ©/ ž §†KèïS±4-¶p;¬7ÝŒ/½Ä’üI¸üV•bÀ—2£ÐxŸ²ãŬDß„³4ñ{µ‰ÎË*Î ÷àRïzá}þEMg'z‹‹ä*Q$£7Eú_óË4ûÇ6(I hì ^ÉÁ­qV È­Î³÷°+¢åY(pk#WBèW¶ÔTGzø]í¡÷”ÚßaO¬zpØÕBöL« .×å¦×ñ\­03ÐkCó|šíldƒ«’˜¤ó>YFö_h^dxåJMÖ¼„º©’K==„²½ª (ò"kx$ÙË 3±Ð(öF&ƒ´9œœÍ‡|Ú,X®–‹”иKJ“¬ eˆ¹,¨îødë9€7ö°àq:ÇØä²•Ûq¤ËRQTÊ2ÔUŠÃ>‰5$‘¬=±i=; õµ“ÐþG#e•œ-~!k4–ž|¶ü´XæC Y)<Æ~JùÇ·öª2£=˜âàÞXö˜P°¡}NdæqW;]ZÞ÷U÷çY—ç4RxoFNžQŽëe½NÜôR&ƒÛDDëYq|uÄ>‚Û™õ:a½î+N›ùû¥Ãöû+~/;¬¿¿°÷„x0³Íe§5”ÕÍÆðÑ{‰?ëÇï۲©ìrœ|œæÝûC2µ^¦¾‹Í\Î0)½Oç‡Sé ÐðŠÖÙâ\c©ÅdëÑÙyè¹ã®˜u]æ ’­ô D 刖GÚi£XË:Šö?ÖmÖ#”ÎHFƒÎžTÚßÍ]=f{ÒFσu‚[=®zm×åÃ)K¦†N¯5HþùLç}ŒB®‘ dWZ–¦Ü&vÐü f9‰»~þ“¹‘:ÄŸt¬ÀÃ\kè'ƒdF€±ƒ÷u•މ͈½8`LäÈŒ#äŠSÑnNv™)Xof+½1 l‡œãÉÌ=’|3ëÍ´£Ì · Ý¨°¨ ;2Ž>Vz“%;u Æš1ãð}Ë„¸ s Ÿ ’x§¹¸Çž†5!¨ý9êÐÂA†q½ÄgÖ;úŒ•Þx4¿MüááBé‚ïof`½¢Ì+ç8ÉXø¬lœ¾±Ò›,ÅÎdáûÎ0Ì‚3†a†a†a†IœÑÀ0 Ã0 Ã0 Ã0LÚà@Ã0 Ã0 Ã0 Ã0iƒ Ã0 Ã0 Ã0 ä 4Œ´ô£c2ÈڟùNoJ¤«7¨î8KjE1šöÄ¢Ì ZÆ2ÝÕ¥±²“õfÖ;*¬sêàvÎ ¬73Ld½T‡ciOÆÁ>˜ËrŽˆtÕÃ0 3Ä4G¹iµüDË>Q+?¢´a­±¾³ÜÖâAŸ!g¬Ðò–õÀžWñ‘’$o¶qxkpáà«Ø‹z<ß0×(Od’x2×SßÑ8M¹J8FD9X‚õfÖZVxS•SÛ93°ÞÌ0®ô¶Ђ™Ø¶ä%H—™É}º O¿ï´la¦q«ÞTY±q!“[9jŽýé°ÓL¶µÎëJ—=ñêѶ'‚š¦«Â§zæ6í3¶g^V•ð<£d6¹žï{/r|Ó{ðˆŸsæú%nFC©pzgy/!Ð- ¾ÕXUÀÞÖ`-m{X°<ÉÞïë€Ò†ÕXhÁ•ÀD8ñk_H$ø@ËcR·€š=Š„ëI6µ X¹:ñ Ò(Û£E¼ì4l™3B,Y2ùþs¢®.±|Y´¦UŽ`EšìLÖ›XoÒÐ2„:'…þ«†ûðKçbçÜΙõf† ®÷Í–.œ/Ÿ1fùÅ!{íkUÊ ëÝÙ‰-OuÂù5FkvÛI±ï¸Øü8ô™Ú¡CSO<ƪ}F›qß',+á¹MxÍw‰m#ΣE÷­ò]z…dÆÖsÑ”k(yóÃÇwDþÖ¨Ìõˆëò–”µÐX9M•"ãûÄo¨§~t¢)Óaqàu¼´£CÁÙ«ß½ˆw:•HÊžÄ}>ƒ½a§œ²FŠTaŒø]›­çQ´¿þðñ)Ôã[篂<ÅzŽM.èjÁÚMTÁxVa{œ{›êuÙ!ÇâÙœ Z"_âÔ‹r’^†(+¡Ö´r-ê‰z<«¤8"“äfcÛý¹˜ªŠèí³Õë9=óÛ»ìŽF¯V® Ì Z˜ë©ëì\·*Áá\TRék|þnzªKP/衧 ÖkÀzǯ^ª«ìläoÅ„uØgøq;°^Ö+'zé·û1\ˆúvBïŠuá^Mã]6™Âñ¢ƒœõ»ÑÊñ£Þè`[Ê+²D)ÿVåÀSoó9ò ˆVá°йÓB@q<ÝçàÏ›ò)ƾöEhhœ­Þ‹”L~6pê­ÙPt†„^CÇPÛI¹¯fÃBQ'I¢ë(kZˆyíDZ{§ÈþSê8y.ÎaËsýÚzVl(@p@Ø]L%Ëõ*œv‘º"vêêq“÷×-ÄRY¶ÐÝ-͵í@×9Ç~ nö ‰ö·×­Pöhë±=ñ¯Ë$ª=mõ bèù¼¹<ŸÖëuc¤÷Ñ|N ?:ŽÍ÷v;”•P€·Ðóþ.%!(»á)Üüé]¸pÔD׳3š–Œc²އ܋õ0×® 9¢Ë{ðÒ&hÝnp`,P»ýx©y»Xq¨\œF°FþÁz?¶üÐ:7I´ǽ£!5¬mîòÛ0’Ïo³ÿÀ‡u»'É^W4áÀÈ™/»‚†£7aÛý7¡Õ*_xÿö0SY`;O–g"(^’¤“bÖ£y!“ŽrG*½Hiô6ìCL{(È€ƒŽ€…"ÚÎH@Âhq;X/ëÏz)ÈW‹óB»€-–¿q+ýz“…õ²Þq­×å79.¶÷ Ý;Nl¤cûÛˆÓDC •³GNáÊi=ز÷fáôMÅ1«“Y}»:ó°²"$~Ç/ãVáï?‰`õ\Y_NcÄá7'‡ßÄ©W:}§Ô0p–¥]2bAÙlrž…Ckur‰èzʬr2Ã×k9Çi'9å·^Rμk=ÖëÕÉsì¶ñÚABõ” ÊcôöXÎK ý 7]ºzÜÎ7qµYý¼•#h:í–ërÓ«{ÞývÀõù´^¯“tÞÇØg@ÁÈîçs ã­õôIâ^q~~6pI˜ëýЉb/8c| ¨È2ø:^—Ù ¸gýµ1–L9ƒ#*kàto@þŸ.õÔV · ÎœSãž["ò®µ’î†ÔÇÔHîºNöz0_ {¤ÌƒÓíâ‹1ï&Lí½yÁià´ç&ý8:ñÒSÚLxèÃáÃÂÁÙÚ…–^aãA6!6z¹ÒèÕÉ“s$îAw†Ð*^êˆÃç¿‘ÿgÖ›Xo"Pú5õÞ±*K”,9¸3ëÍ LïÀU\VcAÎHxn/ó=$U„ÃT>ÐãpƒhUNî`_HþßTxº#ŽãÎNôN1r‡Úz ù•s8¢2 ù|(D– çLΟîiV¸ê Á¿7âàïÖ8ªÄàk~9\¢µèÝOÃ&ÄzÝ‚ îÃiüáë5q±s^qäx;¹jãýJ'×áÞ1械·§«=ÂîäÛß…õôd¡¼q!V< ‰àÔKõw»ØC¯Ûó¦{>cµs:ïãé/j‡WéôðÆæÏÊú̯6À$¥"šsjЖP†‚ Ø O GFŸ7"¿ÌÈ.˜Ÿ=ˆ÷u¿ÖãG‡ñèÖ>äÜ_‚u±†±ÎhH¦G”a˜ñ߇¯ÆþÛ•s{_lVï {TVŠÔTgٜڴBrîµY{ÓGS¯pým!Ñž#Ÿ¼1e; [}´¡“ÇCœwwàߪÚrPíˆAºÚ_SO{³Q>Uf 8¤CoFá}¤¡EÓOز¬„NuÓg©’ Õ÷"'ÔP‰õ¸°ý„x©¿×ØÇ\—¸h"­'.É¡òGA:«¯½Üo$°@Îh8Û‘tA'e~Œ‡Õ8¶¡»äöh[è>–T¨û¸ O¸ô0”z1df´dᬷgOÁ#e^äŸýÊxq¡Þ’B!“CJ‡®j{ œÇ¯ Ï¥$½ñìùåVñ\«Ÿä.q&¡Èe‚-Ò7º«b°^‚õ¦w½4^|ið\8E[Bß˜Š¥i±…Û™`½é†õš,ÉŸ„ËÁoU)|)3(Å»Qó}ËY‰¾/ giâ÷j—1TœîÁ¥ÞõÂ+úü‹šÎNôÉU ¢HFoŠô¿æ—iöm(P’ÐØ¼’ƒ[ã¬@‘[gïaWD˳PàÖF®„Яl©©Žôð»ÚC=î)µ¿ÃžXõ((à°«-„ì™V\®ËM¯ã¹Zaf.$ ×†æù4ÛÙÈ1V%1Iç}4²Œì+¾Ð¼ 2ÈðÊ/”$š¬y%uR%—zze{UAPäEÖðH²µ™‰ŽvèD±72dØáìÜŽ·Ox-©øÀÞD{ȯ:ði«qÐ8Dj³úÈPµt¨;ºã“­çÞ •A§sŒMu÷ñH—9¤¢þ¨†e¨«‡}b }Г¬=±i=; õµ“ÐþG#…“œŽ-~!k4–ž|¶ü´XæC Y)<Æ~J½Ç·öª2+=˜âàÞXö˜P°¡}Nd&nW;]ZÞ÷U÷çY—ç4RZoFNžQŽëe½NÆ“^Êdp›’h=+ޝިGp;³^'¬×`¬ô÷• §Íü½Öa{ßïÖ÷Ø{B¼/™ï(æ2ÛÊêfcøh‚½ÄŸõã÷mYáTv9N>Nóîý!™Z/SßÅf.g˜”^§óéô hxEëÀlq®±Ôb²õèì<ôÜq W̺.sÈÉVú¢†rDË#mˆ´Q¬eEûë6ëJg$£AgO*íïf®³=i£çÁ: Á­W½¶ëòá”%SC§×$ÿ|¦ó>F!W‹È²+-KSn;h~³œÀÄŽ]¿@ÿÉÜHâO:Và‚¹öÑOÉŒc. 9Y&¯&2N‘+jLE»9Ùe¦`½™ázÓÊvÈ9žÌœ'IÀ÷73°ÞÌ0Fz)XHInÁÂQaQvd:}¬ô&K vê&@Œ51bÆáû— q™ë4ŒÔÛŸülÊcŽÛ ̘#DzËŽeúe’õŽ>×›ÞDHç,û&|3ëÍ c¥7ÞŠNÌÄ€Ôk¾Ìx† Ã0 Ã0 Ã0 ä ýò– Ã0 Ã0 Ã0 Ã0I†a†a†a†aÒ†a†a†a†Ih5h2Hûµr¢E§,ÒUTwœ%¦¢M{bAR5zñˆ*Ž[ÆÊNÖ›XofÈ^š„Ò¦ƒÛ93°ÞÌÀz“‡êp,íÉ8xÀsYΑ®z†Ä4G¹iµüDË>Q+?*§R­y¬ö3NhyËz`Oª+NÐùª3áÀ[ƒ _Å^Ôãù†¹Fy"“Ä˹¾øŽÆ™hÊUÂ1"ÊÑɬ73°ÞÌŠ^ZVG¶Ò·sf`½™õ ÚhÁLl[rƒ¤ŠKT‚ä>]ކ§÷ZM SޏUoª¬Ø¸É­5‹FÇþtØéF&ÛÚ çu¥ËžxõhÛ“AMÓUa‚S½ s›öÛ3/+áJxžQ2›\Ï÷½9¾é=xÄk!nFC©pzgy/!Ð- ¾Õxþ`ï k°Vl[;æ^iš)mX¼qP L„¿6¡å.;ðN3µq ¨Ù£H¸žÔøhS ‚•«ñ O â1Êöh?þ iXþnÉ’™È÷Ÿuu‰mäË„5­r+ÒdgÒ°ÞÌÀz3CôÒr€:g¾ª†ûðKçZ`ÜΙõfÖ›o¶tá|ùŒ1ëˆX\±×2¿œhÂzwvb‹ËRŠä°f·ûŽ‹ÍCŸ©:4õÄc¬Úg´÷÷}²žÛ„·Õ|—Ø6â<*QtßJ!ß…¡WHfl=M¹†’—Q00||ÇAäÿ`ÚÉ®Ë[–6¬Ecå4Uаvϼ8¿ k70xø ðRóv£Ì(a5ð»ñN§IÙ“Xà¡Ïg°7ì”SÖH*,°Køb³õ<Š~WÀ>>…zè=^yŠõ›\ÐÕ¹¯zVa;^ÚÑ¡$n¤z]vèÿÙœ mMnêUkèSVB­iåZÔ õæ·wÙ ^­\A™´xôð¿Ê²ÎÎu«²ÎEU!•ŒuÐ{ªiMt¹;Bö»Áz X/ë•XôR]eg#£& Ä>{ ‘ÛÙ€õ²^Éu®—ÞUÃ…¨ï'”\îÕ4Þ`“)ï]:ÈY¿Ø¡?ê¶e¡¼"K”BðoU<õ6ß™#‚hû;-çÀÓ}þ¼Ù(Ÿbìk_T€†ÆÙê=PÉäg§Þš å@gHè5t µ”ûj6,u’$ºŽ²¦…˜×~»w*€ì?¥Ž“çâ¶<ׯ­gņ„ÝÅT²\¯Âi'©+b§®7yÝB,•e ÝØÒ|QÛt­‘sì×àf’h{Ý e¶ÛóÿºL¢ÚÓV¿ †Þ˜Ï›Ëói½^7FzÍçÄðk?Åe%à-ô¼¿KIÊnx 7z.5$Ñõ¬ÁŒ¦åÀ㘬‡÷!÷âFG=×77Ì((úkõ9L°í tà¶ücøÛuí(ûá$üŸþ]·|wλ >þgyÜ?yó'.3_=îý^›ÿÉÚ&œüø÷8ðá·¢- páÃOÐ%å>,ª¿…§_Çßþ÷·Ñ9c)~ô½pà§å^¹ßv|²õøðÄó%8.îÝ?|(λú}<þÓ2q¿fy%z7ÿ ¯þ#É¿‹;óÏ[ôŠg oçÞ¦z]vº»/ã¶Úi˜}<„$Îüã“®àW“±íž°ok~uü"vOÆ w݈ž¶oðÛ6Q²ÛnŒóæy‚u?õ¢çÝ<{˜ŽÿÌ»Šíî/ôÒ°¡.eâ[2ç–éxðV±†°S£wgï$­=”yBAìÂ3'"±»5vÞ]= ÅÂÙùÏÿ3ˆžÉÙøñŸ‹_µ ªc¿ƒÃ¦íñ`½¬—õ:õR]9ÿ¡/”‰¿uSž›Gç^Å»0þ–M¸ X/ëe½â]%k~úÝï„óutýÞ‹ÔFï]?šŠØô†zW2Þ§å;™æÈÉ­ ù¸´çKt÷åy+|ð}Ý…-M§qº4µó¯âØWÓÑðŸ&áèS~ìÛý%ŽMöb壓àNA•¸¾£[÷ƒÙøÎG'qvf>¦\À‚Æü‹ãøc{/JN½%uÅ(ÿ¿=Rï1qNé_ŠwžÝA|¾Wœ/Ê…+¼eŠÕ’³ºú/‹Q:ðø që ±-ø Ç>þZØo—'œç¿øJ8­¿6¢»Ý.õó~<3ΞÄÿøë³‘ëõ˜8í$§ü»}ÆñT_Û§Æ}´Ösl²Ëd\¯›|߯ÿ¤l™Œcf)ºvhÿذ_nÔž÷ý›» }úåÆ@?t¢Ø+üÈ3Æç¡€¹¡ñû*5wÀz”>Ç<€I38¢²N÷äÿ©áROmŠq êÌ95î¹%"ïúX›a éŒðÞ&w]'{=˜¯Œ="<ÿÓíâ‹/ï&Lí½î… ÈÓž›ôãÊʼ(í &<ôáðá>Ðp‰ñezú ›õ^hôêä‡)‹¢q&‚ï:²"b2„VÕ+røü7òÿÌÀz3ëÍ ©é¥4hs½cU6–(Yrp;gÖ›XoB \…Þ-Š@½žæ{rø½+Uð¡| ÇÖƒ/{ƒUô`_HþßTxº#=Ä;;Ñ;ÅÈíjë1äWÎáˆÊ,8äó¡9XjΟîiV¸ê Á¿7Òó½;Fôàk~9\¢µèÝOÃ&Äî57ô.…½']O0œÆ¾^;çGŽ·c‘«ö1Þ'urîí@Ác. {{ºÚ#ìN¾ý]ˆQOÿ@ÊbÅJN½T·‹1ôº=oºç3V;§ó>žÞñ¢vˆwÖÃO!ÿâá¬`=.˜C'°sŽ …ˆ®ÇÈzðtmÇ,Û{¨Åuˆk AND(¾=•OŠ/Êz{(¸`L`óÑ&c~ÚÞèõÂè1NbÆ'”š§î—ÜæRœÙ{ûæÑAä—ÏÌÏÄûº_¯qÈ#ƒÃxtkrî/ÁºØ¿N ÃŒh¸T=úÐðö0"} Ã01 Î†á«±¿3|«±ªøb³zçÚ£:ìR¤¦:ËæÔ¦J—s'¨í¹~µc”õ ‡Ðßí9òÉS¶sQ²ÕG:y<ÄywWþ­ª-÷ÕŽ¤«ý5õ´7åSeF0 ‘€C:ôf”ÞGêP4ý:ÞZ¯$vB§º€é³TÉ…ê{‘j¨Äz\Ø~B81÷û‰k &"Üzâº÷¨/Ié¬:#AËðÄ=^|ñAüñe×GÐIs$:‘âhr° Ý%·GÛBÙ %jæcºÑ÷ÒB/†ÌŒ–L œõöì)x¤Ì‹ü³_?äÔ{P(dòc¨CéÐUmÝyüºð\ I¢ÑÏž_n_HµúÉægŠ\&œ"}£»*ë%Xoº_ziÜöÒà9û˜oúþÁT,M‹-ÜÎëM7¬—+½Ä’üI¸üV•bÀ—2cT¼ Ö8߯¼˜•èû¡p–æ` ~¯6ÑyCŹá\ê]/¼¢Ï¿¨éìDoq‘\"Šdô¦Hÿk~ìêÌÃc ”$4v¯äàÖ8+PäVçÙ{ØÑò,¸µ‘+!ô+[jª#=ü®öP{Jíï°'V= 8ìj !{¦Õ—ërÓëx®V˜™ 赡y>Ív6²AŒUILÒy,#ûŠ/4/ƒ 2¼ò %‰&k^ B]‡TÉ¥žžBÙ^Uy‘5<’ìôkíЉbod2ȈÃI“ÿ©t°ßÜŽ@Œ þ®_:ði°`¹Z.RB“:R›ÕG†2Ä\Tw|²õÀ{Xð8clr•Îí8Òe©¨€?*â¾ u•â°ObMI$kOlZÏNB}í$´ÿÑHi¤—ÿ-~!k4–ž|¶ü´¨Hs9ÊRxŒý”-ŽoíUešLò`Šƒ?4zcÙcBÁ†ö9‘™©]í4viyßTÝoœg]žÓHñ¼9yF9¬—õ:a½F&ƒÛDDëYq|uÄ>‚Û™õ:a½×›^â¾rá´™ï':lïWâ}¨Ãú~u{Oˆ÷CóÌ\V\CYÝl M°—ø³~ü¾-+œÊ¾²"„Ö8=Ì»÷‡dj½L}›¹œaRzæò•áTúƒ4¼¢u`¶8×Xj1Ùztvzî8†+æG]—9dƒd+}QC9¢åÀ‘6DÚ(ֲޢýu›õÐd‘‘Œ=©´¿›=ºz"ËŠσu‚[=®zm×åÃ)K¦†N¯5HþùLç}ŒB®‘ dWZ–¦Ü&vÐü f9‰»~þ“¹‘:ÄŸt¬ÀÅõˆëªÌH1VcðZ½Äå€fL^x=ΊLÆ‘+jLEû»#_‚3)Xof`½ce;äOf®•$àû›Xof¸ÎôR’2¡â­8‘6 á!`G¦ÓÑÇJo²¤`§uµ +:ù˜À÷=.â>2a8Ð0jPo¿uiÊ BíÏíK˜2ã9¦¼ÐX†+Ó/W¬wôa½ã‡ðÒºªœøþfÖ›®7½rYë²+)-±ÉŒØA½6àû8±à@Ã0 Ã0 Ã0 Ã0iC¿¼%Ã0 Ã0 Ã0 Ã0L’p a†a†a†a˜´Á†a†a†a†aÒF š Ò¾f«œhÑ)K…tÕãÕgÉ¥(FÓžXÐM^<¢Šã–±²“õfÖ›®q½4 ¥M·sf`½™õf†tè¥:K{2ðÁ\–sD¤«†§Ä4G¹iµüDË>Q+? Ȧ5€×âAŸ¾Õx^ÊŦλ~¡å-ë=©®8Aç«¶Ì„o .|{Qçæå‰L?–æzÛ;g¢)W Lj(‡#C°ÞÌÀz3ÃDÒKËjâàÈVºàvÎ ¬73°ÞÌષ=€ÌĶ%7(Aª¸t¸%HîÓåhxÚx£Õ2åˆ[õ¦ÊŠ ñ˜ÜÊQ³htìO‡nd²­Ýp^Wºì‰W¶=)Ô4]&8ÕÛ0·iŸ±=󲮄ç%³ÉõÜxß{‘ã›ÞƒG¼¾Ä"nFC©pzgy/!Ð- 2˜@K6¶€ŠÄ—Éãsѹy Ö¾°[;æ&ß+~ QÚ° -xã ˜'~mBË]vàfjKg;+®'5>ÚÔ‚`åj{ )£lñc؆eè–,™‰|ÿ9QW—ØF¾lVÓ*G°"Mv& ëÍ ¬73L`½´,Ÿî¥¾ª†ûðKçš\ÜΙõfÖ›Ò¤÷Í–.œ/Ÿ1f/‹+B8öZæ—MXïÎNlqYJ‘Öì¶“bßq±ùqè3µC‡¦žxŒUûŒ6ãþ¾OXVÂs›ð.›ïÛFœG%Šî[)ä»0ô ÉŒ­ç¢)×Pò2 æ†ï80ˆü¬Q;Ýq]Þ²´a-+§©R„î=k”óLQÊÛØü"ÞéEêŸß†µ›ÐN™QWr{ÇÂùs(a5ð;Õ6’=‰úlmÊ)B P…2"t _˜m*¡v¦ÀŽy| õPpèñ*ÈS¬çØä‚®–ðý#èX…íxiG‡’¸‘êuÙ¡ígs‚¶5ª)Ê.ײ§¬„ZÓÊ!´¨PꬒâˆL’›m÷çbª*¢·/îÚ×äÌoﲿèkôjå ÊŒ  Å£‡ÿU–uv®[•àp.ª ©d¬ ÞSMk„ËÝ°ß ÖkÀzY¯d襺ÊÎF¾L(0‰}ö'·³ëe½Ö›éÖKïfáBÔw–ã_Ô» l2…ã=S9ëw£;”ãG½ÑÁ¶,”Wd‰Rþ­Ê§Þæ;sä1@­Âa s§…€âxºÏÁŸ7åSŒ}í‹ ÐÐ8[½÷*™ülàÔ[³¡è ½†Ž¡¶“r_͆…¢N’D×QÖ´óÚc÷N%ý§Ôqò\œÃ–çúµõ¬ØP€à€°»˜J–ëU8í$"uEìÔÕã&ï¯[ˆ¥²l¡»[š/jÛ®5rŽýÜìAío¯[¡ìÑÖc{â_—IT{ÚêÄÐóysy>­×ëÆHï£ùœ~|Àâ—Ù¡¬„¼…ž÷w) AÙ OáæOïÂ…£†$ºž5˜Ñ´8`“õð>ä^Üè¨ÇŽk ABŽèò¼´ g—°"N)¤ãéëøÁJ¯ãœëj·Ÿ/5oW+ÎÀ•ë‘sâuéÐËêýØòEì<Þ$Ñz÷ŽB5¬mîr} H>Öí:œ${]Ñ„ T g¾ì ŽÞ„m÷ß„V«|áUüýÛÃ8LeíÄ´‡‚ 8èX(¢íŒ$œç±#õ²^Ö;rÒ¥—‚šõ°¼ÌS`ô.`‹å;ÍÊD¿Þda½¬—õŽœ´éuy犋í}R÷éØþ6â4ÑP„Båì‘S¸rZ¶ì½Y8}SqÌêdV_Æ®Î<¬¬‰÷´Ë¸U8…ÃûO"X=WÖ—ÓqøÍãÉá7qê•Nß@Ä)µ œei— „XP6›ÇœgáÐZ\"ºÞ…2+‚œÌðõZÎqÚINù­—”3oÁZõzurã»-D¼vP=eƒò½=–óhÂM—®·óM\mVD?oåšN»åºÜôêž·C¿p}>­×ë$÷1:@`ÅP0‚2½àÜèxk=}’¸×cœŸŸ \R‡Xè‡N{ÀãóP /‰[dÁ{hp.{”Œ‰ÏQY§{òÿÔp©§¶Åâ¾Ô™sgÜsKDÞõ±6Ã@ÒÀú˜É]×É^æ«T”ypº]üáäÝ„©½W"?€íœöܤg%~K{ƒ }8|X¼ðoíBK¯°ñ ›ýøjôêä‡ÉY÷ ƒ;Ch?úÄáóßÈÿ3ëÍ ¬73L,½”ŽLcŸw¬ÊÆ%KnçÌÀz3ëÍ )긊Ëêc,È1æ³¼g¦Šp˜ÊzÎb­ÊÉì Éÿá› O÷`ÄqÜÙ‰Þ)F.ëP[!¿rGTfÁ!Ÿ…ÈÁRáœÉùÂ=Í W½!ø÷FüÝG•|Í/‡K´v½ûiØ„ØÂÁC¯[Á`8?|½&.vÎ+ŽoÇ"Wíc¼?ëä:ÜÛ‚ Æ\öötµGØ|û»£žþ,”7.ÄŠ” œz©þn;bèu{ÞtÏg¬vNç}<½ãEíö¬‡ŸBþÅ,‚õ¸`ÀrÌ}82"º#ÈàéÚ(ŽÿX¶/öP k AND(¾,<•OŠ/Žz{ªbNJHޤ§²^äÐ _‘¸̸€RÕ^0æÎ[B ‚b/<ÌŒÞ<:ˆü2#»`~ö Þ×}›CÆ£[ûs ÖÅþ¶fæ:&œÑL!Ã0ÌX@+ÃWcWùVcU%ð…š£míÕA™"5ÕY6§6­P†œ;AmÖÞôÑÔ+B[H´çÈ'oLÙÎEÈVmèäñçÝ]ø·ª¶ÜT;b®ö×ÔÓÞl”O•Á€DéЛQFxi¨CÑô¶¬+¡S]ÀôYªäBõ½ÈÇ 5Tb=.l?!œ¶{}\ 4áÖ—äœ òKC:«1RŸ¶¡gàW“Þ±¼ ž®¶² ®A: “æ.Ht"ÅÑ„îKÉíѶP¶BI… -Ã.èÒB/†ÌŒ–L œõöì)x¤Ì‹ü³_?lM/2y€1Ô¡tèª6¢ì<~]x.…$ÑègÏ/·Š?ÐZý¤o‰3 E.0‘¾Ñ]ƒõ¬7ݰ^‚ÆO/ ž §,Kè{S±4-¶p;¬7ݰ^âzÓK,ÉŸ„ËÁoU)|)3dÅ»oó}Ò‹Y‰¾ giâ÷j—1TœîÁ¥ÞõÂ+úü‹šÎNôÉU ¢HFoŠô¿æ—iöm(P’ÐØ¼’ƒ[ã¬@‘[gïaWD˳PàÖF®„Яl©©Žt/»ÚC=î)µ¿ÃžXõ((à°«-„ì™V\®ËM¯ã¹Zaf.$ ×†æù4ÛÙÈ1V%1Iç}4²Œì 4/ƒ 2¼ò %‰&k^ B]‡TÉ¥žžBÙ^Uy‘5;k];t¢Ø™ 2âpÒx+JªG1¦aÁãâ³\ÊòÞØƒpŠ~÷ó˜ãñ¯m:ði°`¹uÕ k»©¡ 1—ÕŸl=t_Æ}’ç­7–­ìÜŽ#]æŠ ø£"ÐËPW)û$ÖDD²öĦõì$Ô×NBû?z ßâ²FcéÉgË¿A‹e>’•Âcì§Tdq|k¯*ӸÃ)þÐèe ÚçDfjvµÓØ¥å}?Pu¿qžuyN#åñfääåX°^Öë„õŒ…^Êdp›’h=+ޝިGp;³^'¬×€õêI§^â¾rá´™ïc:lï“âý¯Ãú>y{Oˆ÷aó4Îjteu³1|4Á^âÏúñû¶¬p*»'§‡y÷þL­—©ïb3—3LJ¯€ÆÆÓùáTúƒ4¼¢u`¶8×Xj1Ùztvzî8†+æG]—9dƒd+}QC9¢åÀ‘6DÚ(ֲޢýu›õˆ¥3’Ñ ³'•öw³GWÙž´Ñó`†àV«^ÛuùpÊ’©¡ÓëF ’>Óy£«EdÙ•–¥)·‰4?ƒYŽ?±#º~þ“¹‘:ÄŸt¬À¡Ÿ ’Æj á¡$ šAxqÀ˜È‘GÈ5¦¢Ýœì2S°ÞÌÀz3ÃXée;äOfŽ—$àû›Xof`½‚£”å á!`G¦ÓÑÇJo²¤`§nÄX#f¾ïq™÷1h5¨·?ùÙvÇ·(˜1GŽí.4–¥ÊôËë}Xof+½‰ÎYçMøþfÖ›Xo† Õ&b¬ØÅL &ºƒÊp a†a†a†aF¡_Þ’a†a†a†a&I8ÐÀ0 Ã0 Ã0 Ã0LÚà@Ã0 Ã0 Ã0 Ã0iƒ £Mi_ÃTN´è”¥Bºêqƒê޳Q£iO& ‰½xDÇ;4iœÍÖ fÿˆ ku,ÁÅ8xÀsù¬‘®z†a†a˜ëŽá(7­–Ÿh¹Ã'jåG9д&îZ<èS"‰N~=BË[Ö{R]q‚ÎWëg·¾Š½¨Çó sr†1×ÞÑ8M¹J˜&¢ô ‘N½´ ŽlfútÚã¼_dŸQ¹W;ÛhÁLl[rƒ¤ŠK 0ArŸ.·¬Ûœ9Gܪ7U"ëL—£fÑèØŸ;ÝÈd[»á¼®tÙ¯m{R (Ö:ç‰êm‘u¹Ÿy9ZæÜ§áÆûÞ³ÿ<âë’a†a˜±!nFC©pzgy/!Ð- ¾Õx^.ÙØ*†ÑɯSJVcA oTáįMh¹Ë¼Ó¼F«iÏ„ëI6µ X¹:ñ€QšìY²d&òýçаµKl\ÎI8¯ i^Vn4 ö©î‹^kŒìw»_ÍoÓç.œVÇŒo¶tá|ùŒ´¢eqEÇ^Ëü²_ ëÝÙ‰-.K‘ÚÝvRì;.6?}¦vèÐÔ±jŸÑfÜß÷ ËJxn¿zÍw‰m#ΣE÷­Ž>ªdj;Ð\üRãBÉË(˜?h9~ù?X£v2 Ã0 “i\—·,mX‹ÆÊiª¡{Ï僽a§\´WS*,=/—ð…­í¨=)€cŸB=z¼ òë96¹ «k7PãX…íxiG‡’¸‘êu¹CkFÏoï²9ÒÔ«^[Þ²¶óºUÙ碪vØ×™¦^õ*ua—…#Üš3õò8 ½}²žÈ±Ch±:뤫ÖlȾXzk`«‚‰ÐK=òÎë _;ÜõM«fûìútö»ÙÙSínOªke»Ý/Û}#4íio#CM»Yí¤ Çc¸€Gÿ«’¸CÙXuá^Mã…M¦p<ÿ:ÈY¿Ø¡?ê¶e¡¼"K”BðoU<õ6ß™#‚hû;-çÀÓ}þ¼Ù(Ÿbìk_T€†ÆÙêïQÉäg§Þš å@gHè5t µ”ûj6,u’$ºŽ²¦…˜×~»w*€ì7×a–çâ¶<ׯ­gņ„ÝÅT²\¯Âi'©+b§®7yÝB,•e ÝØÒ|QÛt­‘sì×àf’h{Ý e¶ÛóÿºL¢ÚÓV¿ †Þ˜Ï›Ëói½^7FzÍçÄx¿X~/ìPVBÞBÏû»”„X Ï3ïÿC]†$ºž5˜Ñ´8p.²އ܋õ0 Ã0 “)\3Nïxk7.~ÐÅË·ìU§t3ÈÀÄÄ·>ñbhwªcd(xªà ¼.ö­ÁÖÀ‚屿GH¶áð?îÅ!#ùÚ=@Cò¹èÜlÊÏÈ*¬œî À3w±*éHÏu‘ÓHéöä\–ÖªÔûxãð=¹( ½é着ãÉq-;kö²wIG”zÀ^vrdϦÓêÚŸ›mµˆ+žûzá¸/¡}½nèô¶•Ó@zd½7 (ûkür †^ql.£ÕÔÐf¸Ø«’!éû£=M›ÂrQO"v>ÿ ¦Î™l´M >Ú¤žqóùÿÙ2%‹|¯É} ˆrpö¨Õ–”Oë‘Y»ÚÄþºé†Ów'„óF™bÛ,ŽáŽ×±­ç0T<9GOÂ% 4„¡q*޹o­7Kè4ŽÛpE‘L¿?ôœQî5’³JÃ%È©-¼S p¤Ü“ó\> œÖçúeÙ­ɔ٘sÉÈŠ_¯§TïœN3‹âxÄa¶Ô³eå©ëu‘·7›¶Cl|Ž8ãîí`ž#7jOu½:{’iÿXöèê©©Îζ’«l’Ø×eàlÏy8kÖ³?¨¤z½nÏ› jižO鸉±“K²ñMŸ#8P}/ò/ ÜY ÍñõmÆÐ‰ìSwqa†aÆýЉb/PèPÀµçIgpDe sŸ:.õÔV · NÎõ ¶{n‰È»>ŽaÐnÞHHüºî“Îd‹ðjNtw,£B«êÍ&§Ód~aDž2y7ajï•Ho|{§=7©qÔîz“%?ÿ,ù÷7‰Of½¢®˜z“%=vº‘ôýŠq]fÐBnጇ¸ŠËêc,¨×Ó˜ëÄòü§Ê>áŒ÷Øzðeo°rûBòø¦ÂÓ=é!ÞÙ‰Þ)SåÇ¡¶C~厨̂C> ‘ƒ¥æü ážf…«Þü{#Néî=Òƒ¯ù¥Ã×Ú ôî7ňCkè] {Oºž`8?|½&.vÎ+ŽoÇ"Wíc<ç:¹÷v0ƒ+ÎötµGØ|û»£žþ,”7.ÄŠ” œz©þn;bèu{ÞtÏg¬vNç}”šl†¬‡ŸBþÅdFBZQ‚K§Ö«²At=”õð<]ÑÑü°lŸ1ƒa†a˜1Á5Ð '"/åžÊ'Å z=Š=U™™”I?2+EõÚÒÖ¼]íˆC±ž@*0éDöÆç܈2á´æ—Ý(Ü„«jïõÛ¹Ùx¬øü]¬8˜Dh‹‚ÃWqX]ñ­ÆªJà‹™;É {¥-NmZ¡x ˜›Ê, FU¯pým!Ñžq2ˆ e; [}´¡“ÇCœww"–ž-éjM=fö©2#HÀ!z3Êï# u(š~oÙ 2›'ÁÔqFÃz\Ø~B|¹Þkìc†a&ã¸(~ë‰KrNùr.U÷ÆAçtÒÜ>UK¶¡»äöh[([¡¤BŽ–á —žÞÒB/†ÌŒ–1eŠÔ¤ëèñy°T»"A¤®˜Poyá”ðŠÔë^:tUÛc‡^ª?{ æ¯àMñ9aŽxAVrÞÁañÿT,Mˈîí`d¤µÝu|ƒsn…Îû«¿_Kò'árð[UŠE_ÊÌñ7Yã|ν˜•èß©p–æ` ~¯6ÑyCŹá\ê]/¼¢Ï¿¨éìDoq‘\"Šdô¦Hÿk~ìêÌÃcqÒçc¢±3x%·ÆY"·:ÏÞÃL#„~eKMu¤‡ßÕêqO©ýöĪGA‡]m!dÏ´Úàr]nzÏÕ 3s!½64ϧÙÎF6ˆ±*‰I:ed︠ydá•_(‰‰ÊføÔ)w©§'€P¶WE^d $Ka†a˜‘ :QìLq8…S*ÓëQŒiXð¸ølŽùw•_tàÓçœÖöQCb¶îødë9€7öŒû!Ï[o,[Ù¹GºÌ!ðGõô.C]¥8ì“XAÉÚ“<ïÓü÷SjýLýñ{¼i΂óå³ÃéøÖe#u‰MÍ)`.ÏX êé3Í œú-þIFYlÏ–ƒ–·‡c÷žÇ J¯¨ÿ¼Çƒ|GYn–½óô9–ÞÖ³ÑsB¸Ú¯öépkÂbq3ròŒr*$Óžt½­½ê8qqÖ~uv÷• §íq†ÈØžsñ\vXŸóØKs‡˜æò®Êêfcøh‚½ÄŸõã÷mYáTö•!´ÆéaÞMãØUê»ØÌå “Ò+0—¯ §Ò'< á­³Å¹ÆR‹ÉÖ£³óÐs4oÂü¨ë2‡ll¥o j(G´8BsB˜mkYGÑþǺÍzh²ÈHFƒÎžTÚßÍ]=‘eEçÁ: Á­W½¶ëòá”%SC§×$ÿ|¦ó>F!W‹È²+-KSn3ö%šÍ@týý's#uˆ?éèÀÃ0 Ã0™ÂuÕ f¤«1xMÌ 4i¦þÅ×ã¬8ÁŒ´ÊDÎq÷•F MÚxÿT´ÇXIc<@~. ž‹»âDÚXT€†‡€™NG+½É’‚ÖÕ.¬èäcß÷¸LˆûÈ0 Ã0̘À†Qƒzû­KSNjŽç·%< ?côâ—ªÏ6F°td,¢–ŽLÆ2“±—ëX–8e&.ì ^ð}d†aF†a†a†a†Iúå-†a†a†a†a’„ Ã0 Ã0 Ã0 ä 40 Ã0 Ã0 Ã0 “68Ð0jÐdöµÂåD‹NY*¤«7¨î8KýE1šödš`°Ñ‹GTq¼C“AÚlM‡ýT‡c IÆÁ>˜Ë?ŽˆtÕÃ0 Ã0 Ã0ã”á(7­–Ÿh¹Ã'jåG9дöüZ<èS"B:›jMzuÞõ -oYìIuÅ :_µe&xkpáà«Ø‹z<ß0×(gZÁa‡Üf¢)W ÓD”ƒ>¡å-qpd+N¸¶C{-˜‰mKnP‚Tq °%HîÓåáõùiÖúL9âV½©²bãB<&·rÔ,ûÓa§™lk7œ×•.{âÕ£mO 5MW…‰ÌJxžÙ‡¹Mj{æe%'Ö`†”¿øJ‰ËñÕÛ"u››MÃ0 Ã0×3q3J…Ó;Ë{ nQð­ÆórÉÆP1‚pŒk€½/¬ÁÚ^Ǩ3GuÚÔ‚`åj{ )i²gÉ’™È÷ŸCÃÖ.±ep™Eád7¤y¹ÈÑ€Ú§j¸¿t®—&ûßléÂùòið$ÊâŠ޽–ùe+Ö»³[\–ì#‡5»í¤Øw\l~úLíС©'cÕ>£Í¸¿ï–]zå.t4[ÏÅJÝ·(yEMånþ—Ô‘1ÑôÑpÝr;Ð\üRíd†aæzÇuyËÒ†µh¬œ¦Jº÷¬QÎ3õZÞŽÀæñN§ÜeƒÎ_…íxiG‡’\OP6ÂjàwÖ¶!Ù“Xà¡Ïg°7ì”SÖH*,½D—ð…­M©)°cŸB=z¼ òë96¹ «k7P…Dïaª×åÎ#õ%˜ßÞes¤©÷ý¤éDSzÙ4´|u«²ÎEU!íøŸ¿ LP¯•º°ËþshÍ™zyœ…Þ>YOäØ!´XuÒUk¶Nd_,½®$[On6¶ÝŸ‹©$vØÔ´j&°Ï®Og¿[ý=Õ%Úv0¡`Æc¸€Gÿ«’¸CYNuážPãÞÃ&S8ž+ä¬ßìPŽõFÛ²P^‘%J!ø·*žz›ïÌ‘ÇA´ ‡}€ÎŠsàé>Þl”O1öµ/*@Cãlõœ+™ülàÔ[³¡è ½†Ž¡¶“r_͆…¢N’D×QÖ´óÚc÷N%ý§Ôqò\œÃ–çúµõ¬ØP€à€°»˜J–ëU8í$"uEìÔÕã&ï¯[ˆ¥²l¡»[š/jÛ®5rŽýÜìAío¯[¡ìÑÖc{â_—IT{ÚêÄÐóysy>­×ëÆHï£ùœ¿ÛË÷°Ênx 7z.U"™¥Pƒ¯·ÿC]J$Ð×ã~¼Õÿ0ð¾Û>†a†a®G\3Nïxk7.^„“ {ÕÉ‘0ƒ ñ˜‹ÛæNC°÷z 2|‹á/°v§:F†‚§ ¾Àëbßl=,Xk~„dëÿã^‘™&bÛÔÉa-$Ÿ‹ÎͦüŒ¬ÂÊéÞGœq÷v0Ï‘µ§º^=É´,{tõÔTg g[ÉU6Iìë2p¶çŠÆ<œ5ëÙTR½^·çMµ4ϧŽtÜÇØ˜CžBþÅ,A†4S}¯¨ÿ†a† £:QìÊ ¸ô¸sÇÏžt6Àh8ƒ#*k€œûÔq©§¶ŸuæÜ÷Ü‘w};à; ÜÑ‘øu>Ü'Þá}>èîG3„VÕëNαÉüˆƂz=Ãs²˜ÏUª<àÎx­_ö+çp°/$ÿ‡o*<݃‘âè¢r?Úz ù•s8¢2 ù|(D–šó'„{š®zCðï8¥»côH¾æ—_k7лßp#­¡w)ì=éz‚á4þðõš¸Ø9¯8r¼‹\µñ¼éä:ÜÛÁ ®8ÛÓÕawòíïBŒzú²PÞ¸+P‚Dpê¥ú»]숡×íyÓ=Ÿ±Ú9÷QvDe!¬Çsè–cîÃk”\{=±X Ïm%¸tj½*3 Ã0 Ãh r"BáÿýsûeLïžó¦Mu0weÆzJì%þú>=V‘Me_·`‡‹ô0$ß±wØÔwµxÓ–U¯Â›¾2›J_bð@>¯8|±Ak¦Z,WËNëalÁü‚óò>Ùٺ拟rÊ/eL¯¢¦uTíÿÍiO ™ËhpÙ3‘ö³Ç¥'7­¨¹üŸ!„é ­7p^Í8áËÔpÕF;Ê¿?㼎…xã3˜¥fd;?Ü듯ROñJÔn(6-eDyf3B!ÄAè¬äZ1³1Tw—:€æÍ…|ó«ô¿ÐYCnnd–‰ª#Á™9âBÒ\6®òˆIþz‘Ý|hä~ÂmsÙãÖã§bãԌ؀…Â?„Ñà‚/Ðà’{äë)Äaçÿ…ú ÕôtÊ‹ö¼”[î¼.?g&hð;†7Òù½ÖºÂÖëaÿõj“ÙÖaä×—=Åô8÷ÿl Aœ¿{Å~¯tеC])Tú~ øEÿîB!„ÄCh áÞõ[ÑÑ:Ýnå8ý±?«Áh0Ù ÕÝ^Ùò^ääű¾¡}‡ÔÑËkòzׇŒÜU>y×:â¨' |—ýN=~LïÜÔ¯lÆDàå×W—Kž%LÍ\€Ø¢Ê;íL©óZج ‘›÷9¹+áÏdùÁ—Ñ`‚Téô",Ô…þì—Bî»ï.<^u˜À´3C8ZYƒE‰ŒÎTÀê&¬6]ì #“ì…ÎG›T™ LxñÑJ ©ãõ1Wpôý³èÒ»B‘ ØÊlg¦9d–þ}Øú¦ºŠÖ«=|¬ÉreL–@÷×jUgTé2À°Î ¸(ÇN«8=€ÔÌ$ï0ûN.©ÃúŽ˜¤!+Óë†üzÛ_N§ÆU½¦ŽÌ±ãz_ûË‹•N‘ê˜Û¹óNÁGïYÂïÄêc1€ÏsêYór†/*»eËw¾–°`FNWÎN—ž0ù¹•‹±Loû8ݧlW;È¹æŽ žC˜=(£ýƒº-Ö§žÀýPü¼< Ú3 _QoäýrúÏ7Œk½ŽÞ}bž?iG†TÞï^‘{Ýz‚Ø …z!„BHù„ŽÑðýžmØúöQõ¢¥œ«?îÃií€E4|x Û^PŽgz_dÙ)wÕ`|ä;µ&/uâÌö*WTѳ }‡†PûÀµ±UdP³«|\rC%j7@K§Zž}ňšfá¶±4ÊáÖòÎPhÙz4òr+òͨùľ$«ã×¶âê¡MêUÛK.„éQ2‰°Bµ—Z$ ¢êvÛù.tmÇ•¥ÆÎÊ+l0Á%wчw»¶Ø{$Ä"4§ßPû¶`g/°pÕr»#œi³ÃïáÒìTÀÑÌm¨¯þ²¯ëwÚEÝS«WOÕå»Þ1²ïõ–D æè}¯¦€Em¦¼‹Ïßûíò1°òÉåV–»ïõ¾‚ B²ø±'çDUHNÄŽÍG°÷˜Ú¿r†qúô'G´|ÇA`™r¼„„r¼¾Ù9€LcªzŽ#u¹uK”cÖ1 ß„”÷(¬·BÕ;dÊ«elA½K¡û9³}ÆÒˆ³úøvãÔÎ~p±^¼SÙéCœçäEå´>wNo‡éÑÜÑ€»Gë}Ùóõ‘o§è½û”)/KÖaöéÙqpÉÇìù†ÈOvy¶ˆClÖsÎxx;xÇèEÚÓž¯ËžrÚ?Ê—žö¶ ål[ùfãtGŸ—!¿=×tÌÄžžƒÃVê®7ì~ÓA-Çýé"ŽëMØïž¶ß*y÷µâÒC!„ò Ç=dc5þÁ¬gÒ%¤Â[Ö)ÛÕØ¦¶hæ(ç×ôL ~ø“•)z6a«Ô ¥ô¬û{ñåc‘ïEæ5뤫ep¤-·õÕ¨¨lÅTåp~µk½àAz4âÄ[¹œŸ’OY»1ô¥Ù%7êA[ÓѤƒz |of§6#Ñ¿]éùX~õk×EÈ'ÂøÒ~:óý™´þÅ¥Ô¨ÉJÈ áC_שd;ìéh2ËýÙ~Ú28üÅëµ/Î_Õ£^Ïm:@¦–ßÜc¥äáfåŒzðuo°u‡ÎÚ°Qó4$Nåzˆß;…3wLÓ«™cƒF~y_ÚÌ‚îæfÌF–I@–lO³%´Þq¤öçœÒ"z¤‡^Oi‡ïðiàÌÁ#ÆùË:´¦Þeö¤»Æ7ÖÉÌž¯Gˆósåƒøä¶}Ì “.¹‹ðvð‚+ùíj²»üö!BϹ‹Hv,Æš‡­ òëý§C숨7ì~sÝŸQíçuÔî‚,„ß½,êwkiFO˜Ì-p=Q„ë!„B!åhx¤Ó8]‰Ö§”¶‰EeÍ*ðùñ€êz»UÈ?Ρbþ*íüjgZ2Ì.ËFÆìº»|\ò|ÆOØî¬Á4ÆÇzMY¡'…ÑÊêòõä!rÏáö¦J A‚* Ê$@°.T.ëw¶™ã=´þ³ÌÆÀ'ú%<»¼öïn;¥×½Èf1ìîæÿÖ-Ÿ$ºP‰Ç“ÀÑ÷½Œ†ŒÝÍðh+ðíÛ6kácX› ºWÚçÔÆŠ¤ÀKÀ[lfp]ëUaêØ803:3¤&lç’:u'„à’C÷Ðä2|=ÿNâj‡/{áÄ\ (%àG½7”˜®càwO°¿[Ùß鉗B!„htø½£zLí|éO(Jïúõü{éûÒn… ;ú³/t´¢b ¥×e\†ztã®n`¹ùTÁY>.yÓ«¥?9èÿ W+çÉK—¯'ŠyM¨_‹@öƒdˆÎü¬O.ë^}¢g¼_µ“ØÓО³ÓÃe§P*«PÌ 9/—|R¹ŠA;ÆBçâR2Ê%Ÿ¼™SÚó3ª1«Ù®C9Kwãbñ^máÔ%dk²=¸Ò»>ûò%»UHû©S8ÓXö%Và§œz'ȹ×SØ{j&/’>‰ÃÎáËUøç§£ƒ5m3ƒ=ì–B¹ṳ̀4ÆqÎÚÒÞ–ëáµGzÜ'ÔþyöDé±HÀaï±qTÞå·!ä¼ÂêÍ»¯Öx™ %ÔÀqzíl²A’}q^G“eälg÷46 á«Â"Šé âÖC!„BÊÇ9ëDvJK<ƒçgï³3HÈ8 2µ¥=•å`@žéšqÂ"z)þéÊ œjûɄ޶ƒCÖ‡”ÂôeÉåû_É0d§°ÂYÊÒ¡?‹” ôÑ/Ðã·G‘Ýçh3Цט;/—<œ¨ûÁ7cƒL9ÿ˜sœ=$.`ÓßnÇŸW;Þù;–=z'pà,Û¼Á ¯àhê*U]Æú}WðbGîÕG[2Cxõ1¬í¨ÆqopH™þr®)ï"7ä(¾íMcauÎN3˜œmí"ƒAÊ7êù) ÎÍ ÎÚ:=@¤`ÛÓƒóÉÀ~=3°þ1àÓçF|¹økJ>LyÏ jV¯QŽËs€sÓWZ.›Á…0]~ûeÿ²Fcë<‡×ùºìü6eôë÷Ùè’ 6µƒ ºÚÁœ‹¬ëŒ =Žƒý4$Ì}.e´¿fKOðº˜6öï0=®zsç¥tÈØ Þ¬!õ~Šçý–j+¼?ƒöHFH°]㺎…ƒ8ùÝ[šÎý¾û(¦Gãý&Fè!„B!å1½%!¤$–ÔiGmÏYº!LV½å2; .ù¤Àë^”[â:B!„ØqI)¯ÏMŽÓ7Yõ–Ë­bg¹ðºB!„ 3!„B!„Ìh „B!„BHl0Ð@!„B!„Ø` B!„B!±Á@¹vdúÊ’ç«¿È´–Õø½Ý$yü\ÛG¦pÜÞŒ¹vsÒ¹Ùì¹U`»ý2áu'„B~VD ¹OtÖã­®Ýøõ“/!y| ÞúÌÊ_XFŒâÛ··áÝSº°¥t>……‰|s—;hzõZ¡gj÷æ2ÂU>¹Ì¡¾¼IïΰÉ?ÿúÎïþ2ýz#”)k?@ÓüJ½˜óÝ»ü:$žÝŒÚÊ~ôumÒÙœ-^9O\¼Âõ¨U[Ú  4üůyŒt>Ú„E YË`ßÎ4þ¢¥q¤ïG@þ¢r¬ç—‹™¸ê¸î¶†´O|Èïä&p/Ô<ÄCèÞׯLlúCqTûŽky1’wÈÚp@@Ž›;„]#V`ðÛã"Rˆ=qMë¸fûb̶ëÙºCÎë‹1ïä|ôžÝaÉÙ¯¸<€v6 ¿Òëš/bGÏ4u>HíL¡ûk+_0^pÞ7s»ÝUfÝw¾.\í#˜k0žm|Ji‡k!.ý7ÒÎI»îÄ÷?y½lñùîÄ{Ç),[îû !„âQ4£á^´`Võ(Ò§ÕFó<¯ˆ}Í|î]¿ Ó?„î ¢jæ ³{…r W ¿Z6n±ûÂp•IÞ³Iog—Cê);ò“T¬™²¶·©¬Ù_BaFwVWIAGù)k7¢v¤£v[^*êæeËöBír^{‘yÍÊDÇH+ê×®SrÇù:õ¨š¥°òí8OO>ûߨzƒ B×;ýX¿³ßÛí'Ójßõ *ÜÒܤíó+å´~s-ÎÎ{§°#ïE¿û¹#Jvgìv9”bO¤þ{âC5S÷ÞcXÖ9ÃÊËó_–ðþé©veI’ ƒu°3—ªf½ŠäôqœQÛùÜÌíÖÞ&ެ©;…óÍ#´}T›¬×Ne_ä}uÍ÷sâÒ#휼ÿ—[™¸žï$XÑ™ÄX×'¹w K¹ï3„BˆŸÐŒ†{×oEG«é¿÷súcVÿ íÏh Īñå!Y¬GSzÍç¥Ð·ë%½Y±ñêúÕhY^“¨›Hºz`ŠÜU>¹Í0HïÁFàÃ\@AÊUžX =f[0ÇÚ x:Ôƒ_=°/ôy{%LÏÀwyÝSQ¡®*³eDÿ*à±El¨Éïa0õLýJ•£•ÎâzLû×aWÄË…—Á"ëþ,“ “N/ÂB–ýù½ÿ®L)7œº ‹’âX\ÁÑ÷Ï¢kÈìË\J `Óÿm6ÂÐÙ¶°Õ3ØÖ„Õ¹îeÙ³X¿ï ~¿Ú¿/gSç£w?^Uö]Rïᪧž0î»ï.<^u˜À´3C8ZY£ÎÃÔg½Žöy´Ãcêx}L°}Â,§•Ù$ síYú÷aë›ê¯ù½ªÒ;8¬èäé«Ëõz }=ø‘=¥Š‚ÞFq;à]Iœî dôòú{Â5Ážì|ý.{\ç%ø{Ï3ÇŽGö0ûëËÚz¶¾ìŒ?RU)cŽDÝË-À_s á"Ʀ_ÁGû§bMÛ 7×g÷ 7{»ù)°µþöñX³=‰aŸ-aºCí ´OñûÜ£\Û]L–7üº‡Ønåb,k´"û;0Wý¿äöålj9 œWö]Rï7Óç;õDá²ß¼¥‚×þ|wë—÷‹v\Éf4D½ÏB!Å Íhø~Ï6l}û¨zØ('Ag/ˆ#áÂPçÿZ„áKëÕžrW ÆG¾SkòД‡`¯‰¤KfA6ú¾U:*¿ÉY>.y€¶ß¢v¤;èhé4…éËèu“¡^UvÙÞÝc Z@÷$ÌÂmci$Ôƒ]—íü ëxUlÜŒD¿—a{œå•}k[qõÐ&õJäç%õÐߎ+KMy ~ä‚òR òÍÊþOô †û|£ôx¬ÃíM•¸zÖdúðn×{ä‘X„æôjßìì®Znw\_Ü™ ÌU¯f²oýgÊÁ_Q‰û”Ts0rµD‹oSN¸)»~§qÆÿ²Ï«Sœw»Ï¼}zQÿ?«W{=¨S±¨êrvßùät B‹i³ÃïáÒìTÀÑÌm¨¯q×ëlŸD æ›vx5,j‹îéýüM¹îvùXùär+Ëý^è}%„d3ðcÿ\«p·º¿toòAõbÿX•‡ÙSÂšŽ™øq§9fÇÁa+Í‘oÏýÉ€)XÝì™c}Î¥·=áç%ŽÍݧ޹Z"&M–m_ŒÇÕ"Ÿ5dË7¶h™·äÇ­ÂO¤lòb_À‰®{ºE9ÖA'zìì9œ@ ÚÛ¦a¸gç.Ú–›¿Ýr$›+Ôù/ïjŸ(òÛÁeg{[®}vl. X¸(ü™7‹×ûº‡Ù²Ë«S vŸ xûôr¾Œ¡ $§e÷-¨W?ªf=L‹òïÛ¸Ÿï%ñ>C!„”‚ûÓ‰Æj ýƒYϤ#擉}ˆ0樇£‰Ì~˜ûLA‚ ƒX¥j‘÷GÒåc“ ê½´ £'LÁ?>ü £‡lPÁöxA½xcÔW£¢²SÕƒÝ †P»öµc *úU}y{GyóÉ„y™"/^Àâ`ùß§ òraôèö³‘ðóÒcÀH¸ ¥ò¾ÜÓ§×¾?“Ö'— {A„“i|Ÿø'=èØüÙ>y ŽMÅ¢m‹%ŽX&Y{:šÌ’Í„®à¨ïü?|Ùåp)5jŽË áC¯Pá®×E®¾8UÿBzŶ½ð’Y~s•N‡›•7˜ç¼ çÒ¹ß;…3wL‹o8UßìÓùõùµ'.ÂÏk^£OîCz„sÿ@y9§fï©™xÜs„¤'ÕÊe)%ðò‘-{-9=ÊÁ3½È…œT÷Y²ùRaûÜífG¯ÔÀAxûDÒ.;Ï]TŽkÇb¬yØ J!®v¾%ìŒçº—k@σy9ûsA„|Ùåà²_Ð=Ùñ=ßÃõ‡à|Ÿ!„BJ#4ÐðH§q­O)Gb5‹"fhÁÒ–é@ÓjëxÈ@‘÷`¥Zâ~[$œBÅüUúá¨Y‰œ›]–1ŒŒÙuwù¸äY$›½f_úHà¼ý6Ré ¦1>æ;¾'…ÑÊj»BHù¾×ºu&$Ø"  ©I¯{ö™€…zñØÝ«<åßšc}ŒŸPö̘å>ß"zt æŒÞlPågGM%j3ÿ5¡D/Sàø\ãÄGT='‘Ë€ø,cw\_ºpëmÞ€G[oßö2l@r‚èÞFßË{KêPyùÒ„®×D³gø²qTÄñX6s ¬žs'%œ×Ðë)_à Ü©깈ÌÌè ”R8yrÈê‘@Fð`íK¬ÈC¾¥ÏŠX‘³áVi7 2ˆ-Åzó ¶›¢÷³¯çüÄ\ÓF¥8ÂåèâV±3Ë5\÷²ìWõ<´¹ ˆŒ§ÉäZŸï%Sîû !„’Gh AÒáwöŽê1´¡?¡pEÀ½ôyo)áS y`¡?û«x )½.™õèÆ…]ÝÀr›ªç*—\c³¾*a–ÍýŽÑ§éÿ W+çäÒ Û’˜>&½ùßáÊXSáÀŠ!å[žm ü$=b³¬ë¿ÿA/=ZŠyMïWíç:ß=ºý%ÈPÊl·(÷ýÏi˜6ö_øB­gXvßÿ0;Ê@¯¦® ¶Ö¬ùl!ÈU zcA,.%³@ÓS.©·ÒøI¹Ñ‚GÚó3ª1Ë:ŸEQ/õwãbd¯gMÛL$.zjê¬ã»&ÐÓX§.!ÓX“íí è ³GË”cê90Ef*(ÿy _®Â?˜ÐE°}&ÎܹUÈœò;†#øHÒÕ;LÏðÌéáš[¤Ý¤§ZBl1½Ø!Kaû„ฟ‹Ù)ŽðÞc㨼Ë+ã¸Ïú—ý¡òI´s¢ÄñÿRh¿;‡ã8gmoo+õw&LOxûGÙo²ÅÜÓE_Ëó](¦?‹ó}†B) çô–Ù)-ñ žŸ½ÒiðÞÔ–>2G±µk·ÝJœÞ.lE-“Yì'zÛYR^pMÇ8ùÒtnÛ‡ôð»ƒÃùãC¨aeÒƒ8ª¾.åhÒ äè ±™&ÊYÞ#8 “rÓVyåºÇù†ê Øâf“‡ë~ Þ2ýåüc%¯_ /v4á^»®É áÕwư¶¹•I Aðë*6d°Þà€Šò¹Â¿éÁ&ƒA^ÁÑÔU3.ƒ’ëÁ „¸¦' =$.`ÓßnÇŸW;Þù;–=z§Ö› 2X¯»Ý|ƒjÊ€—sMy¹Á Gñmo «s×× 6fïÆ"ƒAJïrØ …é}Ó ÊKû:o`8å0è糧F‚å{ŒK®{µõ;ŒÃò ¶ÄÎeà6Mý%É­Ìÿ¯Øàv¡z.ŒÏtOƒ Xb¿Nÿ`}¦ÍÇqøtUAÛxí%=Å7}»)ç70¨F27üç)=Ù¹1Üín§ëþÂtõäÛRxŸGé³_“O–®v+I>Ñë®pÙ/äÎAQ0dð¼ô`¾ÁOý„é\×Åeá`1=ß-Åôk<]EßO!„7Î@!„\WÄñ{ Øãs&—=¡|øl¿HØn†ë}?Ç¥ÿV±“B!·4 4I§ —Ý"3#„É£zùãÀeÏõ®×ÅÍfÏ/‘ü×3Ã{ƒI¶!„BÈ/!„B!„îé- !„B!„BÊ„B!„B!„Ä „B!„B‰ È/{3æÚMB!„B!ñ1är<ÑY·ºvë9ó“Ç·à­Ï¬ü…Õh”ùóßÞ†wO‰¬t>……¾ ÊOì•wàŸŸÙ7ÿ³WùXäëxv3j½i§£‡VàBY÷ÏIú,=yr¿=oþ<ÕùòbzBp̳]rì¼úv½då ÷ɤ²su_;2ùCè³óºçæù/ 4<ˆÀê¹yþƒs«™¦OðÛÀQžB!„B~ Íh¸-˜U=ŠôiµÑ¼Ïkçqd3ˆ¶`ëÍdÀܹa2»W(zúûç eã»/ Wù¸äÂÎ[¹, ÃŒî¬\ÊÖ³™×r²Á‘VÔ¯]§K SÖnDíH¿jÁ …òh=…¬Cb)lùí8båon~µ`ßä;õåðÞ)ìÈ &t?wDÉŽàŒÝ.‡k¶‡B!„B~†„î]¿Û$k!±/H¦Ât,üÃKx¢q7þGu[Óº‘é7›SfT IÓƒÞù¶^œü–Î?G”Iîd ªæöè—­ÇÏ:üÓ àêY X(š^AªcpWÊl{¸äY õÔwPí%‹j3&¼ ‰½ø{ÿ˜]$;Â+ €ðëñ2!"l—m/xË3øuV&Y/÷`¥·ïÉ忀 "Ù•ÇA‚:%{|ûbµ$ѾÄÈ$Ó!ûi„dtªFRHæ‚)[â§Kê°^—W˃UV˜£ÀWy¿ÜÖ-Ç®ïl6òÎ:k›µKÞ,O!„B!·¡†ï÷lÃÖ·*Gz¶êì…°¿h–‚ Fh§²sƒ•…3完|§ÖŒ³[3Òkzí{6¡ïÐj0ÙÚÉïÚä,—ÜP‰Ú ÖÁ~ö#jš…ÛÆÒHl´r)[f îÔ:6£vä›é Ž_ÛŠ«‡6a\—ñpÉ…0=’aQA›éÐwyu ëp{Se60Q±q3ýÛm¶Cx¦Fß!I‰àó7sY,[?V>¹ÜÊr÷Þ÷¦2êH6?öø³ªp7út6ÂŽƒãH>Vgåá”›¹°¦c&~ÜiŽÙqpØJsäÛã*¿¦c¾™–Ë^6v&ov ÓØ€ªžãH]®ÀI JèO; ËB!„BÈ­‚ûÓ‰Æj ýƒYϤ‹d1ôáÝ.ëPªezQ =Øs”“¾S¿'÷'+Sôl V)Gz íŽò±È ?IПBÔW£¢²SOxŽ÷j×z|z4/á‚'—óSrói„,Èá’ õè 4™à†,!™Xð&*ú•Ý6Â#/S£Lö‹ 0ýæ+™‡›‘¼8ˆî¯í¶f8÷ÙÂ{§pæŽiñõþ«úfŸÎ¯ÏG¾=®ò"G–y ¾L‡Œ— qy_¾§E@ó4$Nå²$â>/B!„B¹„é4Nc¢õ)“ŸX¤þš´øRøü¸ P8øÇÙ!TÌ_¥tíøJæ€ÙeÃè@.ÍßU>.y>ã'¬§=˜ÆøX¯)+ô¤0ZY]¾žÉ8ôâô±bãÔÏèà Ž4oÀ£­ÈÍñqôuŸ(ímHíXqI*/_ |Vq=)jŸÓ6ëÂ[ž;gwB!„BÈÏ“Ð@ƒd'ììÕ3GhçQBQêØ -x¤ýd¼lˆ0ÄaGÖ¯x f,—¡ݸ°«XnÇkp•K`K›Ì§ý?ájåœì˜ºÇ,]¾ž<*æ5¡~-Ù}]ŸhùYž\Ö½úüiOŸ’zS[úÈÅÖ®Á€<Óû†-AØ´‹Z&³$ØO&ôv Îïþ2õ!å…0=BYr÷@2 Ù),ÿ4–Úá÷ÛVªžýY¤LÓ[ ~y„¿= ½ïH»ÏvïÂué쇑ôcðP_dzK¹OVêjGñmo «eÇcÏ*:Zm ¬*2Nƒ8Öë©©¬#?·s1æ<‚¼Ï ,k¶/Æl».Ÿx™F‡œñ8RÇÆ‘œn¦™ ”ì1.¹Ô»LßÐÃ8,c%Øé*]ö¸ÊËçÁO&ŽãS´˜i1{f`ýcÀ§Ï ùr ð×RmžýBÄ”›„B!„r“â 42éÈàˆÊßs³|np³ÙC!„B!7! 4B!„B!$6ܳNB!„B!„”  „B!„B‰ !„B!„ 4B!„B!$&€ÿåwk‘cÛ @IEND®B`‚cpptrace-1.0.4/res/snippets.png000066400000000000000000001716101504061443700164730ustar00rootroot00000000000000‰PNG  IHDRþLì˜sRGB®ÎégAMA± üa pHYsÃÃÇo¨dóIDATx^ìÝ}\Teþøÿ—: Îp§  € ¨xy›7©K¥–nÚªe›mí']3s+w¿´ù-í'ý,~7¶l·Ö̵¾Õ¦›™fnÞ”›¦–æ ššwÈ‚ÊÈ 3º¿?憙Ãs`ÆßÏǃGq®÷¯›sç\s]×i×§ÿÿ „B!„Bˆ[Z{å!„B!„BÜz¤ƒ@!„B!„ÒA „B!„Bé B!„B!„tˆ*$•9 ç3:D™ „hL÷i Yœ™Eúì;”I­‹œãB!„7µv·ì[ âgòܯnƒœ×yqM®2Ux›¦7¤Í&9 ŸÏ^]ÎŽ e„>–ʜ̉Ä8o*ØLÆòlç-6jb›¯û„¹LK¨VëØV¸5Û£x`á“$òù,ý5v8'·"š˜ÉÌ™=š(òùlékì¨TF!„BˆÖÌ»#÷Ë4ž{!‹Å™Y,ÎÌ$}Á,îM WFÐgZϽ0—ÑÊ„6 -—­9FÏ~ŒdÃe½×º;¤ÝÔ“:kžÈ) ™5¶‡Kç€{¹ì=Q€éÄw?J瀧ml)ÜÈŠ÷R¥#õ¡Te²B!„hå¼×A’Êœ…O2¦WÇý®]xFþb†ÛËðð Zrs›Ð–˦–~Ì|RcµTí[ÅÚ“­·si·f¹yê,›éid¤§‘‘¾™Be² 5±Í‘ÂÉaÀeŽmXjûw¬?®£¬òÖ-!#=Ì·¿Q&ÝjÚØrrës.£Ǽ1¡Êd!„BÑŠy­ƒ`èôqÄhÁTð%«^ÌpÜì¾ô9Vf¢u? ßéÇä±qhMß³åc™Ê!„UzPð5«÷)oz§ÖmãŒYKÔØÉôQ& !„BˆVËkkŒž›ÅøØËú‹Y{R™êdÌ|ß§Üê¢J±.@@üÜ7a4IQaXGãš©*;ÍîßaGaý®Mç!Ü7}bð†›(>º‘ek¾« j` ‚Ñs3«Å\°™Ejç«.›užsHÎë¼ø±Ž{9•¡±AhSÑ^Ö¯ü€jê>ë“z°8ãYîíÛÖWvf7ßßBžÓ¿ßšasI¿¿Æ}Kyy›![;TnÍäX¯ŒOÐa®ØË»Ksðë™ ×a*ØL¦s[$0î¡i °æ·‰z8m>©Éq„ÚbMEGØòþا:¨n75Ôµ1*ÛÂÛmÜ}F³R`ÿß—“7lf]¼é‡6½ÃÚýÖ@ŸÖ™•§eS{^XÙÖðh]5±žòdŸjÖAhÆqæãó"rÚBžlà̆çykOCí#f/bbn]ÊŠí¶ãK!„Bü(¼6‚ÀXc‚Hš0™îÊÔ–H྇¦k¿ùÐbïÃøÙ³ªõªI|§3ƒAö‡ ­Ž¨”{y Þ%´žd4¿s 4†ÑÌYøKFÚnètÑCyð‘áNQ¾«‡Ñs3™šb{´Å…÷º“Y fÒÓ³QÅ ˜‹–rNîrÓ9àDû\Æ'èІöf²­s@{G]ž5½y`u*‹#¿özxr!$ºfxÄÜL¦¶?YcuÑ·1uº»I/¾ãY«k ßµ±Ž¤G¸Æëº’<ý1Æé¡>âyÙÔ?ªø™<—i_ŸÅöà;Ñö»ígáLå§Tñô8»çEÉ®ãT ¥[òe’“T$è1·S& !„Bˆ¬CçˆèÿQnlŽ’¼+ÄêK§°8Nbçë\Ê/¢¼öºk`þ²¿ØFöÛ°$ާGp>Ÿ¥ÿ‘·mÛ²¿ØÆÎ#Îß"…Ò»e;6ðÚÛ«Èþb;¾-À¯ÇbB:xeû„˜öĺû[§:|ðÆ[¬Ý´…ÝûŠ0†Ó¾ð§ì«j‡&3jp$œÿŽG*è9#ƒŸÂT°™ÿmnç€ê²Å3ø®D:…F|½”CÿZÁkï®gGކÞCâ îÜ‘š/öp|Vú1óyxp׋vðþë+Y»i ;¾- }\q1tiÿ {O×:r¬N£&ÜN—Úã|¾å0ÖeÖlí¬ÓQ•³’?옞átìÅ[ÿÄ»—ú1,:„Ú²mìˇ>?ønZÌeÙôÖßxïc§üëéÒ¥=Ù{NÙvžÊÄ©}.ÛÍYÆÇ[>%û‹m›¢‰ ¾À· ¬aªÛM ÏÛX][ø¦CúePd ZmL_òî_–ñÑWGÐ$!΂ߕlöæ_÷qy^6uç…3k»_:IöwyÊD5±°ëþÊíÎjKÈþú°í—<ö9êÓBb£yðü8»aç…Ñø‘ÉtÑÕ:•I)¿øÑ$†Z(Þ³ž½§«”B!„âòÚ*¿á­Åi¬ß—O•9ˆ˜”)<–¾„ôù3ع%_ãåòÑò×X»ëˆc‹Åxœ-‡lßH;ï:y4‰0m&sù~¨´Ž©­©<Âî÷Þd˧X-=§-ä±kç€ËPöÅœÏgK³Xk›‹l¹°…ÃÅÖ¤ºâù¦öCk>Í–åqãq¶­Ì¦ˆŠO© V-ŽPPU„ý‘½A¦ïÙ²î85öÕ**¾cÙö JÊœR¯ói¶¼ºŠ½ÅNù]¾†c& º‡Ó‚˜&,f ¤#zÕ-–öîwøëÛ_;~¿!Øù=ÅFQ}G0ùW‹IŸ{s¿£½Õµ±©)›šóBÔ¹aç…çM)„B!ZŸuØýðñw”I6B¢”Ûê ÛPutëPÜ­ùÊPÇpôαõ‡L7,Ÿµ¯o¦Ð¤#æžùõ¹k™ÆË¦†oê!cuxÿÎûtþÉZ¯ü ùTTúP(“šÅ64:ªwýÅç4)$FiÁ\ÆYåT’š"oz‡eYé¼ùû+ÌèbSy8Yçà½v󜺶h=mlç½:ó¼lêÎ ¡àóó¢7‘ ªÂe$”B!„h½¼ÔAp/œË}ÃúÑY_÷ä¦ïÜq¿aÆ\Q~i¥Ñ„‘4Áu…mg-`ÆXf[8 è3r&sF¹B|è4Å€®ï žž1œn¶¼hô½ñÈ,îmè-•Ù¬xu3…æ ’ÿ«eÓ!ì<)›¾ª‡å ëÏ}s'3 ÆÛßôç’Wf]4=U=X4äšAÛƒ{çN¦OˆuH´Fß›{çN#IæÂƒìµ‡Ç?Èœ¹3‘îØƒÅ˜Ã©b CïfE~o·›ªÚ¢Õ´±êLEÙTÂêFQýˆ1€¹ì´2ÅňًXœ¹¨UàB!„¸U´ëÓÈ”Õsó¾ngæsl_þ Ûl‹eÙi†Í%ýþõ†;¿[;rROª?GÚ®pk+¶×ý2f>OßWoŸp™C_ÌZû·Ëñ3yîW·Ó¿¥IœÉoþë6 æ|>[úZ½¹¼jxR6ÏÞ…nå³zÐôæ´Ù$7ðܨܯZöz0î[ÊËëêwÕkÛ»×õdûÝ‘Tæ,˜HLý‚)ŸÏ^uj7Û¾ÝÍ|š‹—×[ËÀ³vSÃó6VÛ¾hãî32˜•Tïߪ×.N¼_gž—MÕya+CC\ò«&¶Y<8.TåÁƒýÙÝ ó"rÚBžlà̆çykO½EClœþvTì%#ëe€B!„¸¼4‚ ›Õÿïc”c2×m5›/Svb«^}­^ç€eÏrÞÚúeÎR(Ù”Åúœsuû5›©ÈÝÁªâî…X•Û_cÙ‡{)¬0ãØ«¹œÂ}[ê˜`9¹Š[ó1kãÿëùŒQFxΓ²©á³z°gíÒ×Ù~¢”*ïdÕ…eÏN™ 4yrýiÍQ™Í[¯®aA¹S¹L”ø’7³:gVñÁW?Pæ\0óekì’úAø ÝTQÙ­§½_gž–Míy!nÐy¡Iáξa`:ή;²9œkLòæ„B!Dsxi ó,OßÓ•šœ•¼¸æ¸2YÑÆtŸ–Á¬Á: ?YŠ]ÒU#„Bq³ðÒ!V¹ý²‹ÌRôò"BˆÖF“8“a.Ø&B!„7é 7ÄŽ÷¶Y|d>÷ÆH'm‘¦s*?r†ªƒ¬^ÙÄZB!„¢Õ‘qcTf³béf k,«›“,„¸YY,@M>Ÿ½¾ŠSrš !„BÜtd !„B!„BÈ!„B!„BHB!„B!¤ƒ@!„B!„HB!„B!!„B!„B Boè>m!‹3³HŸ}‡2é¦7rHkfÅð þD+½äO†ò˜N¹Õª±4qk {¢7³— ä ʤ¦5äA!„Þ'¢•éæ'o•ö9Ý~N‚í§ópe\Û£™²á&-kC{… K¸ÑÊähdŸÞx¤kfÅ:~þ”¨ŒRglBпnŒS&ÚüÉéß[3+–5ý•! Zø` Ýý@ÛA™Á:´~&N»5: ü~ÞƒÙËú2Æ©Ÿ)ì©$f/ȤéΑ7wek®¤Ä@üc:ãÉáöóX¦ÿ%…ÙË:~õ98‚Ë2ã)ÏY𑇿ðf‰–iËm¡ªlÓãÛÄõH!Ó®Oÿ!ÿQnl‰ž32x,öYë¸wA#’±d•2”3žåÞ¾]ÑiÌTäî`ý{[È«QFúH‡Ÿà÷ÓÙDôŠÄOP‹)'Å«ÿ¨ŒTGÍ~[C,ØÌŸ!,©'º?¬Ï*ä.y\§¢›ô{„¡5æûêo•uÔÄ&|@Ì °z=W—³ïæÂ7S1üzázE¢3å¿ö$úŸN ´‹­ÆºÉb,¡âß+©:üïº8µ‚ŸD?ea]õh€ë#Õ'>¥ôãו‘6MׯfÊbûêmeuúhc<ªß4:/œ@r3nê«™ºO[ȬÁa˜N¬#ómO3ï])CÂIO©ÿ·½€ßTnõÜÈ!<“âÏ•Óçùý¿k)RØ:º;o(>ÏŒÍW·¸õØÄ®LŒ²ppãy–”)S­Ãõ,F`ñy^Ø|•SÊ€ Ï´4&'W±ûËÙ¡Lô¢¡K’ÂYV.¬«Œ°§’˜Ú/€â/°éC—𛊻²5Wؽ™šHõ™“¬Î2*“]?Þ‹†tTn®«ÏÁ̘ÕŽeÍßš>fíÔ䡹¼Yg¾õD/RûÖ°÷™Zp‰¹)4Ö­¡Z’‡ÆÊVÏôxfßrÓ_nJÝï@ûKètG Cüh\³©>ú)e›º?óŒªýúêÞSˆDùÖbQ¡APUaû-}PQÿ–zôÜL¦¦Ø;´„&Üɬ3éi;™|k3Ÿ£[_û4€º¸Tºÿú%×PUÔì·5ÄZvƒž|ƒØ}Ñ;^Ý›ELJ7÷ô<¢z„áh:wÔÄ:ÌBß7Œö¹¸õEr—ÜíøñøÙYÇ4:ÏšFD׺ 4€FIøOŸ£óçhϧÑyŽu¿öݶ×èÑ÷Fì£iŠ`õ«F³ê×wòÖ-!#=íGë˜ÒG\åȶ³Ìx³ÀñӒ΀]ß•2ãÍBo sàwNÿ^ž2±щ¡ÜÕžÊï/4Ø9P]fdõ±«øE…3¯O;eò Áù$ò…;º’ ¥‡.(Sn~^.[ùÇY9ï€Gæ))jNl8ÌÊy?-}¸Q“‡fñrù‚>¬#î†þ´5M´Ek¨‡f硉²‰V@ÕýNa?ŠÁöÐA£Ç<؇ŸTĪ¡b¿¾º÷âòrACÁd,·ýG¨Ì“K”~Ì|Rcµ`Êgûÿ[DFzüû—šÃmLžä«™¾uÚ ŸG—®~\¯) xÍo­¢«²¹\íõ)DN¯üˆGÔì·5ÄÂtÓ¦Ñ9ÌRôáóN殣üïù9]âôt°”SöÉNª\R]©‰­†68·“K>S&ë©zµ®ÓÀågë),@MÁNGtû;†¤šÿ"ÿ¯3­q/ÿ7…{K°àGPßæµ±ÿ¤ Žý:þý7ÿÅE#hº¦ä2ÞÖóúU£9õ[stYýzk þfÔÑ(®dQÁueR«4{ˆ¿êKløöš2©ž¯vçäÕöt¨çneb‘0"Œ@óE¾ßàÕAm­ÂY6ƒ(,aûgeR«öcÖ™pÕ–Û¢-—­­P{¿c½×úoÛ}ΜÙCµ4qC•¡ªxº__Ý{ q#yiŠA*s2'£Ü¬T°™ŒåÙÖiá—9ô÷Ŭ=ã”?“ç~u†ŠÝŽ) ¾‘Bàœ—ˆìd¤lÕýT8%žDôÌ*÷’ûúB`<Ÿ|Ž.!P•ýß”}ó]]lÂKDÏH!À1ÔIÍ~[C,øWºMïKûsŸRðn–S°ÿJdÒyJVÿ±nØzCüÔÄ:ØâÎ}JnSyqao£r—<âØÚaÒâ’õ\Ú:“‹Î×…¿@—y£È_GþjÅÐ0Û°°†§Bx#5ˆêœ³<ñg)C"HOñãdöY~Ÿ«LU' þî›0š¤¨0´¶)^Ue§Ùýá;ì(´=HŽ™Ïâ{âŸtU•ó:/®iafº„0ùñèOüÀêW\;”­S 4ämøž#]º3fD€êËÛ”ÇÎìú,}Òz1(¾#ÖYòª/V²m!?²؆Õ¿<ĉÄ$ÆÆh±\:ËÆE—鑞HJ'-W sùÇ׃"êdÛïU.ž)â«7*)¿ì檑²LZ6½mxòóI Š @ÃuªKŠÈ~¥Œb§}OZ6(çæ²R‘G¥IËÕX\S `lŒ†«…gøÇ’JÇvOó`m·ë|ÿæiЇÇÕÕ[#íæÐD¨gèÏ»Ò/2Ð6’ë*¥GòØø·jGˆ5pìÍäïΘFòàq¬mˆycªu©¦; fø¤H"±® áæøuæA=ØyõømF= 2qOÄ3´o!ÚöÀuªKŠÙùÏRòí£Âš™‡†ÊæÄ5`á:Ú»bÐdÙšyÝiòzÖ\;Ÿ*c˜½ˆ‰ P¸u)+¶»¹Pu¿ãžîñωêtŠÜ¬¹Ê$ð$ p·ßfÝ{ ÑÊxy'î &¨8âÒ95œîïm½ñ öñBgã ìTs}€ŽxÃ=Iø„t±müŒ+«³©²€aô³u±¦4)…Œ”mzѶQÍ~[C,húÆâG9·6þð À§mhxûÑŽÅmóä»Np,N˜°ðsÜM‹p6p*B öÄ'.›¯í=F5|Ï„Oz†SèôEGŒ”­¸@'¼DÔî¦LÀkÒÓN\«­ûœ]A µ€pÇ&Uõ €wÿžèdkç@û€HÂú{§<¨¨ßVEGÒ# \§麒<ý1Æ5¶®DCÂô¼å´0`w€¨.®‹>¨g¤ÓGæÝÝ…Û¢lèØ)˜q÷…0Å)Îׯ%èñÃÄ‘<ëÈù®”RÚ—à§LR)ûšBr¬½s@‹!¼ãgÏf¨g¸|"¨æägîoÒá?tJíÇ${ç@`It§_Ýå €QKr‡ãf@C`§ÎÜ1·7£ €êRz06ÆZ šàpîHïAJ'ëïþ1ÑŒ\›¸pã7ÿþtŠgêÂÂë/}áÐtÙÀ/¨3“þ2a‘¶kO{#»1á‰f¼!À¶è }1Â(€˜— gÿ9Bù)ÝžímëÈuéPÏ„Ÿ'¹Ö[íæ¬É:Áô‰¤8lü‰èçÒfVè6½/÷ÔËCO†·(VOÛxøÏHrtPwüΊ%Nyœ©¨_¿žR“‡„…—âx0·ÖWWÆMv l¦ÊÖïye@ÓÀm³š²©¹î¨¹ž©¢âØQw^¨‰He@‚Ðs{Ë·ä~ÇÚŸ¼EX¨9ánd*žåA©‘ýª¾÷¢êÐ9"ú”ÕËcßÛÈþVÇ€Ñq”oMãÏom#»,šý#(±ÿþ]D aìí1èÊ[H`Ü/çó‹{Ò¥£ýnµ=—r·óCKîKqAƒº¢½ø=9»Àÿ§èf¼H·Ôè;`}t耥à}j/WwPsý'è{t%¤W —ì@3y Q]ý0î}žŠ}¶îl5ûõo±—ÀïöÙ˸b¾—N÷Í£ó]ÿEØèG :žëWÊ©-mhFõ(G÷Ä¿ö<{ºèÚ5<Cr¤Ó(7Ü}Îa<vA%ä¯Xäštå ŒEIøõŒGßµ7!·O ¤O,ÚšJÖþž+νÄS1v .ËÔ¯SzÇð¡„Ž~”ÐÑb:¿.1Tߥ VÁvÞ±ì/,$Þ•Hð¥“ÖóZ!¤ÿXE¢ÕvÀTð%ïþe}uMââ !ø]Éfo¾çËèü˜’¤³vz5¤ÖÄ·Gj)´ý:<®e.ðrv+÷]â×獵{p†K—ùÜéøü€5ý×:(˜ã><éþgã<ˆ:$„ÈkF>Ù_K¾2±CûåWK‘S™Ô ¥wÿÊvl൷W‘ýÅ6v|[€_Ä„t&ðÊ6öù{lm» KâxzçóYúyÛÑæÛØyÄóoZsûÏcétñ,Ÿ|X…Zݰp’"üñèÀÕ’|6½t†¯?(áêàpbô:4J8ù½56êÙ¾ŒìªáêÅsd¿•KöÛÅìÿ®‚êAÄëïRÃþ¯j º#ýèGÕ‘£¬>®e`œžŽP¼ã{¶Vè©ÃRYÂÉ£à7=žI·b.ÉgÓ_óøúŸNû%,ð<ÇmyPj¬l½&EÜÃõ*Žýëÿµˆý'jˆJp§vTnªÄ^Ë'7•°ßö5) Ãå ö­øvÔV¶FßI`®bÿgW¬ÿo‹§¬Œ#{¯þTo~šèö›L<̓£ÝüÐjÛS]x†-Écçz÷í¦ÔTz¶1Z¨.<Ãæ¿åóõ?‹ÉɹÄå.®\æ|±5®îرæaó+y|ýA ƒéÑ)`ýe}cV{´ÒQþªä(ºWòÕ¼c|îT/GöºžûjÚ8r¸ŽŠ=y|œUhÝßwXú„Ò5XŸ¶„ÓGëöëi=øäøUYjó0òá®.žemÖiv`Ý_¡®á5üð­ƒ^e”,›íÍ?í§ƒK¥l{û$Ùo³S ÇÂ:’ãñŒõÚ€š²©¼îx|=kOÏ •±uòð‹Mb¨…â=ëÙ{º© MÝï`½Ÿûõ«t»ëQBGÜMpŒŽšýoP²©¡”žæÁÃýªº÷¢urßÚ\ ¡è¹L…í¢‰@ÇeÊœïdths œ‘FúóO2&!0Qœ³†Í'¬=¹>0¶T€u‘K øMy˜ßZþkwql±³ÅÖ¾½øº1%–„@ y9÷_-Ìå‹Ê)³Ø¬îQ81ÞÌÙ™YJþQÛì¹óWùaÉQNT‘¡®uW]Êö·®rÍ~Ïz±ˆMÿ¼Æ¥‹®âCИ/²3«œ²³Nû}¥ˆb ¢{C`<,æJ¾ZzŠöuNVr¢ úÃr_)kœ#,Æ6 ÀiÛÊÿSªüáOôfJ¿†;šãja.«—Ô ³v×n.šª³Ô®$‚¥ä «—T:ÚãÚY'_)äÐ>å¬ÃÌW/©t;ùo”P øw©ßnjbUñ°÷.:ÃξÑ>•C¶:s95UÔƒÏ_¨ÍÃU3Lÿ”ºR—}xŽ^ièáM¥&Ê–Ò7¸ÌîWΑ¨n&®ÉT¿#[mÙ<½î¨¾žyJűc§æ¼P °{åód¤?ϲOZ¸¥üКGÔ¤©Ê‡æå¡‘ýzzï)D+å•‚î32Xœ™Åâ‡ú£#ˆäÿÊbqf¿1èWY,ÎÌàx Æ„ÐöšÈÔ”t@Uî—¼¹èy–­ù¬c±|ºœRõ¤Õô˜@·¾‘h«ùÙä.™hkàþ;É«kVrÁ†¾= °ÔŸS®f¿­!ÖÉÕüœ}Ƕ áË/rîh9ü:OÚŠŒ§ãðžh(áìº7•‰Ž· „wòãê‰uäýõŠó\'ŒÎŠ•dÛuµ®Œ{åhÓÃØ®ó8ç²OQ]ƒãâo©, l[F€+öE:먩ßÚóŠÞèk§¹Ú¢…³¸àÜáòòS°a/F‹}ÑÊŸ(?à3%GèȹA‚Ãøßû#ù‡ó4„1ÍÆíê—©nøË0UâïåÑ<Ÿi½V/ÎÌjr½_é—Ò Mu9G>V¦¸*?Ùô«A ”\"×͈áüó €§a®Ug.Pì[UâþD‹èh;qçKNÃô— döKñÖ!üž–’rN*Þœ\r€•óΨ~e[suèÖ{R±”œñZç¶›³¦ê̯[þ@ÙQ÷vîÏ+F8˜.QY î–CW«Š‡mÜap©‹“ø¥óqæfž½šzðùñëµyØùåEªµ’îÀìeIL~6œ¸ÁÞ{‹KSeÓÕUqšBÞµeóôº£özæ)5ÇŽšóBM¬o8-hýâoÉߚà è’g»ýRÆsîWާ­•W:T)®°>D¦²ƒ¬y!/®´}£t6TPâ¼x¡·•–;: Ìs8ûÆLÎ9æ7ý„Ê]çðè{à`û›?¨jöÛb)纨9ʹÕ ¶(Çvõ3®~ü>•FÀß}o«`=`ɯ{s3ÇÛŽ.£xÝë\¿ò ¦Õ÷S°·„ëø4ô!Gl»nÚ³×¾™KÉһɳ=t¼þ8U¥Öoxj.³EµÒú½ú–c )ÝQ@û€0eD›®çÅŸEØÉ¯ñi 7J‡ö¨}_‹7î¯4‰3yúWw’ä•ýµHßp’"¡òäyë7Ý>w½î[;_»áek™kgÏ“{ 4‘ݘ0]õ¸ïð Îüšó*»›Åð¦ÏЧG'û: óy=xоdú¸€Õó°uw)¥—4DôêÆ¸Y·ñ‹…^XƒÀƒ²Õ½‚»5kÞõÌçÇNkr-‡k~Ëù­§0ã‡ì(eDó4²_5÷žB´V^é È[³˜Œô4Þ̹ äóYzéi|VT$#=Œtû ²)®(çØû«8pÁi¬@Ôƒ$†Eì®Ûê{1U”S±á·Ô–;uGL (¨Tv!`J*A#e[÷RM¤›áÙjöÛbÿ剾ޚU¡ÖÅøZ-ûèr.~înÁ—©øu(çÒn×oä¯nû¦ßi±Æë\ºÎrUa?I"#U‡í‹%z©~#†¢×C ‡ÔÓ¡“í1Ù1%¢m—Bÿ¡òD ™o;MCØî¥!«*TVÁZÔ~ T™[ô­òÀ±ý0UG×ٮͶŸ­M­†` Äeùú–‹N—9¶Á;cÆ®šÈ`Ü,º×EfÅÍXù»Ò˜KÙä êŸ=”P øÐ6åG¼ìßÔä–[‡ûÌ\N@„mÕùˆ4:ÍŠ0{ßåšI¿'²«5GߥêÀB.2‚>…è)ÎsÔì·5ÄÂÕc§¸Fa÷/ÁϾú~Ç©è½Ð¨-ÚëˆmUl£®åïäŠÛé´§¹v ÈgèÐÑvAö‚öîëêºÎ@ÇNS øõšF—)Nñ§0m9š"ßq$š¤{rÑ]ý°œÛé2ò¤9õÛ^_·î~»ègžak· -°£RÇñøÝý‘ÉaÖ?b{]ßþÐV…øµþÕr 9¶¡úcõ¼<°þ Ÿ¯-¿þôSùeXt Ԗ׺¬« V€F ˜1–Õ¨}FÎdÎ(å­pJ£ #i‚wŸûÅûc9SêÑ0^Oäžµ€¶£ÒÂïfŽÜÅŸäçûÓ+®ž-Wµ(¤ÝÙ³Õ  uaÝ’=æìí²Ýy—øti.Åæ@’íÍpï6w“<ª³•”½zð³§ôÛïµ»ø“øl Én†\wÐÕ}cê7<„ w#(=Z±05±F“èHÂ$=ú eªz~ `Áx±£m͆ðéLîfç*êÁ×ǯ'õ *ƒÃ™ô|‰Ã¾é>oâìE  E×É9ØÊ“<ØyR¶³ç­×’áOYïk;ôÕ3j±õÕŸJªÊ¦Bs¯gÉ‹û3{Y 74ÚBűc§æ¼P‹íƒ‹31gL -Ñ!…ö‰ÿ§¤àO-Æ£îï£Tç¡Áýª¼÷¢•j×§ÿõa0znãC’±dÀ Ÿ$¹ÂÍ{Ñ5½y m6Én¦þš 6³Hï ¦b˜7p7Ïå;ë^"zF Îï]í0• yó謯åò¶…\øÎ6|\Í~[C,S1üÚ}ìõšò–>^·aø[$¤6ô¤ 8ךXÛûmÏ}J®r}ãéøäst )§ôÍ1ºí .'öžž ÕT´Ð~ø[tKuo¤lÕýuþ±/=3ûl;ËŽ¬X¨Øêyýj¦l ¶¯›@wí¦¦~Œ­åröó\øÆi¡M5ÆÌotÞzUÎ뼸&lë”ÌJ ¢pk+¶;ÙöáÛ\šK÷âóÌØ\Eu€¾ÉùÃPwߘühGÞö~gÿj>1´Ñµ ®;Çã»l½ jbmBx#5ˆêœ³<ñgßk¤ ‰ =Å“Ùgù} ª*rROª7¤Å¡^šasI¿¿G½) -i7¿Ÿ÷àÑÑþœX}”í_+SëXß­Pÿ½ã¶w »¼ç¼{0“$¥Ì(€¹‚¯–æYçÛÞGŽý³Ê}Ù~wþ7G-HR÷ÛʼyZ6€IËU˜Ëʦæý7ñÎ÷†Þ÷Þäþ•uu¨ûŒG»b0WòÕÒ3usçUäAU»©¬3Ýôx¦ßâæM 5{ó;m‹¬ÙóàŽr!F5±ãcùÅýêåCY¶&ÛÀFÿxOÒðµDY—žÖ><~Áózð8¶cÒmM˜/²í¿ ÈWÎË÷0—Ím,\£¢Î<*›ò\Sž Ê뎧×3g.ù®ä«Ö1ñôØQs^¨‰­“ʜ̉ÄTì%#ëe@#÷06Ýï€éÐ2Š•o€ç¡Þ~UÞ{ ÑyuAˆË•4ŽP€› R–ã¬]ú:ÛsËëRÍÖ·Ü΀kë©Za]8Ä>èéºÅˆñè:×±Žitž–BF.lz±nûµõ\Þ”C ~¥>ã²Ý£ý¶–XÜÇVŸÎvíhMœF4Ø9p`.«²©¼h¤îñ¬–«OQ¼ê×õ.Ð׿yœ³Ÿì¥ª²ÖÝb¤êЧõ×£°¹n±îïü†Ýt ª~¯Îáây#fKÝâ‡×,Fª¹k·æ»n©Åtn/çÞüuó;nBG]àícW¸b±õ‰Z,Tž½Àß·Ub{áÛ S[ÉÉjéÄeb¦$@õ%þݼçq‡’MY¬Ï9‡É~ñ5›©ÈÝÁªÒÐd Ëžå¼µõÊj¹”ä ¸TÖøMºZy—ØôÚIrJªº½ÊÅ3gXŸáæfZ… ðõ™+T››îÐñIÙn¤oJÙøe%Wµ!Œ]ÐÄîÊïSSg¦Ïðц³_2×µ³¹šâœ|—‡bw,æjŠsN6ð°âªÉØÏ ظã«›>&›6œw{nª©Ÿ¿ÖƒÇyØWʶ¸Xí4$Ý\c;Ýtày<.Û¾R6~ZZ·¿ê+œÞqœ¾tÿ×Â㲩ќëÙ¾RŽ]4ש.,wÛ9€ÊcG©ÉóÂIÓ±ÙÎ5&Š5Õ(ê]·Ôb:”âUO4Ð9@³òÐè~UÞ{ ÑyuBÏôìÆFé1»a 4òö.<Ó_C^ö9~ׂVᎮ<üpÆÙøÏ6ö'¨-—ÍW|Tg ŽbpCMl›æ£¶hÚrÙ|DÍy¡&VѺyuBÏœú¡œ¯Š¯Ó1©3 Õ©uÃõ<ÖߟÚâ ü½-t #Â4_äû mï&½-—ÍW¤ÎZ¶Üm¹lBáMÒA „?’•›Ï’wÕŸÛ&tâI7óWõüaB!Uå¬Ü|•SÊ€›TnÖaV>SàöýÞ7»¶\6_‘:k=Úr[´å² !„7IBüˆ~÷^yµ×©vó¶Éjc ÔV±ù_F¾R& !„Báe²B!„B!dB!„B!¤ƒ@!„B!„ÒA „B!„B¤ƒ@!„B!„HB!„B!:tŽˆþåF!„m_÷i ùí/¦0"ÞÄŽýÊd!$ÇŽBÑ6Ék…h̘ù,¾'ŽÂ­i¬Ø®L7›Ñs³ l&cyv½ß›£ûŒ f¥Ý„ÇH,|’d@>Ÿ¥¿ÆeˆnÝœÇN÷ s™:2–P­Ö±íæ;oë(¯_Êß…‡Úòßy·eKeNæDbœãä˜B8‘)>¢Ñ÷fÜ/’ž™Åâ…3•É.ÔÄ õúLKã¹æ2Z™ ÔV문â²íÿÌÔX¬ÿ­ª(ª ŠŸÉs™Y,nô'ƒâë>rsÊeï‰rL'¾»)ð„ï¨;ço¾c'rÊBfíáÒ9p³óèzv‹Pwü !„hŒtx™¦s?îAzúlÆô C§ p¢&V4_xx­F¹Y4¢­Ö™Åö_û tY•ɶÅzc}«É[·„Œô42ßþF™$n1jÏù›ëØIáÎä0à2Ç6,%#=Íñs3c,׳:jß[[6+çÀf •ÉBˆ[žtxÙÀûg22!­¹”Cî¦XàDM¬mÑè¹<=¥·r³[jbd¿£nÌ™U¼èô‘±5lC‘ë,³öŒòƒBˆÖ) ½(øšÕ{Úзëž\ϼDÍõWM¬BˆÖGÖ ð¶a³øMrëßÛB^mžWÕA2–¬RFª‹u2bö"&&@áÖ¥¬Ø^¡L¾% œ6ŸÔä8BµfLEGØòþ\°Ý1ÙæÝ5¦*çu^\“ë²ÍºßhÇ0T³´Ú†ç©ªi Oc5‡pßôq ˆ ÃZ<ÅG7²lÍw® Œ{hCã#ÐÙꡪì4»?|‡…uwŽ Î‘·Õ‘£šYgÍ2„‡5ƒ¤P¨8º†—ßS”Í™šØ¦ØÊXñíR^þ¸ý¤4~7*¢~Ý8s;‡³Žµ~uœÙ°ˆ]á³™<,ƒ0ãЦwX»ß¹­­çyHÎë¼ø±Ž{9•¡±AhSÑ^Ö¯ü€jœÂ=h㡳3™œPÁ®Ì,¶>k§ŸÌÓé£ É]Ç¢•ߨšj-ìÿûrò†ÍäÞ¾]­ùp[6[~™ÁˆÛ±ëâ2‡þÞ¼Ž•€ø;¸oÂh’¢Â°žšõëæäW¥3ž­Û§ÙDÙ™Ýl| yNmv_ZÃBÍoÍb™Óy®IœÉoþë6 Uy'k§œî<>ç=̃½ýý5N {¬ázP}Î{~ì€gÇ/7 Ýùn,¯>87‰ŸÉs¿ºÊ­™ëµ€ñ :Ì{ywi~=“aá:L›Ét“'þ^4çz¦–šë¯šX´å¿ójÎc5ç…Ú²yvnXyZ6!ÄÍOFxÛž7yy¥ëE¾AjbR tÄÜ>N™xK17“©ƒí7 ZtÑ·1uzËfŽ›o߯cǶ‡‘†¨i Ïb5‰òôof0Èþ  Õ•r¯ëœwMoXð$czÙoN´Âû0þÉ…<Øz‡Zjb&3ï×3H 5q擽‰Të‘í¯‘‘žÆË[¿E4nÊòÒ0c Ǧ3s”­s@וäé1N¯4†ÑÌYøKFÚ@tÑCyð‘áNAžµqaY€>¼î£. 5¥ÊéHzdSSl7§4T¶~¶àIƸíh‰î{h ɱöÎêêaöl†Ö;Ô=ͯ:£çfºîS«#¼×ÌZ0“žNyø×ß7SlÖu×cŒ±mÔôægÓoÃÀe}¸ÆµsÀÓs^El‰Ä?”æ¦æó³±~†‡Ço/·›ËZ"¶N؉®k‰¸YóÇ›ç¦þö¹ŒO°N"Ô†öf²­s@{G½vöôï…ï®gVj®¿jb=Ñ–ÿΫ;=?/Ô—M ÏÊ&„hä5‡>Ïà» ®-!ûëÃÊDOcóð‹Mb¨…â=ëÙ{ºJÐÆ¥2qj‚ËvóÆ_–ññ–OÉþbŦh¢‚/ðíAÛë¶ò÷ýÅ6²¿Ø†%q<=‚óù,ý¼mÛ–ýÅ6v©ë×Ox–™B â{6¾½œ÷>ú„ì/¶q8 Ãcƒ¸|jû¬#ͨi Ob˜öĺûƒ©àK>xã-ÖnÚÂî}ECƒi_x€S•ÖÈ>?ønZÌeÙôÖßxïã-ìø¶€öqIÄëéÒ¥=Ù{NÒ,ƒ"ýë—!nwö ¡öüwÖºPYgÍ2è1æ?6”N”²õKüó€}‘­úÔÄú”­žêÕŸµ~õøë´˜Šv°úÕå¬Ë>Œ&qq†ü®d³7ÿº-Úzžw øz)‡þµ‚×Þ]ÏŽ ½‡Äܹ#5_ìᬊ66vº;{u¡¶Ì–¿asyþ©Ét-Ëæðy ïXîéBéþØWÇ>G›ZH¼+‘àK'Éþ.ϵ`޲¢ÕvÀTð%ïþe}uÄ}Ù†ÍàÁ”0®WìeÝÒ×x˧ì-ÐÑ£†ë§ÙøÂËl³®k§R(½ûGP¶c¯½½Šì/¶±ãÛüz &¤3W¶ÙÊ¥2¿*èÇÌçáÁa\/ÚÁû¯¯dí&§¶ˆˆ¡KûoØ{ºÖ\“ÇÉë½Ú;†øx _íÉ£çó˜ëOÙΗøßT;íÙós^MǤ¿µVÿmë¶|JeÔp’ƒÑ•²ãÐùfœóž;ž¿øªÝB“58åvg.o½nÚó¬ÓQ•³’?옞átìÅ[ÿÄ»—ú1,:¤îÜuðäï…o©¹þª‰õLÛý;¯þ<öì¼h^Ù¨»÷là<®ÓtÙ„m‡Œ ¸ í^ù<éϳìÓ64—Òc&,f ¤#z…:¶þ°ëþúö×.‘j¤&wαýõwØ[X÷‡ïBUã“<Õ´E“±É£I4€¹h3™Ë·ðC¥uhIMåv¿÷&[C³S¯ói¶¼ºŠ½ÅÖ8‹ñ8Û–¯á˜ ˆîÑêVsî9%§§÷ÇPõ=ë—fñѱ†‡Î¨‰m-Ì›É|m#§Œ¨)bÛ!k;ë£b•¡`Îç³¥Y¬µÍ‡¶\ØÂaÛ"$Ö/T´qqU€>4€žñáhÑÑ=9ź?¸LE 91Xɼ,[÷˜p´@É·pØh=oŒ'7²ùèeІÓÝe\º¹|´ü5Öî:âØb1g‹-¶ sáI~ÕØ7­ù4[–otœ—ãq¶­Ì¦ˆŠ·Öµ]åöר’kF=Žy3f15%sÁfþºIÑÁæñ9¯>U9+É\¾ÅzLÞÿšB@ãëùá*Ž_'^m7—µDl ±lvY Ðít>ož›v¦ïÙ²î85öE*¾cÙö JÊ~Èjòï…©¹þª‰õ\Ûý;ßœóØ“ó¢¹eS£©² !Úé 7™oX¿é4&mÉÓŸcñ Ì{d*Cã”*¤`ªÊØæn÷ ¢‰@”j| cÁ­âãì­÷·ÿ'‹Í@(‘õ†­þx:Ox–Çn@k>ÍÆ¥ïpÀö­¨;jb[“’£Mµ›“â#ìP”kÇò42ï“WÑÆgЍ4’bƒ(++EÓ€nQ ‚’fÌý·ó¤lyÅÖ‡ßÈÁSé©·>Jé'3±oPƒ±LñâïåÑ<ï<<¼‘¹Çžä×s D†ÚL^¤xÝ墉D"”bïÛïp¨JKTJ æ|¶½W?OžŸóÍËCeñq× –|*ª/O©OÅñëÄ»íÖLÞ<7mªNìà°SlUÁÁº_Z5×_5±ê´Õ¿óÍ;›>/ZCÙ„m‰tˆ›Nåžåd.ZÊ;¿§Ø¨#ªï&ÿj1ésS•¡2Xoø~dz›¯B[ Þ½ë觯ðη¥˜µ=˜œ6—¡ö¹Ùn¨‰½ÕYÛØúÐgýdâCK9ùEU†X~áTUøþ-)»¶q¨ ´á#x,} ‹3³øÝ&J æÜîPô€&q&OÿêNÃëæ„ßôфڟg´:BÜœÞÞ>ço6­éåm7kÙÔ\ÕĪÕVÿÎûF[.›âÇ âæTSÄáMï°,+2ßc…]l*'+í „D)·ÙY¿U 00T‘2º¯uØö`nÚ9¶þCW¶á—Q½ë/ЦI!1J æ2Î6ññÐ^õ¿©pÕX©wêã,þúá÷Téz0yA?KjøÛ 5±m“š6Î¥¤  §û XÂ+NsàÐnÎT…?,šjª°ÎööͰT’ `ª*¯{ »¹œÂ}kloOhžcûaªŽ®sn{ý¤ïåb¬±ÿÀùßwþÉZ¯ü£GŒö2û7즂F>Y2ÏÏùæå¡ž¨!Ä€š††¶{ëœWsüÞlÚrÙ¬Ô\ÕÄªÖæþÎ{é<®§5”MÑ–HÁMhÄìE,Î\Äœ1usónñ2gîLF$Õ-Ùn1æpªØèлYíºÒhÂHšà´ µ‹\òÊÌ íAê ëz@ÔpXÉøØÆ¿³TÓMÆ:M1 ë;ƒ§g §›m˜¶Fß›Ìâ^ÇÕo8\hÍï½s'Ó'ÄzC¦Ñ÷æÞ¹ÓHÒ¹ð {mÑÎC¿»Ø^Ï5w“m«j»Ót5Oåþwxùõ["ô‹tÙÐÒûêbÛum\Yi‚€0ÆôЦªà %äòC¡‰Ð^“I sUsß`๡ƒz åÞ‡//²Þìþa +Öµl5ó0c,³-Lô9“9£žbàm ÊAןûæNf@Œu*GcºOË`|¬–ªœøhÏzÖï» †Û˜:M1÷ßãs^}4†ºsFŸx/sf%(v3dٻ缺ã÷ærcÊÖäß SsýUë‘6üw¾9çqÓš_65<’ÁâÌ,ÒL­×Ù)„h[Úõé?ä?Ê¢šz/oÕÁº…‘ÔÄ:8½‡ºb/Y(ÒÛ8Û{¥ÝþY5Ÿfãâåõæ…j†Í%ýþõ†'»¼9þAžûÕPÅ~Í”(" W•nß!¬¦-<‹ 3Ÿ§ï‰«—×zïIe΂‰ÄÔS>Ÿ½úZÝçŽ]šÍTäî`Õ‡ih ¼×Y޳véël?QŠó¥§ý<þÈm¸Ì¡×X;°~þ£R…–˜ 1Úi®¶Çç¼§yh€Ù>Ý£‡_¯Ÿó*Žß›ŽÏËæÙß‹BÍõWMlcÚòßùžÇ jVÙÔÈeÛWùuÇ»Œ ¢M“B!Zì¾´,†éϱýÕרvÁé> šûž\À°pùÖéFið›G!„h¦nÃfñðý}0PÊ®W³Øâó•o…?A „¢…R‰ Ðî:÷¶sübBÊ)¾‰nBˆ[Q÷Öµž¸¿ bßF颓B!ZÈu~ª;¦£ï‘ù^Žr³ðA „ðëõDGUÙioÝÈ–ceÊ!D#B!Z. qMc`|(­}©03æŠ"ŽíÜÌÚ]¾_AXIB!šK:„B!„B!k!„B!„B:„B!„B!B!„B!„@:„B!„Bt!„B!„é BѦ…¤2gá|F‡(šHkźO[ÈâÌ,Ògß¡Lºñ«ÃÆÒ„BÑ*IBˆ¶IÓ›žœHL Ñ(SÑ hâ¿àfzˆM`h¯0t ·1Z™|#µÉúB!nm:GDÿr£h9¾7w=<‡‡œÂøad}X€¦ó¦<úNÂÝwçÎ1CHìTþcEÊPÑÊô™–Æã%ÓþßßQ L¢­áØi yðµÑs~ËȈ+z÷¶œµ(“¹~ù8ÎE0xpc,|õ]ž2¤ª ºËEë0ø‚UÏ*š¥9ÇCÛ¬_!„âÖ&#¼LÓ¹÷ÎÎ =}6cz…¡S8Ñ$Îä7¿™Á Ø0´öÚ0bÏàùÙÃ]bEëA[ÿ[3!šÒŽÖ_Ò™Oj¬–ª}«X{²þëåä*Öç\F;ŽycB•É­RÞº%d¤§‘ùö7ʤfS{<´åúB!neÒAàeïŸÉÈ„ ´æR}¸›be€KY•U¥ûd%éid¤§ñ·¯ò1Ú„áŒÓ+?!„¢iý˜<6­é{¶|œ«L¬çÔºmœ1k‰;™>ÊDá†Ô¯BÑVµëÓÈ”E ›Åo’‹XÿÞòjR™“9‘˜ªƒd,Y¥Œlн ²~™C_ÌÚ3ÊT1{ pëRVl¯P&ß4‡pßôq °¾0›(>º‘ek¾sÄtŸ‘Á¬8ô÷×85ì1îíÛ0ãЦwX»¿®î<Ž3ŸÅ÷Ä9>çNUÎ뼸Æ~Ól=Br^çÅuÜûË© B ˜Šö²~åüPS÷Ù€ø;¸oÂh’¢ÂÐZ FUÙivø; ëKçI=Ø œñl]¹Ì&ÊÎìfãû[Èsú÷©9Î<Š H`Ü#3‘à4bÆ¡þñîI~íí¶ÿïËÉ6³ávsâÉ~}Ònª+Ïòë!ÕyðA=8ñÕñ«6—ôû{`Ü·”—×y6]+rÚBžlà̆çykOý¼zÊãk ¨¬_Ûß§OS°™ŒåÙÎ[@Íy¡úx°òeýzt-B!„ÏÈoÛó&/¯tÓªŽ…·ûHe@‚Ðsû8eâ-A“ø O×›š¡#*å^ˆw ´Ä?”ÆÔÛ 2€®+ÉÓçó³Ū£1ŒfÎÂ_2Òvó ‹ʃ8O%I྇¦k¸ÐbïÃøÙ³ªý«¦FÏÍt-—VGx¯;™µ`&=ÝŽ*Vsœy›ÀÏ<É·õ©Ë¯Ž¤G¸i·ÇêÂQ·_ß´›jóë+¾¨_¿“cÑRÎÉ]ž=¼”ì:NZº%Q&5ƒºk‰gõ«–çç…Z¾«_O®%B!„ð%Y¤Ð§â|W"Áµ% .RXOÈd&Ž£cÅA–zL™ äá?šÄP Å{Ö³÷t•2 K`ÚSè/ùà·X»i »÷a ¦}áNUZ#CúeP¤ÿ˜ ¾dõßV°n˧TF ')<ˆ]);W›¿‡ì/¶‘ýÅ6,‰ãéœÏgéämÛ¶ì/¶±óˆó·^Öc ShÁ×K9ô¯¼öîzvähè=$žàΩùbÖeÆBéÝ?‚²xííUd±ßà×c1! ¼²}…öýz^ú1óyxp׋vðþë+Y»i ;¾- }\q1tiÿ {O×:rl¥æ8ó vØ L ãzÅ^Ö-}÷·|ÊÞ=úÇa¸~š/¼Ì¶rk¨šüZÛ-­ÖÚnïþe}uMââ !ø]ÉfoþuÕûõI»©Zþkwql±³å­¿)UQûÆ¡5ŸfËòŽ8‹ñ8ÛVfS DŧÔ;Qsœ5Û=&-Pòí¶Õ­ñäF6½ Úpº;›nN~ÍÖzÈ«jŠØf«3}T¬#¦9ûõz»©Ð¬üúŠ·ëÁ§Ço¡ ªˆSŠ”ÆåPa ¡^yu ªk‰Gõ«ž'ç…z¾­ß¦®%B!„ð-é hEFÏ]Äøh(ÜúZ£«BßÊô1耒CõçÝ6¤²ø¸ëK>U€›ÁîjbU)>ÂÛ7¢v;–§‘‘þ;œ¶ÄßË£ 2x>3‹Åö7s„=¯‡"Cm&/rÚgf‹M$ À¡ü×å[¿…Œ<•žzë#>q2û5Ëì‘ÍËoÉQÕƒ—ÛÍsÍ̯¯x¹nÈñÛŒK¨Ñ¤ÜÒ|ª®%Ö¯ZMŸ-ð#ׯB!|C:ZM,÷ÎÏd|¬Ffj‚^Ó’ïÓZ7MâLžþÕ$†×ÍCnÈMW»¶q¨ ´á#x,} ‹3³øÝ&J æÜl1*?póPÓnm™šz¸!ǯΠÜÒ´­6CêW!„h“¤ƒàÇÐüš‘ÑP¸5‹Û_¥ 7JʬsR;Ç*‡«5„PãÁüÖ&c „D)·5ÏÀ±ý0UG×9^{™‘žFÆÖ|e¨ŠzÈÅX˜¾çç}:ÿd­W~Èë4ÃRI2€©ª³}£¹œÂ}kX´Òù]î¾Ê¯¯ö«®Ý\5vìø.¿®˃:jêÁ·Ç¯í›z}()ëM¤¨ªhÑ·ö jòZÒxr<´ÒúB!„WHÁ)d/ø%É¡&[ç€g#FÌ^ÄâÌE̪Ljû¦ÐõÁÓ3†ÓÍ6\]£ï͈Gfq¯ÛÂÃÿ¯O¼—9³‡ »~«&¶ÒhÂHšÐ’•Æëh´€cYc[Ÿ‘3™3ªþm5õp  tý¹oîdÄxþ­Ÿšã¬©Ø¡ƒz åÞ‡//²>Øýa +ÖÕ]sóÛ_íWU»Ùxrìø*¿vžäA UõàÓã7—¼23è¢éÙä~ÄÀ\vZ™â${,bqfé³S•‰.Ô\KZÏ_Ö/ x$ÃZ¿ ¦º}C…B!|«]ŸþCþ£Ü(Z ©÷JW$cÉ*p¼«:HQÇ)¶ŽÓ»°+ö’‘õ"½í 3Ÿ§ï‰s3Œù2‡þ¾˜µ¶Î«_sÁf9½?\M¬ý]àÊ|¸{|Lï+w9)§F50Ÿ(ܚƊíu¿{Zhzó@Úl’x¶Rî×JÍqÖtlŸG1³¯N¹0c®( ûý•ì(´MjV‘_{»Õ+ƒí«_øÙÂ'dkëÂOÒX±K#„B_’7lçšŇ¾V&Þ*·¿Æ²÷RXaV WßR÷Pѳ}X{S7ãÄZö,ç­­?Pfrä¢EJ6e±>çŽÝ™ÍTäî`Õ‡q7(Ùãz°gíÒ×Ù~¢”*³ªæ8k:ö‡ Ùš±v¸Ô—mhÆÏžËhû»Ù›•_øh¿jÛ Oå×Σ<¨ ¶|yüZöìà” B“'3Ô“o¡5)ÜÙ7 LÇÙÕàÃ+pf7‡Ë¬+í™rÔëhHS×’Ö@Íñà³ú%—m_åמì[!„^%#D›Õà·hn¨‰êÝ—–Å0ý9¶¿úÛ.8= Dsß“ .ß ï ó,OßÓ•šœ•¼¸FñF…îÓ2˜5XGá'KX±Ë]w†çn•k‰¯ê·Û°Y<| ”²ëÕ,¶Ø^÷(„BˆCF!|,•¨P-á®ktŽBL@9ÅMŒþBÊí¯]dÆò $6üU´&q&Â\°­É‡WQÇÛõÛ}†uí'îطQ:„BˆŒ m–šoòÔÄ µ\ç»c:ú™ïå(7‹&9­ÿÐ$÷sæÛ´Tæ,˜H çØµò5¶Ø×¹°ÑtNåñ_O$¦æ ïd­âTc£ß=tK]K¼X¿ÖzÓQUvšÃ[7²å˜¼ÑG!„ø1È!„åò‘c¹óüf3æŠ|}òºtߨÌfÅÒÍÖX0VÕ:µX€š|>{½ñ‡WÑ/ÖoÞšÅd¤§óâÒ7¥s@!„øÉ!„B!„BÈ!„B!„BHB!„B!¤ƒ@!„B!„HB!„B!!„B!„B Bá=ݧ-dqfé³ïP&‰KH*sÎgtˆ2AøœÔ½BqÓéÐ9"ú”…h ºÏÈà·3ï#Ѽ}ùÊT¡Ê˜ù,~â©ËF%0îþ1tñm¨Ë{(P†ˆKÓ›ž™A¯€Kœþn?Õ×]’GÏÍâWSÇsg¢…ìïòêýÞîæòø£S™8þî¼ký‡üG¹ñVq÷Î<–ˆ_U%oÿë2[ì_„´Dlo$ÕòÎç5캚K÷ªrþò‘]ÊØÌž˸ðˬ~§’•‰Àc¢™Ø ò¶ó»“·Pó™Ïâ{â”[]Tå¼Î‹k¬Ýgd0+EÇ™ ‹Ø>›ÉÃâ0hÓ9mz‡µû+œ>™ÊœÌ‰„ä¼Î‹ë¸÷—S„0íeýÊøÁyþl@ãšÆÐøtZ3Ue§Ùýá;ì(´ÞAÉä„ vef±ÅèôY;ýdžNMHî:­lÞ7ΧÍ'59ŽP[LEGØòþ؇S«¬3;ë~£èÍfÐj¡p«ûoJGÌ^ÄÄ(ܺ”Ûëµ¾¦c=o kÕÏ—­ÜõØÿ÷åä ›É½}»ZÛNy<¨®3k~cœ 6“±<Ûy ÄÏä¹_ÝFåÖLŽõZÀø抽¼»4‡¿žÉ°p¦‚Íd*>7pƳuy5›(;³›ïo!¯…ó¹=Û¯çm¡.Ö³sÈJå~Ͱ¹¤ßßã¾¥¼¼®‘y[[W|»”—?.B?)ߊ¨<9ØÚÚ]û:ó¸lžÇÚßCÿï5N%Ϭ;ï•ǯM@üÜ7a4IQaXOc÷ûU]¿ªëÌ*rÚBžlà̆çykOý M_„BáK·ô"…¹EվԎὃÖKK÷Òjv¹{SãÒU>9m¡ÐöÜ>nP0!µ&¾=RK¡2Ö±}B¹¯¯ÕÇ/ògûNþI(!´'$ØÂÉcfÎ+Úª¸aÜÙ³ñå°kÏÇÎ#Ö›ÊþcÙM—Ñ éÕûLm]ú& ùörkퟌgð]‰è«u$MOÿ0ᆮ$ÅTñÕþ³Ö šÞ<ðÛ'Ý­=ˆøwìL¡Ãè\°‹£å×¹3ŒaÝü¨øa;G+íqNºaìàH,¹_±ó˜úás3™œ†Î)ZC$I]kë SYgãægrOï0t;Æþ¿—O¹[ -•‰ÓûŒ–àNdï:¢ pâI¬çmamcÿúù²•»þñ #¤ï’»…ÔµòxP]gÖü;\:YoÁ6B“58M§~ô ƒÎ@LÿÛè®@܅ιuÇËè¹™ü´·S^;héØ)žAƒ"(üæ0å®kîyÌóýzÞªb=<‡Tï×fð½Séz‰Ãë¶p¢±kzþ²¿ØÆîãUÔžÜEöŠc)~&Ï=÷(÷Ü5ž;ííœèXœðλÆsç°²¿>lWS6±ÖãW‹_· êÙ¹î¼×Ñ¥o !¹ÛùÁqIàgO>Br„Îqî:ö{[<Ư¿£¨Ymìa¹a¬Œ`àíqtö»T¯½êxr}B!„/ÝÒå5lË­ehïú$é¸íÒ¾Tÿ¬Ö O:>˯‡óÀ `†Æj(Û[̯¿»¦ sèёĠëœ;XÉ»eî;Ú$ÛiöÛ°$ާGp>Ÿ¥ÿ‘·mÛ²¿Øæò k½¡Öã¯Ób*ÚÁêW—³.û0šÄ!ÄBð»’ÍÞ|ׇN¡_/åпVðÚ»ëÙ‘£¡÷x‚;w¤æ‹=œú<ü ãºi1—dÓ[ã½·°ãÛÚÇ%¬§K—ödï9…±ÓmÜÙ« µe¶›çasyþ©Ét-Ëæðy ïXîéBéþØ×ÐÁÑ T&NíCpÙnÞøË2>Þò)Ù_l£ØMTð¾=h[?_eé'<ËÌ!Pñ=ß^Î{}BöÛ8Ðá±AõÄÈÃ/~4‰¡Š÷¬gïi냃{žÄzÞê;Ñj;`*ø’wÿ²Œ¾:RÿxPYgÇ>Gš…Ä» n¤ƒ X§£*g%>Ø…1=ÃéØŠ·þ‰w/õcXtˆãxÑ™ÏÃø^´ƒ÷__ÉÚMNÇYD ]ÚÃÞÓŽ.©Û¯çm¡&ÖÓsH}5ávºÔçó-‡±.Ù¶vóWnwV[âè PS65±öë™®£SÁ—¬þÛ Ömù”ʨá$…¢+eÇ!{wq(½ûGP¶c¯½½Šì/¶±ãÛüz &¤3W¶9]sÔÖo3ýˆ™L]m]gJ=ž\„BáK·ôvÕ—Lü潎TjILíÆ#=_”Ìû:Ðuh4oÜ^÷í­Ò;Ÿ1ãÍ"~s¤™_ÞbÌ›É|m#§Œ¨)bÛ!ëc}T¬2Ìù|¶4‹µ¶ùÅ– [8\lM²) Š×ù4[^]ÅÞbëØ[‹ñ8Û–¯á˜ ˆîa]õ¾¸‚*@ @Ïøp´èèžœbÝŸF\¦Â¶uLXÌ@HFôª[øë‡]ïð×·¿v‰T#5¹+pŽí¯¿ÃÞº›ó U  ؽòy2ÒŸgÙ§ ç¶ñ8¶É¶hsÁf2—Û†Ò7u<ø‚é{¶¬;N}2wÅw,Û^AI™ëÃÐÀ¾qhͧٲ|#?T:g+³)¢â­Ç‘ZÍÚ¯š¶h2VÅ9ä¬ÉýÚÅjªŠ°w1´È™U¼èXŒp³µ£·`³Ë…KVÙ‚Õ”MMlªœ•d.ßb½žÞÿšB@ÓÛ)*—–¿ÆZ§oà-Æãl±ëõMMý6WFÀZ¯LÎ<¾>!„Â'¤ƒÀÉ¢u…¬jGHRT£è޶䃺E ³¶•Szµ!ý#ùßô¼ÒÖ•md®°Rñv(¦ìXžFFúk¶×ÖÙ)+>ÎÞzÏÌG8YlB‰ŒÎQ h $ÅQVVŠ.¦?Ý¢ @%g»ñÈ7¬ßt“6‚äéϱø… æ=2•¡ñÊ@R0UelklXöÒd[4ªãÁªNìà°Ó±SUpÐ9Ù&ÈP@ۃɋ²Xœéô³h"Q†å‡<ÐÌýªi‹&cUœCΚܯB½}ßjʦ&¶Neñq× –|*ª¬ë…ØÄßË£ 2xÞ¹[[Cmý6ƒÑëü!„§¤ƒÀÉÂi1Üfø•ÇŠyâÛ†‡øûÒwF~—}‰Z:ÝíÆuRï²Þï[oÜ ¡Ñ ŸL|h)'¿(¢ÊËÏ¢ Ü ƒª š5€¨Ü³œÌEKù`ç÷uDõÁä_-&}nsß7n°>°Ñ Ô{fVKgí˜kÔ”MM¬&q&OÿêNí‹ ¶ÞŠ „B’ 0XÇËÄp[ˆ™“ÙgybWsnɼ§ºözo”® „D)·ùšmhTo†*ov5)$FiÁ\ÆÙ3¹”T¡átKxÅiÚÍ™ª0â‡E ÑBMUˆA×qxÓ;,ËJç…Ì÷Ø_aF›ÊÃÉÊ@»Æê¬ÈúMd€¡Š”Ñ}­Ó$nC{¹ù¼Ù«3_ÉÅXcŽðóPv矬õÊyÀWûUCÍ9Ô¶oÔõ¡ P&ùœš²©‰mDÔb @MÝ•cûaªŽ®smÛ­õ¹ziª*¼6"A!„ÞwËwôìÄÒéátÅÄÞM%ü>÷Ç]ô¯gxº;„Ž˜ÉËu?Šá± Ѭ™ÍŸÛ)“n•FFÒ„áÊ$û†Ã…fÐöàÞ¹“ébÒ¯Ñ÷æÞ¹ÓHÒ¹ð {mÑ••&cL¯hª RB.?ší5™ÄP0W•ºìÝcñ2gîLF$…;6YŒ9œ*6:ôz—hð¨ÎrÉ+³–-u†uz@ÔpXÉøØÆ¿‡1{‹31gLÝz QÛ”¼bëb‘ƒ§Ò=Àöʸ¹‹˜œà¡M×™ï((]î›;™1Þû6ÜWûõœºsH=Ûq¬‹¦ç ïØQS65±u4†ºs^3œ‡9”P ØiêL€F ˜1–Ù+úŒœÉœQL1ðµ¨~ÄÀ\vZ™âbÀ#,ÎÌ"}ÁTz*;N„Bás·ô[ ÆéÌoî0бª’·7Tð®»WÑ©•ÊšŸEðÀ ë[ Büu ·ýþ@â5Šìo4PÄ>0(˜»zw$D •ß—vÜ}gÅ-ûšC'å½Õ' ]§¾.¯Úù´bÕzÏV¸w¼ªÎÝêó E¹×I–HhHÉ£îäλÆó“уˆ1hÁ”Ïo¯§ÀöÞðöñÃÛ•N¡µäþ{=‡ÏCi@oFìA'¨næ+ ½»î¼þ)£\Êß/Ü̧ùêŸÎ¯1³ò¤ÎŠ*:1tpW ‘ÉÜy×xÆÜÞ—.¯Svâ,×;…P£¬KPùj2Ob=o .v"it<Ác4vöGùïÚ~·çÏx²ŒÎCÑ-"Ž~CǺüûwÞ5žD³»¶hšºýªh ±jÎ!5ûµ+Õ$2ªOW:jްû˜7WÂo:/jʦ&Ö~übëÎù;†ö%ÜߺðæŸ×Ô­MPÛå6†Åaè>›Ü+’`Û«\ϦËä ‘îgL´†‚¯Vqà\C ì&0jÒDùƒ¶c \Þ¶ „Bˆá–APf¼FuqY\fKkX<Éb¡²´’m6ºÂѳÀ¹Lä(o–=Ëykë”™~„ɕټõêö”×M1›(;ñ%of½æ²ÐWIq…5Æ\ÄÉC¶{rÖöÁʲܺ`5ά⃯~ ¬Ê©üæËÖ<,YîfÑ3ëì̬Øð=eöóÁTÊÉ­ËYöUcÙÎ5&Š5õ5±0naõ‡)¶ç×l¢8g Ë^ýoô÷yTg¾b9ÎÚ¥¯³ýD)ÎÍÜb¾Ú¯*Ρæ°ìÙÁ)„&O®?|ß×Ô”MEì…ý9+*Çd®k4³¹œÂ}kX´ÜuáÍ’MY¬Ï9‡ã°5›©ÈÝÁªâÍîiR¸³o˜Ž³k›‹“C.۾ʯ«‹ÝvB!„ ]ŸþCÜM-„BܤBÆ<ËÓ÷t¥&g%/:}».n¼îÓ2˜5XGá'KX±«ñ.ŠnÃfñðý}0PÊ®W³ØÒÜU\…BÑ,·ô!„mSåöWÈ.2cHyå«è‹&q&Â\°­ÑÎî3¬kI²ÌærÎ~±Š·¶(ƒ„B!„B´q:GDÿr£h9¾7w=<‡‡œÂøad}XROÏ,˜ywÞ5žDó6öå+#|!”~£‡ÒÅ:tÐÚs‰¦=ì+¬U !„B!„hÃdŠ—i:÷ãÞÙ¤§ÏfL¯0tÊ€hg25%H¹ùÈeí’42ÒÓxçÛRÌh‰INQ !„B!„h㤃ÀËÞ?“‘ AhÍ¥úp7ÅÊw4½ùÙôÛ0˜ó9tâ²2õ†9õñgœ2h•IB!„B!Ú8é 𲽇NS‘û%o.Ébíþ ,Ê7zN{dƒ™ÂOßa¯I™Z߈ًXœ¹ˆ9cB•I-T…Ù“ !„B!„hs¤ƒÀÛö¼ÉË+·W£LpÏ>µÀtt +vU)“ÝHe@‚Ðsû8e¢wh”„B!„B´uÒAðc²O-¨:Èïç(SÍá\`¢øÐ×ÊÄÊ¥ÆDßÁÃ╉B!„B!Ú0é øY§\æÐ‡k8¥bhÿî•Ï“‘þ<Ë>-R&µØ– »)6‘tÿgfñÜŒeˆB!„Bˆ6H:~$šäÇx0%ˆªœX{REÕX3n~c¢/sèýWX{È“5„B!„B´2‚@8I Ä}-B!„Bq‹‘>Õð‚†ØGnMcÅveªÕˆÙ‹˜˜…[—²b{…2¹x`á“$Wl&cy¶2Q!„B!D&#¼mÌ|gfÙ~&`¸­nÛ™ÊO¨t^s(„B!„â–#7_¾æP!„B!Ä­J¦'Ãyü…iÄË!„B!„¸ÕÈaÍÐGƯ³Å¤LB!„BÑÆÉ‚[žmaBƒýwÇÞ_ÄêC×0!„B!„mšŒ fS)Ç>yU:„B!„â$#„B!„B!#„B!„B!B!„B!„!„B!„B B!„B!„@:„B!„Bt!¼¡û´…,ÎÌ"}öʤ[ÖŸ å1rk‘C"X3+†Lð'Z™x4•?që {¢7³— ä ʤ¦5äA!„¸•Ihez ùÉ[D¥}N÷…Ÿ“`ûé<\×öh¦l¸IËšÀÐ^aènc´2ùÙ'„7éÆšY±ŽŸ?%*£|oáƒ1t÷meJ± @;üº2N™ècÁ:´~&N»5: ü~ÞƒÙËú2Æ©ÿ*ì©$f/ȤéΑ7wek®¤Ä@üc:ãÉiöóX¦ÿ%…ÙË:~õ98‚Ë2ã)ŧ§6ÍáÍ:-Ó–ÛBU٦Ƿ‰ë‘¢mh×§ÿÿ(7¶DÏ<{„Œ¬õÜ» ‹‘ÉX²JŠFß›ÔéSÚ+ ]•ûŸêðü~:›ˆ^‘øij1åï¤xõ•‘ê¨ÙokˆÛƒù3„%õDâ‡õ¹¦€Ü%+¡ãTt“¢s0´Ær_ý­2¢ŽšX Ã„ˆV¯çêröÝ\øf*†_Ï#\¯Ht¦ü7žDÿÓ „vÑ£ÕX7YŒ%Tü{%U‡ÿ]§Vð“è§L ¬« pÝb¤úħ”~üº2Ò¦éúÕLÙ@l_½­¬NmTSûmF5C÷i ™58 Ó‰ud¾íqæ½*eH8é)õŸvó¶ð»“Ê­¾óØÄ®LŒ²ppãy–”)SëŒÁ3)þ\9}žßÿ»–"e@3Ü=$œ‡û™ùêJÞQ&*†ëY:9ŒÀâó¼°ù*§”7@ŸiiLN®b÷–³C™èEC— $…³¬\X× aO%1µ_Å_`Ó‡.á7wek®°'z35%ê3'YeT&»~¼ é¨Ü\WŸƒ#˜1«+9Êš¿]U†5HMšË›uæ QOô"µo {Ÿ)à^º~µEk¨‡–䡱²Õ3=žÙw†Üô×£›’§÷©¾ºŸÄó<´°„Nw¤Ð1ÄöÀ5‹‘꣟R¶©¡{O!šGùÖbQ¡APUaû-}Pázë«éÜ{ggž>›1½Â¨K# !`æstëk€ðC—J÷_¿äªŠšý¶†XëÃnГo;¢/zÇC¦a³èøðâžžGT0´Êtgjbf¡ïF{Œ\Üú"¹KîvüxþÀì¤cgM#¢kÝÅ@£$ü§ÏÑyHŠs´ç‚Óè<Ǻ_ûnÛkôèûN#öÑ4E°ŠúUËWûm†¼uKÈHOûÑ:¦ôÑW9²í,3Þ,püÜÈÎèÄPîŽjOå÷íØõ])3Þ,äq/utÐÑQÓN¹Ù­ê2#«]Å/*œy}<ûŒ·…‡G`p>9}Ꭾ$Cé¡ Ê”›Ÿ—ËVþÆqVÎ;àуyJJG š³rÞÇOKnÔä¡Y¼\g¾ ëH`cÃÚŠ&Ú¢5ÔC³óÐDÙD+ æ>ÕW÷“jò@a?ŠÁÖ9ÐA£Ç<؇ŸTÄ Ñ2^î H s(˜Œå¶ßã5€Ùbr‰xÿLF&¡5—rèÃÝ»¤Þí†Ï£KW?®×P¼æ·ÖÑUÙ\®öú"'ŒW~Ä#jöÛbaºiÓèæ‹G)úðy§s×Ñþ÷üœ.qz:XÊ)ûd'U.©®ÔÄÖ CœÛÉ¥Ÿ)õT½Z×iàò³õ ¦`§#ºýC Ò@͉‘ÿ×™Ö¸—ÿ›Â½%Xð#¨oóÚØÒÇ~ÿþ›ÿâ¢4]S rëyýªãé~ÕÕÙͬ£?P\É¢‚ëʤfö~Õ—Øðí5eR«ôÕ®óœ¼Úž®õÜ­Ll#F„h¾È÷¼:X®Uø1ËfÐ…%lÿÌ¢LjÕ~Ì:®Úr[´å²µjîS}v?©"`¿ßûoÛ}ÜœÙCµ4qC•¡B´ˆ—¦¤2's"1ÊÍJ›ÉXž Ãfñ›ä"Ö¿·…¼Ûgoèƒç¼Dd'#e«î§ªÀ))ö%¢g¦P¹—Ü×ãéøäst ªìÿ¦ì›ïêb^"zF Žá@jöÛbÄ¿Òmz_ÚŸû”‚w³œ‚ÝøW"“ÎS²ú@N ¨¡¡Pjblqç>%·©¼¸°·Q ¹Kqlí0iqÉz.mÉÅçëƒ_ Ë¼Qä¯#µbX–mYÃSž¤SÚ4‚Nuhg«ßvG—qîcë5õkbÖü/5IóèÔ7®×”pñóW¨:ìtì©Ø¯{îëL7ç½ýWè>#ƒY)°ÿïËÉ6“{ûvE§Lç8´éÖî·:j¾?ÍŠ¥{ñyfln|8ó쟄32.ÀöMû5®\4²+û+/)#ÕÅ&„ðFjÕ9gy⻆;)þ4+–îÎÈóÈ‘]x& ö~TÊÉ>aLI ´æ£ú ·—³äœíÒÊš1/èvåØ9ßå¾Ó"eHé)~œÌ>Ëïs•©êÄßÁ}F“†V `¦ªì4»?|‡…¶É1óY|Oœâ“®ªr^çÅ5-Ì @—&ÿ!ý‰XýŠkGµuІ¼ ßs¤KwÆŒÁP}™c›òØ™]¿¾ú¤õbP|G¬³ä-T_¬dÿÚB~8d ° «7~yˆ‰IŒÑb¹t–‹.Ó#=‘”NZ®æò%®PÜS=Õ/ȶ߫\/TÆ:1{ pëRVlws¡â>µY÷“^ÎCCtNT§SäfÍU& Ñl^Aà¡=oòòÊ-äÕ(n”ñv*¹>@G<ƒáž$üBºØ6~Æ•ÕÙTYÀ0úÙºØS š”BFÊ6½hÛ¨f¿­!4}cñ£œ‹[=xÈ<ð´í"æOcc_"ڱႺNp,N˜°ðsÜM‹p6p*B öÄ'.›¯í=F5|Ï„Oz†SèôEGŒ”­¸˜'¼DÔî¦LÀkÒÓN\«­ûœ]A µ€pÇ&Uõ €wÿžèdkç@û€HÂú{§<4g¿ Ô™oéHzdSSlº®$OŒq­‘Ð0=o9-FØ ª‹Ë…kÔ3Òé# Œa\çaøèØ)˜qÓ»°0Ü)Pe,À¸=~˜8òCÃêié;!š‡“:Öå#°#·ÝÆl?elóä|WJ)í‰Khé¸ï¡)$ÇÚ;´Âû0~öl†úx&;Á÷w!‚jN~æþ&þC§Ô~L²w‘ô@wúÕ]&µd w8n¦4vêÌs{3J±°¨.¥cc¬•  çŽô¤t²þîͨÁu±‰ 0ÎqóàO§øx¦.Œ ¼‘ùwM— ü‚:3é/`»¦µ'0²žh¼CÉ-Û¢ƒöÅ£b\(œýçå§\t{¶·­s ×¥s@=?~žäZo ´›³&ëlxÓ$’âx°ð'¢_œK›Yu Ûô¾ÜS/=Þ¢Xu?`Ýׇ.1tP0!Æ+|x²þ7 11z†‡kñÓ¶ãÊÙ2^_‘¥û.e »ÁŸã%>½”×8ö×1*˜DC›ß<Ïïþ (6´_0Q~µÙêªyBéÝ?‚²xííUd±ßà×c1! ¼²}…@þÛ1³ KâxzçóYúyÛq,mcç7ß´4Ãí?¥Óų|òaýÞhݰp’"üñèÀÕ’|6½t†¯?(áêàpbô:4J8ù½56êÙ¾ŒìªáêÅsd¿•KöÛÅìÿ®‚êAÄëïRÃþ¯j º#ýèGÕ‘£¬>®e`œžŽP¼ã{¶Vè©ÃRYÂÉ£à7=žI·b.ÉgÓ_óøúŸNû%,ð<ÇmyPj¬l½&EÜÃõ*Žýëÿµˆý'jˆJp§vTnªÄ^Ë'7•°ßö5) Ãå ö­øvÔV¶FßI`®bÿgW¬ÿo‹§¬Œ#{¯þTo~šèö›L<̓£ÝüÐjÛS]x†-Écçz÷í¦ÔTz¶1Z¨.<Ãæ¿åóõ?‹ÉɹÄå.®\æ¼m>dݱcÍÃæWòøúƒ.&Ó£S ÁúËúƬ.öh¥£üUÉQt®ä«yÇøÜ©^Žìu½N¨iãÈá:*öäñqV¡ußU`éJ×`=~ÚN­Û¯§õà“ãWe=¨ÍÃȇ»b¸x–µY§Ùýu…º„ÖðÃ7¶Ž•yPj°l¶7ü´Ÿ.•²íí“d¿UÌþM% ëHrŒ?Æ3Ökjʦòºãñõ¬<=v<>/TÆÖÉÃ/~4‰¡Š÷¬gïé¦ï7rŸŠÚûI;/çl O¿J·»%tÄÝÇè¨Ùÿ%›l£V…ð÷]–Í•ŠžËTØ.ú˜t\¦¬ùwœ¾`]àÃR~SÞ#æ7ϧ§=µ®£ètýo‡¯ó8çókéÐ%•ð)ˉè«ÇrîSJ?wö­f¿­!–©hƒM,]Fô$Pï´ðI€uá•Nù:Æ ~K‘c^ü§\8÷©ë\ùƆZ9¾ ¯?¬€‹T_©Åùѳ½^6X1!&é'„èáÚùlνûGÌ—N[·_zŸšus¹â±ž+{ °à‡~è<Û(‡7ˆŸy]õÖú³Øë¸yõ[stçÞý#–+çáÚ¿©Ý°# îk‹hÞ~šª32l&s¹môPMÛY—çÓG9 ðT¹‘Ç#ÌÃ6\ßiÛŒŒì²…ŒÓ‚å2›7ùØiý³•Ÿ–p¤ˆp¼nPM¬]ˆ¸dæ+Åö–ª-.%óÓì3^?i} kÆÂY (»dTÞ²×ÊåòÑò×X»ëˆc‹Åxœ-¶6n¼Ð¦Ä’h!/§ñoª¯æòEå”ÙF9jmp}XÝ£pb¼?˜/²3³”ü£¶Î–óWùaÉQNT‘¡®uW]Êö·®rÍ~Ïz±ˆMÿ¼Æ¥‹®[âCИ/²3«œ²³Nû}¥ˆb ¢{Ck<,æJ¾ZzŠöuNVr¢ úEM÷•²Æi1ÂblÓœ¶­ü?¥ÊOþDo¦ôk¸s 9®æ²zIÝ0kwíæ¢©:KíJB XJΰzI¥£=®5qò•BíS~À:Ì|õ’JDZ“ÿF Å€—úí¦&VÛxï¢3ìüÐéíóW9d«3—SSE=øüøõ€Ú<\5AÁôO©+uÙ‡çøè•¦Þ<ÔDÙRú—ÙýÊ9òÕuÜšLõ;ÈÕ–ÍÓëŽêë™§T;vjÎ 5±»W>OFúó,ûÔKË{z?éÄëy¨Çý yDMšªL¢E¼ÒAÐ}F‹3³XüPt‘ü_Y,ÎÌâw£"€ ý*‹Å™<¯ü䤯z‚kzL [ßH´ÀÕülr—L´Î5pÿäÕ5+¹`CßžX êÏýV³ßÖëäjþNξc[Ððå9w´ ~§ mEÆÓqxO4”pvÝ›ÊDÇÛÂ;ùqõÄ:òþú ÅùF®FgŪ³íºZW½r´é¡^׿yœsÙ§¨®Áñ‡ÂRY@Ù¶ŒWì‹tÖQS¿µç=Á×Nsµ½Õìת‰:ó±’£7¾S®£?PfâŸÊ §ì:àGt˜õw5±®ÿ¦¾9ŠNúþ„Õ ¦J@ü½<º ƒç3­gf5¹Þ€¯ôK鄦ºœ#+S\•ŸlúÕ J.‘ëfÄpþy @„Ó0ת3(vŠ­*qGt´¸ó%§aúË2û¥xëþ@÷×kOËFI9'zN.9ÀÊygT¿²­¹:tëÎ=)XJÎx­sÛÍYSuæ×- ì¨û;wŒç#L—¨¬wK‘«‰UÅÃ6î08„ÔÅIüÒù8s3Ï^M=øüøõ€Ú<ìüò"ÕZI÷`ö²$&?NÜ`ï½Å¥©²é‚€ê*Ž8McoˆÚ²yzÝQ{=ó”šcÇNÍy¡&ÖëTÜOú–ÓÂÓ/þ–ü­9\±€.yvã_ ¡’W:n:¥åØ×]6_Ìáì39ç˜ô:t(wàï_€íÿ5nþð©Ùokˆ¥œë æ(çVÿÚ¢kØÕϸúñûT÷=³­‚í›pK¾ûUøo8ºŒâu¯sýÊ'˜VßOÁÞ®ãGÐЇ±í:¸iÏF\ûf.%Kï&Ï6Ê¡àõÇ©*µ~Ssñ˜-ÊWõÛ‚ý6Qg·:³Še·±Ú­ÜvðÆý•&q&OÿêNü²¿éNR$Tž<ƒÞ’s½î[;_»áek™kgÏ“{ 4‘ݘ0]õ¸ïð Îüšó*»›Åð¦ÏЧG'û: óy=xоdú¸€Õó°uw)¥—4DôêÆ¸Y·ñ‹…^XƒÀƒ²9ÖßiÕšw=óù±ó#Rs?yÃ\ËáÚßr~ë)Ìøá;J!D³y¥ƒ oÍb2ÒÓx3ç2Ïgéid¤§ñYPuŒô42Ò³öŒò“?–½˜*Ê©Øð[j˺r#&T*»w‡0%• ‘²­{©&’îõÏS³ßÖûo,—€€HôõÖ– µ.Æ×jÙ¿ /çâçŠ_'€r.ívýFþúç¶oúk¼~ÑÈu  ë,çP†ð“$0RuؾðŸ—ê7b(z=Ô #hî~›ª³¶ÍlÂuü\™¤„·ËUì_Jª‰µ«¬‚µÜ¨ï¦!¸þ¡†„Uæ}«¡™ô{"»úQsô]ª,äÂ!#èSˆžâ<ïGÍ~[C,\=vŠk„vÿüì«ïwœŠîÑû €Ú¢½ŽØVÅöMøµü\q;íõ4×®„añ :Ú.ÞþCÐÞb]×¹èØiª¿^Óè2Å)¾ãT¦-§cCSä;ŽD“ôaO."º«–s;]Fž4§~ÛëëÖÝoý Á3lív¢îSsöÛtµmGÎ]Mï d¬ÓÑ“£éµçª± Ukw´ü:øÐÏ _D5Wi•ÐqÛ?RT|™µåµŽõš#@£ÌËêN€>#g2g”òV¸N¥Ñ„‘4Á»Oýâý±œ)õh¯'rÏZ@Û‰Qia„w³ GîâOòóýéWÏ–7º0eCΞ­m© Ãè–ìÙ0go—í†È»Ä§Ks)6’ôho†{·¹›äQí¨¤ìÕƒŸ=¥'Ø~¿ßÅŸÄgcHv3亃®î$ó„ǻ”­¿`™šX£É t$a’}2U=?°`¼XƒÑ¶fCøô& w³sõàëãדzP•‡ÁáLz>‚ÄáNÇó&Î^´Ztœƒ­<Ƀ'e;{Þz-þ”õK‡¾zF-¶¾úSIUÙThîõ,yqf/Káá†F[¨8vìÔœjb±½bpqæ"æŒiéÈÊûI'Þ˃RhŸøÿÑyJ þÔb<*  ïi×§ÿ¯Mš=7‹ñ¡ÉX² Hà…O’\áæ½èM½»Ê¾ê0üy„×ï´Å¢|·|ÂKDÏH!Àùݤ¦4oõµ\Þ¶ ßÙ†y«Ùokˆe*†_»½^S@ÞÒÇë6 ‹„Ô†ž”çúQë`{ì¹OÉU®ïàb<Ÿ|Ž.!唾ù Ɔv.'öžž ©T´Ð~ø[tKuo¤lÕýuþ±/=3ûl;ËŽ¬X¨Øêyýj¦l ¶¯›À–¶x^gj4q;¿Ë¾ûŒ f¥Q¸5Û‚lûðÆ{ïÿ4+–îÅç™±¹þÊçvz$–îîÖ0»ZÅæu¼ã4'SM,@`Bo¤Qs–'¾k ??1”5c~ÅÜ•cçxܶáÈ‘]x&ÉŸ¼íüÎù«}Û>œcícCxc\P½7;¸‹µKAzŠ'³Ïòû4Aä¤4žUoH‹C½¶4Ãæ’~zSZr<øý¼ŽöçÄê£lÿZ™ZÇúní€úï·½Ýå=ç݃™´ (eFÌ|µ4Ï:Üö>rìŸUîËö»ó¿9jÉ@’¸ßVæÍÓ²LZ6¨Â\V65￉w¾7ô¾÷&÷¯¬ ¬CÝg<Úƒ¹’¯–ž©›;¯"ªÚMeé¦Ç3ýÎ7oj¨áØ›ÇØi[dÍžw” 1ª‰uË/îïT/ʲ5Ù6úÇ{òІ¯;ʺô´ðáñ ž×ƒÇy°“nkÂ|‘mÿ]@¾r^¾‡yð¸lnó`áâ#~ñ!uæQÙ”çšò\P^w<½ž9sÉw%_5°Ž‰§ÇŽšóBMlTædN$ b/Y(Ôݧª¼Ÿ´òr‰5ZF±¼É@x‘WG„¸\ñâ54c"Ópm=U+¬‹ŒØ']·1]çú Ö1ÎÓRÀÈ…M/Öm¿¶žË›r¨Á Ôg\¶{´ßÖ‹ûØêÓÙn2[ §oÂ}Ð=0—‚UÙT^4R÷hTËÕ‹§(^õëzóëß<ÎÙOöRUY눿n1RuèÓúëQØ\·X÷w~Ën:PU¿×çpñ¼³¥nñÃk#U‡¼ÐnžÖY÷†bö×àXÓÙb¦ôt)ùgý~5±Õ¹•œ¬†>ALQ&Þ Õ•üeç%J«è pcJbT_âßÍ{w(Ù”Åúœs˜ì—|³™ŠÜ¬úð Ëë Yö,ç­­?PæøPË¥$Á¥²ÆoÒÕʻĦ×N’SRí8ßà*Ïœa}†››iv.<À×g®PmnºÍ|R¶é›R6~YÉUmct'±»2ÀûÔÔ™éÃ3|´á,Å—Ìuíl®¦8'ß塨‹¹šâœ“ <¬¸j2ö³6î¸ÀEçqcŒobë‘ËT;¶˜©,ÌgÓ†ónÏM5õàÓã×Ãzð8ûJٶ㫆¤›klç±›Î<σÇeÛWÊÆOKëöW}…Ó;ŽóÑ—¶×ƒ*x\65šs=ÛWʱ‹fà:Õ…ån;Pyì(5y^8i:6›Ã¹&ÀDñ¡¦Å*ï'­¼œ…ë–ZLçR¼ê é^çÕBq+ëÙ'Œ?ŒÒcnäûÖdäí]x¦¿†¼ìsü®…­Â]yøáŒ;²ñŸmìO[[.›¯ø¨ÎÅà†šØ6ÍGmÑ*´å²ùˆšóBM¬Â;¼:‚@!ne§~(ç«âëtLêÌÂpejë®ç±þþÔ_àïm¡sHF ù"ßoh{7ém¹l¾"uÖz´å¶hËeBÜš¤ƒ@!¼håæ³ä]õç¶ x²¹£?¶@}˜FHU9+7_å”2à&•›u˜•ϸ}¿÷Í®-—ÍW¤ÎZ¶Üm¹lBˆ[“t!„—ýî½òj¯SíXÀ u©6Ö@m›ÿeä+e¢B!„¸eÉB!„B!„B!„B!„!„B!„BHB!„B!!„B!„B B!„B!„@:ÄÐ}ÚBgf‘>ûeÒ­)$•9 ç3:D™P§MÖ™åB!„Büx¤ƒ@øXC{… K¸ÑÊäÆŒ™ÏâÌ,æŒQ&4O÷ sùÍ ™,ÎÌrüxkßÓôæ''h4ÊT›ÔY+¦1ÐÄ1~ï: ºqjã…3•ÉB!„BˆFHhô½÷Ë…¤{ø Ò}ÂcÌKËäyÇÃëü6ò`˜ËÞå˜N|Çeò 9e!³Æö T«U&ÝP£g?F²á2‡Þ[ÎŽ e²oê¬Ï´4ž{aîv\Y 7²â½ƒTiãH}(U™Übú1óyüž¿…B!„¸YµëÓÈ”Eói:÷cÜýSš„ã1¥ê KV¹Ú… áá_Í )T™Ïgé¯yíáð¦4f>‹ï‰£pk+¶+ÕHááŒGHÒ]æØ†7Y½§HpCèÇÌç7÷ÄQ³ïu^\—«Lö¹Ñs³ûãW=gdðXŠŽâ­Y,Û^¡Ln¶s³˜k¦ìÛwøëÇÇ•ÉB!„Bˆ&È/xÿLF&¡5—rèÃÝ+F?2•¤P0•}Ïæ¼HFzšíçÇ}ˆk[ÂÐ뀂¯´ÎèÇä±qhMß³åãß9КœZ·3f-Qc'ÓG™ØÓq²¥s@!„Bˆf‘Þ6l¿I.bý{[È«IeNæDbAôé¿è¦`3‹–g+S4bö"&&@áÖ¥¬hÉ7°Qò›_%´b7Yë•©Œ›ŸÅ˜èrö¿º„ì= Œ{hCã#ÐiÌT•f÷‡ï°£Ðyȼ­ìN[(ØLF#å8m>©ÉÑŽ!âf3hµxa-/Müûž—Me, 6—ôû{`Ü·”—×5ÔIáyuŸ‘Á¬Øÿ÷åä ›É½}»Zóa:Ç¡Mï°v¿í¸°ÂhLUÎ뼸F}§…Çyp#rÚBžlà̆çykOýújŽÑs³ÚÀ¹&„B!„h’Œ ð¶=oòòÊ-äÕ(êëž‹Žr|\ÿ°a© HÐ:bn§LT§øÎT¡½¹/J‘¦¿—Äh ât]瀦7,x’1½ìÅZ á}ÿäBHlhѽ¦›ŸÉÔÁq.óÇ[4•<~&Ï9Ös°=tÇNtY Ðem5eSk309-åœÜÕPç@sèHzdSSl溮$OŒqzE¨Ï4/%»ŽS–nÉC”IÍW¿Ú…B!„*tèý?ÊÂ[â|W"Áµ%d}X™È€Ñ÷Ñ#°”sµ¸çÁøéÄ{¸ó®ñŒÙãiŽW+?äá?šÄP Å{Ö³÷t•2@•³†~ÜÑýÿoïÞ㣪îýÿ¿”È„Ü!! $„@ä&‰ÈE@ÁK@<å"V8µG(‚ÒVÚûM~'ôÚð(9•¶ZD ¶ž GT,ZD”j*ZðA@n$@0‘$0!˜Ì$™ì\'7ry?<ÎZŸÖÞ{fœýÙŸµv/¼9ÌGÇ*_Ë'þߘåGÞ¾ ì?u €Aý”I}ÌØ ðÖ ä¥7Þf÷¿r¸¹ï`úúûЫ×Ídì;éz…3|úÞN2ÞÛIÆ{bî‰ÁÿÒ 2>9Sño”ó™üó†@Ѽù—µ¼ô·md¼·“C^CéÇå“;ù4Ûø¬zÆrçˆPºÛݹO¶Í“X§hîœ|½®ão¹ü`M¾Ïn½›ÛC½1›»`ËyŸÿýýþöÁaL1#éë@×+ìϾÙû\¯·GLýý³y7ù×ü¥âßÙɞõ_é¯KƒÇP“’®ôK/˵?žò‰™ÉÔ±t/;MÆÞ£ÆniUÜ0Ñ„澌¾{Á¾nWÎ-½‰½ŒGF×|Iô£õËII^Κwš~5ºä½9m‡À[ÆTiÓì§Øÿ^yÒ ŽÛûYÀ~Š·ŸÙÈþqøzÖv–ûZ‹'ÛæI¬Aµø¦»pÄ“é)-£±c(±[DDDDDäFR‚à†É¢Ìؾàíõor¨üÊyÙyöo~“£VÀË×ø¤±Ï1l3:Ÿ;†“{>6†ÖËós`_ç w;àɶÕkiãÙnÔ\ ã‘3›W’œÈ·~Õқѓ›¸.‡ˆˆˆˆH'¦Á t¡È–pÄ s3œ<5ØÑÝœ°:§„âëœ^Pô›ªLå¶á°ae›)Ž˜03Ø 8{ÚÐW¯ó¹’!£ =ã‡8-Ï“mó$¶\¶s}æÞ|CøPíýv# $Ô°5Ëí÷òXòDLùÛÙ•ö»êkijhÙT"j:o·eóî3ϲ»Øõ¸žý`=ø«6g9ô{€'4Šª»ÁNÁñóxÝҗ≬ÛU¥ÓC®}–³”µµÌ—÷dÛ<‰u1^LòŒþ”|ºš§·Ô²¸¤û,jn âüÈ5î×kTÙ¿.åc0»¦Ø†hÌÊ…ÎJâñ¾œÞºœöÕ8!Ãcã§“èþùO¨‚à†Êâo«ŸcWV!öò&»‚ãï³!½¦ä@‡²l€¼LãUþ&(y›ç!88rVOgðÂ3›ù,§¦ñV?)n°Ó¯°në”_¶åsbÇZÖ|иÛï5Š'ÛæI¬‹cßnNÚ 0vZõ© ­Ä±o-/ìø’[Ũo S‡í{›)9 """""M§ ‘V0á ~2¥7e׳jó1cw§5+…#,än[ɺ½•·µlªñ‹ÓI;Å›¿\Ûˆ©."""""¢ ‘VR¼ëwdœ·ã÷sbnPÁ fŠ™Ç#ü°çìlÖä@™Ãµ–Åü6°Æ‚ˆˆˆˆH;Ô¥gHøE¤eä¿NÌè[¹%v0^§>áäåëÆËÔ3žG~4žàÒ¼ôì›6ó¦ç;úpÇ­!tÂÄ{˜8:„ŒÃDDDDD¤ª iMŬ[½Ü2%ÖÎ5ÿÞáʲy÷¹œlMwd¾Èºm_Üø5DDDDDÚ)­A """""""ª %DDDDDDDD A A A iÏ¢f%±"-ä…w»šG@<‹’–2>ÀØ!´DDDDD:Œ.=CÂÿÛØ(ÒöE3iÆzus Ç{ûÈ1†DM^Ì#?˜ÉÔ„)L¼'‰÷$cßÉ§ÙÆHÀ49?Ë-^—8õÉgä”^7F` ŽeÔèXn;Ç}ä”#š®Ï„Å,|ÄuÜF‡ñá!cˆˆˆˆˆˆ4ƒ›Ý:ò[c£4Ég ñ³g2ê– ,Ö¤¬ÜhˆˆfNÒb} Íîj|ž”‹š•Ä‚AØŽo!í/»«žÄãw›ÉݑȺ]ÆV¿8„H™^É«'ÆîoøÜ'¸wHo,f;EY»yý¥·9SCÀ3ŸýÇmxål'um†±»I|&,ågSúb.oÐgBDDDD¤Å(AÐÌL=‡2iÆLFEûÕsR£Aë‰ã¡”ù ¶\æèÖ lÚwÞPEùIi٧ϱjK–±»Ãs&G*Þ½•¬x1}#'kÈ— ˜›ÂÃqòv¤³fW‘±»ÑÆ.Ngj¤‚½ÈÞ8fì‘f¤5šÙðóí‡ÙžOæk‘g ¨Å«+II®þ÷ÔÖSØkÖGÆ'I£ácr>¬79C™vw_̶/xûΗð™°”øH3زÙõçTR’ùõŸÞ'×øÞÆ´ûÂOàä–œ¶› »{ƒŒM`°#CÉ‘§A3ÛŸyŠ¢¬÷Ù°2W?+¢†‹­õº÷îþ˜9ÇçoÕ|‚:va*+ÒRY4!ÐØÕ Ä³(-î‹ãAf=ž(:²“Cµ¼¨¹)¬HKá{ý‚>÷ ’Ÿr#å æÜÞ¾Éø}1s™Ì—žeç +e§ßfÝK°·Œ1>ÅÉñ1Û3 Á2q£MÆÞ¦q8Ъ"""""-O ‚æ¶oO¯¯y®vC˜F/fx ØŽìdg‰± žaÑÀBÄ“ŒR®ß<ž¬H"L% rjÕÄBÒ<㳉™BNì­¯ÒÀÂàù˘W>O°ô&vöÃLò1„¶w æÕÓ•­^ac˜3c ¾áŒw{†» {Q„™>±#]×̹©mLyõÀþ­‡].ʲ6ò2?4vv¬«˜Ž±\cw“DlkGkŸâbÆb1cËyŸ ©‰¤¤®f×y;Л˜ÛwV;~±¡2Âøç–Ðð$¶ÁÂBð°ºÖðŠfÒÂþ¿Ï"6Øâ ¤g?·ç¸Ë;Řƒû{Å'f&Ã‡ÍØ%"""""-@ ‚6¤²z £–ê§Ö/'%y9kÞ©ï*w'vz#«Œ‰„œíU×{¨¶d_}ëyNzjbÏÙNÚZWµHÙyvf:‡OX¤1´}ð²`ìe0|n"ÉË—0!ÚÏ™Œ:¸™íÇ'êµ§?RTøÖZeÐÎ)éüâ?Æì8Ǿ­oCDDDDD¤(AІ8«òù|ÛAc—´¦ZÖ0ºp¤yoé·{mõ+kKhxÛ`e6€ù–©ÌŒ ÁX³ÞgCêrÖlþ¼œUuíž]ìi·” h#Ê«ìY»y»ØØ+­ÊR×½';°¼"Ê Wlxýé$V¹­§ÑÓ ˆ nëTS{yAƒÙ¼‚”äDþ¸õ ¬–ÞŒž¬µ6DDDDDZƒm„³z ÏßúØØ%­&›"+àÈ0cW+ðd]Ob.ƒ¼"€B޾¼‘Ï¿v«{€˜@ è<µß|s ¡¾Î5 v»á¾9Tø»DDDDD¤(AÐTVdð÷zÇëì·9lIYœ)°ƒ%œaƾÎaÿñB ˆÛ.fl˜^awñÈÂQy™;O©6”_°œ2ö4š¦,ˆˆˆˆˆ´ž›Ý:ò[c£4Á„¥¬˜ÒרZÉz ÚüðûÓXÈgϬäoõ&âYT~Û¾¢ý¤¤¿b èØêٿփϱjs–¡ÕµÏr¶“²¶îuL£“<£?%Ÿ®æé-5/57…q~äîHdÝ.·רjC;aȜąÄÖ0Ëž³Ô:ö_è¬$áËé­Ëya_]+4ÜøÅé$Vÿ̈ˆˆˆˆHóSÁ æ^=PrÝæ°…9öíæ¤ c§1ªæÓ·;Žc¼ºú9veb/o³;ïbPWrS‡í{›)9 """""­K"žà'SzSvp=«63vK ¢f¥°`„…Üm+Y·×jìn´ñ‹ÓI;Å›¿\Ë~c§ˆˆˆˆˆ4+UˆïúçíøÆ=Àœ˜ÎXFàSÌ<á‡=gg³&Êv0÷'~þc—ˆˆˆˆˆ4³.=CÂÿÛØ(ÒÙå¿NÌè[¹%v0^§>áäåëÆL=ãyäGã .=ÀKϾIa3ï¦|Gî¸5„îÁC˜xOG‡ñá!c˜ˆˆˆˆˆ4UˆÔ¤8ƒu«·“[æ Äª9õµq8€²lÞ}n#'[`792_dݶ/(°U¬ˆ """""-Dkˆˆˆˆˆˆˆˆ*DDDDDDDD Q‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@¤ý‹š•ÄŠ´t’Þeìjñ,JZÊøcG=}"""""Ò®è6‡"­(jòbfŽ‹$Ðl®hËݑȺ]UÂ<Íœ¤%Äúdónò³ì6†Ï¢´©D¸7ål'em†{KÍL™“¸X¯lÞ}f-»¿vT페Ƣ…ã #›wW?Ëîâ*Ý"""""ÒŽ¨‚ -üN0]Éæ|gìtñö1óŸSCøëÃl^Éæüõû=HмÉ*í\èô$ÜÝ¿Jr é²Ø¼ÛñOjH4Íø…ë{™Ì—ª'¹o²î¥XÍ}‰0ÞØ-"""""íˆ*š™·‰‡ïìÁwút«l´òûWJØëèò›ù‘D¹…Vú†3çøE–±]Ú§8J™Ï`ËeŽnÝÀ¦}ç­ÄUMЀ Ÿ KùÙ”¾”}ú«¶ÔýF07…‡ã,äíHgÍ®"c·ˆˆˆˆˆ´º‚`@L ÏOµ0ÒØÑ“Æ„:“WK9°ókÎÜ LT·o)>~ô¿æ0wƒóoÍþ+\£ QƒjÌH»„Èùð&<1”iw÷Ålû‚·ß¨;9prËNNÛÍ„Ý=AÆNi:u‚ Ê¿ ÞaÁ$>àǽcoã¼qü2Ågóùýÿ}ÍÊœRìÆ7Þ]n¾¥àÜ5>¹VÙþAæEŠk×j.îxxr8›„ó›MCèh¼úÝÅœÅI,*ié¬HKãÉe a2DƳ(­<Æõ·¸ùJüM£Ç3ÀEGvr¨úÌ‚ê³=³,7Ú8ÖªÆ.LeEZ*‹&»DDDDDäêÔ ‚|ò5Oe\æŠo?œÕ“ÿ 5F4BÎe}§Œ½ß;ªûäK+ùÜLL|(OìÊÈ®×Û›ß|?‚®rð3·¬›!}L€‰¨XoâŒÒvô›Ç“'ð®E#§V=©Ošçö„hîp:±‘AT.S`Æ7x 2ªîóîf5<63…œØÛðj‡ {Q„™>±uÕäÄ3,ÚXˆ¸c’±SDDDDDn N 8™UÌÒ× 8‡7£î åWÑ­xUþR)¿xókò¯šéJâ¿G’<¥'QßXÙù·¯øçÚsÕ9ëœûÒÆAc§´kŽ¢l2·ý…”äDR’y*m={ÏÛÁÜŸá£Ý#3XçŠIIÞN®{W“ElkGóŒ}uÈ;Řƒû{Üdp(ËØÈËüÐØ)"""""7P§O”^²ñ³—r8\l&&¾Ïk½Kµ¥¥œ/1”øv%ܧöDÅ‹ïœgî†óüìðuc—´%§7²ÊxŸ³½âä?%9‘”•ÝžÅßÖ>Ë«{W´8JŽñv¦ë*~«½-ûè XÏsÒØU§ƒ•¾Œ7v¹ùhýrR’—³æ†W'ˆˆˆˆˆHËS‚ÀMê–\Xo"`pÏßÑÅØÝ"~3?’Ûzt¡øxOýõ;Ï^¼:)œßD£¥£óêw/?X–Âr÷iSúÃZGCÖ0(±[DDDDD¤½P‚ÀMÒ¬nóý–â£y<ú¯,"ÐDÓ¿Û‡¨npåèyÝmçȵoXÿΞÚèBÔpo†Ÿ$–)f?ùÑDb‚ý¨X‚àF²ø[ê×jU"""""ÒÜ” ¼ý-<=?‚ÛìœÈ8Ë£{qé´†Ý Ø8Y5q$ó"gº1ªJtdÃïŠ/`=²¥ê4„ÙÆÐ–M‘ð d˜±«N õ¬Eì6v‰ˆˆˆˆH›×ézû±zv0½±±ÿ­ üWVÍ·l Å6€nÄÄ:ï`PîîØ„_eesÝæ°cò2™;%9mƒÆÍcÑ­=Å ‹3v°„3 ÌØW‡°¡Dø‚½à”±§ŠaóSX‘–Nò²™ PňˆˆˆH›qÓ [G¶Þqs÷Èž,Œó¦«µ˜¿üý2o7Çüé˜@6O¨£4ÛZÈï_)a/àÀó“üpË ¸ù†3çøE–±~³ ’(€â‹¤m¹¢;´ ñ,J›JDÎvRÖf;½/‘Çï 16WÈݑȺ]®–Ö¹6õàs¬Úìzóxëb½˜äý)ùt5OoiØb‚¡³’x|„/§·.ç…}µUáDó½¤%ÜîúˆänKdÝ^cŒˆˆˆˆˆÜº‚  äJó H¥™’*Í)féßò9qÑîÖú W.^bçßjN ÛvXÞJçõƒç°•¿ìvвv³ñµX ±-ͱo7'm;Q ¹ÊoŠcâ °co­É€,v~MÅ;¾!¯-"""""­¢SWˆHí&<ÁO¦ô¦ìàzVm>fì®"jV FXÈݶ’u{ëNgô½€‡f —|ö>“ÎÛyƹ:uˆÔ®x×ïÈ8oÇ7îæÄÔ~©ß3FøaÏÙYgr j®síGg Â(úôM%DDDDDÚ%D¤V»_ÚI®ÝØùK¹7¢z’ÀÔ3žGæß†¯õ›Ö×¼¶BUv¬_²÷¯«xzKÝU """""Òº4Å@DêÏ¢%C9úܳì.ö ODDDDDÚ%DDDDDDDDS DDDDDDDD Q‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@Djs÷¸^l^Áówt1v‰ˆˆˆˆH¤ˆTçïÍìÁ&ò/^#àÖ ~âo ‘ŽF ©fÉ„„äòû­9\jáÎ ƒDDDDD¤CQ‚@DªðŽ `\È5ì±qÿ³ç2×BzðØ ›Œ¡"""""Ò(A "Uüç~]ÄÊBçãÒœb>ȃÞ#x¸«1ZDDDDD: %D¤Â¸;z1Ôû {÷\«Ò¾~O!ùÝ|ùîeDDDDD:ª›Ý:ò[c£ˆˆˆˆˆˆˆt.ª %DDDDDDDD Q‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDntëÈo"mW4s’–ë v{!gßÛÈ »rŒA-,žEiS‰poÊÙNÊÚ ÷–6«Ï„ÅCÜ|ã]NÚ€ÊTŒÔÃ`;F†’"""""bÐ¥gHø¥ñΛ¢Þ5“Mk^don(Ãï‰ÁÿÚ2>Á#Ì"!!‰Æ3,Ê‹¯¾k)=8‡© L¼'ž±ƒC)É:Ê…ÒëÆÐFŒ¡áúŽL ·ó¼\íý("""""*šÛ¾ <½þmÎÔ{ÞnÁd5ž>O1ànìâm,DÜ1ÉØÙ9™Œ 3~q3ãzc)/H0[¾e" –Íc@þ±‹Ó˜9¢/…f,á·1söøª-8† Íñ"""""Ò!)ApÃdSd‚ÇðØ}+Zúá{‹ïr®ìþ— eÙy™7jÞ}[‘E™¿‹‡F×”Li8Ÿ K‰4c?¿›«RHINä©´õìʱïm$ÜÓØ…ãi†‚x>-©b*ÉÆm_PPf¯Ùrcpò‰™Éð`Àa3v‰ˆˆˆˆˆhŠAËr•Ÿ×8Å ˆó7dÔ€ ü#ogâ= L¼'q#†æßÍRšCÆÞ£†çÁÙÏ2Èx/ƒý§¬Æ®N'«À—ƒÐoØX&Þ“À¨ž§Øs¸ÈV¯;îÃ-Þ§xëéd–:kù¯_»HÖCØÍÅd|RÓíë›bJÜ„!zu£ë×Ç8’W À×¹ùת¯×ø1Ô-jn ?Ÿw?w  ûµsì{í¯/¬>µADDDDD:7UÜ@Å»žå…_R`«¼’l/Ê&sÛ~çâ†VÏOt;2MŸšMh `îÏ´T×í&ËÿR§àb|R}ÌëoÂf!vö“ÎÛXΟɨ~^†¸–ƒˆˆˆˆˆHý” ¸ÁÎîÚÀV$W”ž§¦?Ë«yf|kA¶1\ â§'Âr™Ì—SIINdÕæ,cÈ W¼o-i©«yeÏä•X2–i?ZAòâxch‹8³y)ɉüqëX-½=YkWˆˆˆˆˆHu7 ºuä·ÆFi.ñ,J›J„õ)+7;k5~q ‘62ÿ´‚WO{¥R4s’–kÝNʳÆNÜ»,q>_ðÊŠ1N©›ëçl'emÃÆ`ò‰ãþ%s¹=ÐÁÑ——³)ÓÙÞø14ܽËÒçåÙûQDDDDD:U´&Ÿ`¢b§ñƒÄ4"ÍØs2jMŒ]˜ÊŠ´TM4vuNMŸcÀç9…`¹•ûOcXDÓ¬¢ß,Z<±ƒ+œt”ädž °àãSÚbcpS¢õ EDDDD¤ª hn–²bJ_ck%÷j‚~óxòG·a<´|Ħg^çd'¾®+ÖEûIIÅЉ¸*Š~õ¾V¦ÌI\H¬ñ`¸äîHdÝ.׃zޱõàs•Sj9ÆØOñ抵ì/?ΞŒ¡‘Æ/N'!P"""""R*Ú »kÁ—ì}y5©«kK Û¶Ç1^]ý»Žçc­z÷Á¦9½‘W>ø’÷µ_¦àøûlXé– Ç """""Òª vl <5‹~yÍPAÐIŒ_œNBØ)ÞüåZö;EDDDD¤SS´O^ጚ?‰~f°;4±¾¡Êv0÷'~þc—ˆˆˆˆˆtrª vƵî@Å<}G_NeSf­s2Ä)öaž|ðV,å ÞaCDDDDD:.UH»e·åstÛ3JxÀ‘ù"ë¶}AM‹ˆˆˆˆˆHUª Uˆˆˆˆˆˆˆˆ"""""""¢ˆˆˆˆˆˆˆ ˆˆˆˆˆˆˆ ˆˆˆˆˆˆˆ H'Ï¢¤¥Œ0vtpu»EDDDD@·9iEQ“3s\$fsE[îŽDÖíªÖ²L™“¸X¯lÞ}f-»¿v#:,SÄ4-OÙ¼»úYv#DDDDD:/%Z€©çHîŸ=‰a‘A˜ì…äfîdÝ–OŒ¡ Ÿû÷éÅLQÖn^émΔ#¥= žÄãw›[=A0~q ‘62ÿ¼’WOtŒä€Ég ñ³g2ê– ,Ö¤¬Üh ©`Š™ÇÏþã6¼r¶“º6ÃØ-""""Òi)AÐÌÊO>|€=k ©ë?®Òæ}ŽU[²ŒÝ펩çP&͘ɨh?g2çg§®À€¹)<g!oG:kv»EDDDD:%­AÐÌç)¶æstÛzR’IINädcÌÑc˜äSë3a)ñ‘f°e³ëÏ©¤$'òë?½O® ð½i÷…»¿´´kAøX€œoXr†2ím_ðöí?90|Æ<ÆEûa¶ç“ùÚGäjqrËNNÛÍ„Ý=AÆN‘NªKÏðÿ66J”áÓ÷r(÷bESÉ©}xÇ&Ñ.}¹‹#®yÏ÷ÌœCT÷Ëdþåøû©k8ŠOòin£F„âß2ö­xrc¦òèìxbìø4[óÚ‡~Œ¸'ÿK'鿊α³’W4“æ/äÁï}„„&ÞϨØ(n>wˆœË× ˜FÏbú° .gnäÃVc7QsSøù¼» È:Šeò"y`–óµÇ ¥gÉ1Žäµ­÷ÛySûf²iÍ‹ìÍ eø=1ø_»@Ƈ‡Œ¡U]?K^‘ŒŽ Ç×úŸŸ«¾¿Êéó&""""…*Z•ƒ²Šó‹»ˆŠóêéʯ°1Ì™1Ð9E!0œñ•].ñ ‹¶"î˜dì”¶¤ß<žLKgEZ:+Ò¦9ÕõØõ—4¯2Þ49Ë–0á–×zf|ƒ‘°$‰91¦ÆÅº ÄL!'öÖWÁ`aðüeÌŒ+_°ô&vöÃU*`Ú„}xz}ãÖ븰÷E˜é;ÒØåFŸ7é<” h Óˆ ŠŽñ÷òè°|¬®ùÏ^ÑLZ˜Âÿ÷ãYÄ[\AôìçúÏ ʲ6ò2?4vJ;6èÁyÄú‚½ào>“BJr"O¥­gWŽ ð#vrå ª'±NÑD›ÁšÃÑzëðÍX,fl9ï³!5‘”ÔÕì:ozs{õÄC»•wŠ 60÷7ö¸ÑçMDDDD:M1hã>L¬ï2_ÞÀ‘BW)sðíŒÊÍ_àòÀ9<2÷; ìæ<9ø:»/÷'¦Çu >«œ’Pîìgd¼—ÁþS5—‰KQ|ˆ=ïí$ã½d¼ç æžüs¶“ò›u®¶n¥ðq$LA0§x+ýÏì¿ì\òúµ‹d}’OظÛºÇ{ûÈñ(¶ÜmÜ55ÿ‹Ÿ±e_íSn½›ÛC»aÏÙίÖî¤Ø8¬dy f‸r‚=‡·¨ŸsúÂýL¼'¡Êߨž§ýšU¹¦q4dŠ_ÑgDW ûª*}ÞDDDD¤³PA ¿8•„pÈÝñlÕ[Ê•Ùpæ[¦23. `ÍzŸ ©ËY³ùðrVè&…kücì¯vÐs"ÏÚÏÓXƒjñ5»p¤sÜþ¯Äflé¼” h)¦Hî]šFB¤‰Ü«Yg¼•Z^%®ÿ´àõ§“Xå6—º§/@ÜÖ'iàù=Ôk©éœ­ãÌæwöpÿ[µùÞQ¡͘i*%Z‚×Pæ,û1ãÂ!wG:ëv#€ òŠ 9úòF>ÿÚít.ìb¢ó|äö éÈl8ì@Ø@FOZMqÄ„™Á^ÀÙÓžÆ–Ë¦È ø2̽¹SH¨¯sÝÆ.‘NH ‚æ0’‡–ýØ@›+9PûÜêýÇ n_¸˜±a^x…ÝÅ# Gäeî4>\·][‘–Ê¢ Æ.i·>æP®Ìý¹wñ48ß&ŸÜ»xƒ-`Ï=À~cËeq¦À–p„Uéè¼Â†á ö‚SÆž*†ÍOaEZ:ÉËf2À˜é@ntëÈoÒxQsSXçgl®d=@ÊÊÎÿ6 dNâBbk¨ú¶çl'umMóÀãYT~˼¢ý¤¤¿b 6ÉuÜr¶“RãqâY´l*·-tcËæÝgžewù‚•žÄº˜F/&yFJ>]ÍÓ[j¾Õaùû7wG"ëv¹uLXÊŠ)}±|îÆN 0r«VîŸ7ƒÐYI<>—Ó[—ó¾'dÑ|/i ·»>£¹ÛY·×#""""Ò1¨‚àFrãÕÕϱ+«{y›ÝFÞÁ͵$Ðm×:²â ^xf3ŸåT}?Ÿ é†~Ob]ûvsÒ±ÓªOMèlLqL¶cì­59Åβ+÷qgßo""""Ò¡©‚@¤ ˜ð?™Ò›²ƒëYµù˜±»Óˆš•‚r·­dÝÞºo_Øgôš1_òÙûL:oç#DDDDD:Uˆt"Å»~GÆy;¾q0'¦s^7ÅÌã~ØsvÖ™ˆšë\{àуðŠ>}SÉéД édv¿´“\»±ó—roDçJ˜zÆóÈüÛðµ`ÓúÚ¦ñ¸³c-ø’½]ÅÓ[:oÅ…ˆˆˆˆtšb ÒijhÉPŽ>WózVgÝn‘P‚@DDDDDDD4Å@DDDDDDD” %DDDDDDD%DDDDDDD%DDDDDDD%D¤9DÍJbEZ:É ï2vuZ¿y ‡-ÆÖJãF†°yAÜpcg+¨o|Òù=:…k†óïI¾Æ®VÓÆ ""Ò™)A mLLßy°Ä•ô¢]=Çã:Óô­ít[£uK–èÛoìnEãðüü>l^Yñ÷›cTËKz ‚¨®`îbì©tw´p]ûx3ÉØÙ¼ý-˜»ú2uVçHtý~®Â·üUÐãƒY¸f8÷ÍvljÚ¶Æã @·ˆž4äcôýHfÿ>Ž…k†WüUìÏ!Ì]3œ¹w3<«nžŽ¡1šsŸIÓtäcáѶÍî×!¾D¤c¸iЭ#¿566Å€¹)<y˜”ô׸wY:ã¼²rc•8SÏ‘Ü?{Ã"ƒ0Ø ÉÍÜɺ-ŸT‰kQ]¾C×[HÈ-¡t5\Ö½‡¼M¿6FzÆ“×m ±à:1ÿ)Aƒ` èŠó¼&‡¬•¡ûL,÷=HÏþA˜K’õÌÏ•<‰ºL~…ˆÛƒªe®.g|—¯?ž‰ï#ØÇÐéÎøo-Áçß&ØË³ÉÙä(¹@Ñ?×c=ôÏÊ8Où/Ágúd‚zû`®;J(=þùo©ò€=>˜™C½È{ÿsÞz­Jx»RÓ¶5VУ™çMéélJ/1vWáÿÈ-ÌÙÝØ\¹?G„0wAo8|„ͼj «•'ch¬æÜg-!ìÑ[ˆRÆþŸæÐŠ_]7D]Ç¢-쇦Œ¡®m«fv?N h÷ßGíŽë7¢ûo¹+GÞ¡à­~Ëyü»Ú ý­ìÉxEšÀxÖda~`-r=ŠÆÇ (ªúÓ×3Ÿýl.·—'ÌADŒ˜Ëò…­uùt$^óž¤Ïò:@W,}ã‰úño«†zÄ“×m ±Î“]¿%Ï9v>'Ž5Z@÷‡¶Ò÷'ÖßíØÕÄ“Ø ðÄÍ”pqÇ*²V~·â¯îæZtO¤ç‚Y„ô®L˜|B þ·'é92Î=ºáüé¹Èùºå/{³ÉŸ!³ˆüA¢!؃ýÛ’Zx g¶¬$%9ñ†%¦²W9¼ó,s7äTüµfr <&ï†ÝLñ_×™ØûI>s7äòH3%z‡XènºÉØ\£Ò‚6½J×°`Ô°ç4·àà|Ý?œ-á®ÞÄøC~æ×Æžö¯™·­ðùc¬ìó˜ÇÅuJ9¾õëû¼â¯©'7žŒ¡QšyŸµŸ îx×U~ÔQÔs,ÚÂ~hôêÙ6i¢Kø£ÕËùÆÎ"ò¡%†`W7”'¿•=¯HÓ4s‚ šž`+)t=îK /ض*QŽ‚ó[ó9ºm=)ɉ¤$'òDz±æè1LªëÊh3¹iÌcôêÝ•ëe9ämþ¹óDtc—ËàfŸ8B''ŸÒ ž¼n[ˆ…‘XfÍ¢gØ/áükËÝNÌ«^Yî6åûôêëCG!Ûö`­Ò[•'±•‚0{çöpéówÀëXŸ©LTùÛqP–³§"úæ»Fág‚²ã'ûóœqOÿ?r÷_ÀAWü†4îw»orÅëVüûþÎÅ0õŽÇ¯J]lÃ÷oÃy¶Zf mO÷n@^1©9×]­fáH_º–^b뿾1vµIìýŠWo¦÷p¾kìì ¢Çám¿È[›µX®M¸‘Ûækr/°ë]‡±«M»‘ûLªêÈÇ¢#o[‡qñ×J.ðõÎ5®ßC’ýQÀÔ÷Î*¡žý®n8~+{0^‘¦j¦)ñ,J›J„±Ù(g;)k3Œ­î]–θàËdþi¯ž6ö6§8¼ý–Ð%lœ5Ç­+ò·„ϋëx?YÏ% t_ò$½Àšñÿ(øØm Dôo Ÿ‡WE9'¯Ûb˜?Ðgön>÷9ÿ›î\ƒá tðW\Øôk ‘žI“ñ«­Ê“Ø ®¸sïUßXª(?FÈZ9¿¢µË}[éëÃ¥ó¸øùW•áþOÑë±;ñÊÞBö&CYV å[U§,¡Gâ,üKÜöa9×þ½éÈνáœbãÉþuN1€¯7ÿвÁÑcH$\/»ÀÅüë¡ú¦ßÔ¼<ƒgjøÜ×òš›Â‚8øìOk93z÷éÅ ØÎ‘ùÖ‹¼úYyÕQãýfA$Qy_1w{ÝåÌ ¿̸¾^®+íßpåb {3.±þ’1Ò³Xïèž÷£ôàYý¤ö$ÅoDåÞP˘ÇëÅOÃþ¿åsbPÓc¼ã(½Â]…¬<çúêŽ dó„ºt»rôì­9i72„丮œÈ8Ëe{=ãÕï.îŸ<žÁaA˜sǰœâ£×^dw®ëDrÂRVLékxfUփϱjsÐ+€i¿ì‡Ïñ/Ùô»ª‰jçg¶~Áá^QL€/@ée޾u†=Õ÷× Ä[¸½_wœ³ä”^,æ³Wsù2Óà*«/y?“ã1ƒ¹;ÂŒãÒYÞL½Lÿäâz˜¹š›Å_WV}õ}¼?wõs½îU.ž>ÏÏSx¹JXUulÀ}k†ãã*ï]>˜ÛC½0qÒ çÉø]yn¯}ߚᄹ?97‹õ†1Ý·f8auÅÕ2Å &iwG˜¸š{š¿®,®hoèœÇí:_l8EÞ˜¾•û­ŽãV¡ž}@Œ£¾ß›¡¡Þ®«tWÉ?|†7ÿXZâÝpœì1QL¨c Žu•˜×ÅjØ—žã.#üs_(Ñ¡Þ8W…¨áýë®û¡\³¾±ðp }íǨ!~˜o®Sz!=ÿ—OvyµY#ÇP¡¶msSu àà:&n®qŠA½ÛÖÈïz¿Ï«ï.<Œu7va*S£!wÇjÖíjèoŒš~C×ÔæRÓïj7õŽ¡Q¿•ÝÕ16‘&hæ ‚æà ¬ÌØÖÜðî­úa ù)¾SÓ  —«ñ]®lÊÀêßñOTÆv™‰ß}qxQBÁ[«\ž¼n[ˆÓHºRÈÅ 8qüü'®/±hhläo ¯XŒp2~½'W,Nô¢ë+ß>“píø¶*Íßì?J)à?åy‚ïû)]ºÇÑeðSô|äNºSBᇆä@ôo «¡|ËgÈd|#˃|¸É|s­òyår.p èæß¿¢É£ý @W¼¿û_„Ç:“7{…üoÿå6†ZÔ²<CK±0xþ2fƹ’–ÞÄÎ~¸qUCA>¼à¶a@X¯* n~À‡qnOIz ‚IýÝËð»Ð½‡?“f÷")Ø-ÐÃX€IÑ>tÅÆá/kOxÎÌÉá<4¸{å8¼»sÛ” v5Æ6ÎÁOòÉçfúF7õ£¹ÿÁéÄF–'Ìø"aáBFµðL‚šøÏèE¥œx·æéð-=â‡r_yrÀÛÁs¢Zù5 À+‡sWÅiÞ=zr×âÜi˜g‰ëÏÝÎ`òæ®äþÄõp>îÎ#*cc’†1©âÇ?@7zôëÇ̤‚«/©Q¡þmƒ®~=¹ï÷ÃêåúN»ïÐ>L~´î„R\‹–/F]eÂ…ÿb|V}žèJdUIx®+Ñß\u¿ÕrÜÜÕ»ÏÆ„0{Y q'6ÝÚ·Ê1sêBŸÙC˜Rm Ó¤XÏ4ôù~4ƒ+’T¾DÒ×ø>ó`?´äû·¡<CtÒ0&ÅTœ˜;÷Wo&Íö¯ØõmÛÐåÆ1€©–Ÿãžl›'ß;ž|Ÿyă÷ŽgŸ Obâm,DÜјe€¯q½â\ijßÕ•0††þV®—ûxEš®æo$e°.9‘”´Ý¹;œÓR^þ›ûã®,V˜FL0PtŒ¿ç;›YHó‹ëŠk*D·ÃòÐV¢ÜOp®®TyBvé×\܃ÃJßGþ?Lÿözú@Éþ_aÍr]öäuÛB,`ò÷G YKè·WÜ9 ï²—ðögP›–@÷10q³[^®Ú•ŸÄ…Íû¹RæƒoìýôýÉoé;ãN|9ämü¹!Ó:ßûâ°WÏe»æQg ךõœ?~ÊS¾S\-zŒ"ä»3]mý¹)ò§øüàN|üC+¢=ß¿]ñîáƒý\9˜GÖª_qöx ×ñ!`Ôc°›Ú÷ƒçch(×ç>9‘”äí仫1c±˜±å¼Ï†ÔDRRW³ë¼èMÌí-öøÝñaÜæ{×.²iså;Ï^ºqÛKÅí=‰-7$èf(-ã`=S§á¶>Âcg57ÓÝûf®œÍç÷q>çŸy×î ‰v…œ(ªx½íyV¶»ýs7äÔZ=P®à*t êZ%™ÒŽ¢l2·ý¥bêØSiëÙ{Þæþ í ÚõlEÿ»9Ù¼[ñ>rþ5Kõã ¾bÿcO93¾þf®^Èæ§œsè?ºàüˆšXöÄûÃÕ‹çØùìç|û§ŽðaîUÀ›Á U¯6ôèŽõðþ¼»Øùã¸GòvgòêÁRÀ‹€~θ®³ûqg„Éùï§^׿7cgÔ¾6DýÛÝ"BÃÊÑòuVŸ&Ϧ~=(û¼å¶†@Kýï7øñL¹Å»Æ+™x<ÞÞ&JsOóúÿ«ý¸Õ·ÏîœÝ› 4÷tÅñøsÚ—|p¼¤†Ò_3¾þÎ1”¿wv/¼‰_yÚÕàØ×NWlÿ¹Å|à¶OÖ?öyW¬zŒ¯].æèû_V¾ÞSGØwÁæ ºÏ-ЃýÐ"ï_÷ƒ§ca‚‹gyÕuÖ?ö9o¼ŸÏE›[b×Ã1Õºm®;Œ 5Á¥|v®u÷±ÏÙøQõRO·­¡ß;ž~Ÿy¢¡ï§|. Á¡,`#/óCcgíügâÓ®ŸâJ¾«ÍÃßÕ•9OÔ4^‘fÐL —è@|¸L‘ëÿî>!X¸LAýgŒŸ?†`.“¹õMcWóór~ eÐuúKDüì Âúúp3×(9²…ó§ª_¾þñ#|•}.½â ž¾–!>8νCþ?Üʾ=yݶËLÌþ€)’^càíSþE]¼œ ùõ^SV´åüœóóáßá2À¹wªÎ«¯«Ôªâªy-ɧ‹9”^¹æv‚7ûø`ö7Lˆü|à›¯28÷¿¿Æ~锳ýÒË”mYÌ•ŠdÂë\ÙŸƒƒ®øŒzÌUåð<ýæÝOHoçþs”ïãÆíß²#k8÷¿¿Æqå+øæŸ\Ûº‡À>ÄZ©Öýи1´{ÎvÒ־͙2 ì<;3Ëóù„Uû?ký KxÄx²÷U•㹯”°×>®¯—Ù¾µ„7ÜNâ׿sÃ¥@ˆWÅí=‰-à \²ó¡½©®åå“öNåçøÏ¸@@P#ΪEÁ%ç¤ò¦ÝV.‹¿­}–W÷®hq”ãm×1v»œÔ:¦GííàÌÁº¯T_ÍÍ⯩…¸ò¼‡8¸OPåµÖ˜~ÝÀ~‘=iùdqMíøê*_®<ÂñR 4°ê¾+Íg× WùÆîz|ñ´öä@c\ÍÍbÓÊÊ2뚎[õí³øÞD{ƒãÂi6­,®8ßœµqâw¹d~j|‚³Ì|ÓÊâŠ÷NöóȺõª~Ü<‰õHñþÔÓìyÍíŠöWWÉtí³*MöC‹¿ÀÓ1\µ~þÜW¹Õ¯ão¿«~úÚ(õl[Ü?à2ýîÙ™•3|mî O·­¡ß;Ÿ5”ïrž|.<‰øhýrR’—³æ†.<ˬ;ñ¡„‹ïü®²Ù£ßÕUy>OÔ2^‘fÐ, ‚¨¹)¬HKgŃ·bÁØÿHgEZ:¿¸3ðãö¥³"-…9®ì¥ÑøÅ©$„CîŽgyõD+,xTæ·'¯ÛbÝ\ÍÞÃÙ] ¯<½ŠsG qÐÿQCÛ÷«æŒwîÑ•«Ç·pæ¿#/»„ëÑÓpƒ›z;W½r¤þR¯ë?¹Œ“”–Q‘xpçP°ó %¸e™Ýx²¯}åZ¿ Ü7®ª…ZÕ³\<CK¹pĘÀh=Ý»6þÏØ,¸t%<ÈùؓتšaIƒó'Zþ„¥u4˜W¿{ùÁ²–§9ÿ°"-½ÞõZÊи˜J 9ü†±§ªÂõŸ°úš —Ȫ¡b8û+àEˆ[™«õô×ä¹ÅZ/Ôüé˜{0ñ·neúk†³ð·ýœ%üÞ5_7tÛ¸PÈ C™Ê‰•Ÿ³þ±Óß²­±ºô‰bJœ7Ž §›-9@›»úöY×>^t ŽÔ|bW“’¯ W‘m—(.…š–"÷$Ö# <Æ]F¿b0?tŸÕ0ÏÞ“ýÐâïßðt {Þ¿H©Ù—Á3†±pÍ`¦=LßµW:xª¾m³ø¥V»-‹TO·­¡ß;ž~Ÿ5”'ïrž|.<‰õÜH¼~J¯^׸œáVLãW·¬:Æ+Ò š%AÐh¦Hî]šFB¤©ö^Ì…Õß匫Ê!ç¹G°æ;¯Ä”]<êŠj¥ý[ç~h¥1tvN”kŒírsµ©íAsü¾2ÅÌã'?šHL°_³¼^“ fp(ŸøªåêÍázåU»–ÖêÛÖ4ßœýЬK` íÃäÙ×-4ì³®¹•]{1&„Ù úÑ¿Gù:µkñýЀcÑ’loä°é±ÏÙñQ>ù—L„ÜÒ‡I nãß“ša ‚l[Åú;mZã¾ÏZü½ÓRºüÞ¤ÞJ2Vñµûbäxú»ºÔ7^‘fÐ, ‚3›W’œÈ†ƒ—«Ì'}7°pÍ+5Ü™Àk(s–ý˜qá»#u»ê¹ix³Ú­ ¢­?çZ¡[æ-d2~@±17¯éñø™J(رŸRBk¸÷©'¯Ûbÿ‰ãàŠOµµ¥‹ñµYåWÍ ¹øÃbƒÌ¤k€B.}TõŠüõ¸®ô»-*sýb ׯÞuÍó¯ËH¼¾3/J°*_$°™öoÈ(|| æ2‚úöC3¡°;€` ß7vqÁ7ƒã*å%=‰-WlüÍTÖ¥Ü(&ü=ÌùûV{“®*¿{(¾€õÈ–*ë ¤ìÈ6†øPeùú¦ K&€ËÝÚ<iWí@¨?Ñ5,ºÖ·— ì6ò±òw± °çW™_å/å¢ñ);m-ÏÆžç³Èµ›‰˜8 i  5RCöYÉeg™°Ÿšæ37Ð`¼[íÿN…zcÍ4ªÎ»º¾ã{9?›ÇOð²k͆õ}Îú÷«_éõd?´Îû·îýИ1œýë9ÞL:Äú§¾ä‹‹ºED0!ÞU®î1”kȶ•”f/ŒuU11Õ¿´»mõi©ï3OÞ;µª÷sáÆ“ØÚt{ßEOÚ .g,çëÿiŒððwu kÐxEš®Yå‚}ß6Îj4= åFòвhs%Z©r Â?)Ë*t–šÏ[‹WˆkÕùDzÌ…(9jXèí¾ÿ"´wWÊŽü/ÖÏ“ø:³|âŸ^¾P¾n[ˆ…«GOò AÍXI×òÕ÷»ÏÄòƒû ô‚kç÷WĶ)®«æßdï©ea–S|sçÂ1cJ—î®d@·‘˜¿ç\×ý ýè)J®·Ì¢×t·øî3ñšµ–îµM‘ï>Óà§Z’Jxï®8Îí©’EnÌþ½Ù§r©¸›ÂŠÿ\×q;n˜z@CöCãÆÐ>wL~L½ß›»Ý~-™ÎPo¸v®”òªPObË)¼ݼÚ ¢+ßê,Ü6²+q\Ì ÷†k…×*Ökh /“°SRPù4n‹î4þ®T\b‚<¹yχöë†ãt~ƒÊx"ë¬s1·;ƒîã*GîÕØå·r‹7\=[H}išœ=[ æâ“‚èÛ°2çæÞ¶Vqæï¬Î"ÏîÍà dLóîz5hŸí.&ð¾¥?ß{Üÿòüq¯nÄ<Al %×],•²®c˜üH€ü#ÕÓx[b³݉¾Ï?c¯çºšL€ƒ’‹e”¸ÖlžÂ}cjxqöCK¿²<È`î[BÌ·/ǯlœ½èÌXz¸;5d å²mg¿r~—ŒyÜù?–.C|¸s…óÖŸFm›û}»âV®‰ã¡Úª-ÎçÍ÷‹¹jàîeQÄDšŸ'ûÌöÚiþ¶õ,y—ì•ÇÙ^JÞÁì*'Å5qØKÉ;x¢–“•ªê}7‡7wÍE>Çu)yá$;_¦´¢ÅNqn6omýªÆÏ¦'û¡Eß¿ Ü çùìÜý5KÝJÒíe®Ïq É>†oÛ§ù¼ùN~åë•^áÔîcüí}çÝiŒ¼mžhÌ÷Ù§ù½h®Sš[Xcrß;Fõ~.ÜÔÛÌ·ôèwu¹fƒH+jÖ ‘ÎlÀ  ~y§ö:®Ø·%ãîèÅOo5q&ã¿hb‚ M¸«7=BÉî¼ùìmyÛZJ í³Z«jàIl‡ÖBÇ¢MèÈÛÖB<ù\x+"Í£Y+DD:³“_òAÞuºîIR°±·mñöáá[»q-ïkþÔ’@ôØ ¼íùbkÇû‘Þ‘·­¥hŸµùXtäm‘ÎI ‘f´~ûYÎ\íÆm“{°¤–¹£7š·¿œD€µõÛ¯rÒÐNe¥býOsj¼¿w{ב·­¥hŸµùXtäm‘ÎI ‘fö‹—r8sí:¥ ´-¥%epÍÊö¿—ð±SDDDD:-­A """""""ª %DDDDDDDD A A A ‘ö/jV+ÒÒI^x—±«yij(i)ãŒõô‰ˆˆˆˆH»¢ÛŠ´¢¨É‹™9.’@³¹¢-wG"ëvU ó@4s’–ë Í»ÉϲÛB<‹Ò¦áÞ”³”µî-53 dNâBb½²y÷™µìþÚQµ;b‹Ž'ŒlÞ]ý,»‹«t‹ˆˆˆˆH;¢ ‚bòȤ&‘œ–Ί¤yÆî ¦ž#ùÞâ$–?•Ί4çßò¤'˜s{°1TÚ¹ÐéI,¸»•ä@Óe±ÿx!¶ãŸÔhšñ &Ö÷2™/UO8rßdÝK°šûÿ`¼±[DDDDDÚU43SÏ¡Lš1“QÑ~TœZ²rcÕ@Ÿ»X”8ˆÏíän[ɺ½Vc‡´Kq<”2ŸÁ–ËݺMûÎZ‰«š >–ò³)})ûô9VmÉ2vW1`n ÇYÈۑΚ]EÆniTAÐ̆ϘǸh?Ìö|2_ûˆ çØðÄP¦Ýݳí Þ~£îäÀÉ-;9m7v÷4;EDDDD¤]P‚ ™íÏïnVÃc#1Sȉ½ ¯v¸°÷E˜éSgå‹Þ¿"""""m•7RÞëüáÏQ`³6b&/çñÇá8Å®?¥ó·ÓÆ'dp(ËØÈËüÐØ)휣(›Ìm©˜nòTÚzöž·ƒ¹?ÃG»Gf°Î“’¼\÷®&‹&*Ø ÖŽÖ5GÆ(ïl`îoìq£÷¯ˆˆˆˆH[¥Á æ(È!¯Ä0•À7à@ߪmn>Z¿œ”äå¬y§áWwå8½‘UÆ“øœí'ÿ)ɉ†Å+³øÛÚgyuïáŠGÉ1ÞÎtçV« èK /`=ÏIcWRTâ|ÿŽ7v¹ÑûWDDDD¤mR‚àF ˆgѲ¹Ä[(:²™ß§=Ç®¬Ë@ƒg/cѸړÒ1yõ»—,Ka¹û4„)}a­£!ks]"""""Ò~(Apš=Éyƒƒëyú¥Oøº$‹ëWðû=ù€™ˆ;5G»31ÅÌã'?šHL°Û-2o$K#T­Vå """""ÍM ‚Æ5Ï›BŽî:V¥çë·>q–£†×Yª-Ëð»‡â‹ó¶—U¦!ìÈ6†¶°lЬ€O ÃŒ]uH¨/`-b·±KDDDDDÚ<%n˜,JÊ|‰™0†>>•—^ÝG(@ÑùO´t›¸ŽÉËdì”äT´ 7Ew¶öƒ,ÎØÁ΀0c_†á ö‚SÆž*†ÍOaEZ:ÉËf2@"""""mÆMƒnù­±Qš`ÂÒºçŒ[T,Lg½˜äýk)'·“»m%ëöZ íñ,*¿e^Ñ~RÒ_1ôKÛä:n9ÛIY›aì ô¾D¿3ÄØ\!wG"ëv¹Ôó>³|ŽU›³œ<‰u)o–|ºš§·4l1ÁÐYI<>—Ó[—ó¾Ú0ˆæ{IK¸Ý5{!w["ëöcDDDDDäFPÁ 䨷–UzŸ6ì­v¬_²ëO©5$Ðmâ:° o¥óúÁsØÊß v;EY»ÙøÚjz'´$Ǿݜ´A`ì4F5ä*¿)މC‚ÀvŒ½µ&²ØùAvåû½!¯-"""""­B"R£€ Oð“)½);¸žU›«®“a5+…#,µT½TÕgôš1_òÙûL:oç#DDDDDäFPˆÔ¨x×ïÈ8oÇ7îæÄÔ~©ß3FøaÏÙYgr j®síGg Â(úôM%DDDDDÚ%D¤V»_ÚI®ÝØùK¹7¢z’ÀÔ3žGæß†¯õ›Ö×¼¶BUÎ)4{ÿºŠ§·Ô]• """""­KS D¤nñ,Z2”£Ï=ËîbúDDDDD¤]Q‚@DDDDDDD4Å@DDDDDDD” %DDDDDDD%DDDDDDD%DDDDDDD%DDDDDDDÝæPÚŸhæ$-!ÖìöBξ·‘våƒZX<‹Ò¦áÞ”³”µî-mVŸ ‹yàžHÍf° eåFcˆˆˆˆˆˆtBª h!&ŸLúaÉié¬Hšgì®0’ï-NbyZ:+ÒÒYñT ÍiŒ’˜ÍAô›²˜Eã|]R Ÿ KydJgr@DDDDDÄ*š™©çP&͘ɨh?*NÁj»JÏ¢eS‰¨á\Íž³ÔvrEúF0=‘‡îÁœó)k?4v·W5A;© »8©‘v þõ"x㘱[DDDDD:1U4³á3æ1.Ú³=ŸÌ×>"ÏàfÔìID˜Ázd )ɉ¤$'òëg¶pÔ æÈxl|†¸;ùÆ»œ´•©©‡ ÀvŒ %DDDDDÄ KÏðÿ66Jã7E1¼k&›Ö¼ÈÞÜP†ßƒÿµ d|xÈ9†)Óo%ðò~~µöŠVGÉY aÔˆüÈaÏá‹Už0va*ÎŽ'Æ~€O³ËŒÝHwÝ9Š^e'鿊α³Q†Ï}‚G˜EBB'ŒgX”_}y’b‡1²\?F܃ÿ¥ºÇ0|ÖRzpS˜xO–iÆ~~7W¥’œÈSiëÙ•cßÛH¸§± !Æ3,Ò ñ|ZRÅT’Û¾  Ì^%²åÆàä3“áÁ€Ãfìу–å*?¯qŠAçoȨAøGÞÎÄ{˜xOãF !Ì¿›3¤4‡Œ½G σ³Ÿeñ^ûOY]NV/  ß°±L¼'Q=O±çp‘1¬^wÜ;‡[¼OñÖÓÉ,uÖò_¿v‘¬08>†°›‹Éø¤¦Û)Ö7Å ”¸ CôêFׯq$¯€¯sò¯U_¯ñc¨[ÔÜ~>ï~îA÷kçØ÷Ú_9^X}jƒˆˆˆˆˆtnª ¸Šw=Ë ;¾¤ÀVy%Ù^”Mæ¶ýÎÅ ­žŸèv:eš>5?šÐ@ÀÜŸi©®ÛM–ÿ¥N% À7Äø¤ú˜×ß:…ÍBìì'·±œ?“Qý¼ q-9‘ú)ApƒÝµ?¬H®(=OM–WóÌøÖ‚lc¸ÄOO„å2™/§’’œÈªÍYÆ®xßZÒRWóÊž/È+±6d,Ó~´‚äÅñÆÐqfó R’ùãÖ/°Zz3z²Ö®‘êntëÈoÒ\âY”6•ëRVn4vÖjüâ4"mdþi¯ž6öJ¥hæ$-!Öº”g3Œ¹wY:ã|¾à•/bœ R7×1ÎÙNÊÚ†ÁäÇýKær{ ƒ£//gS¦³½ñch¸{—¥3Î˳÷£ˆˆˆˆˆtª h#L>ÁDÅNã‰i$Dš±çdÔš»0•i©,šhìꜚ>Ç€Ïs Ár+÷/žÆ°ˆ¦-XE¿X´xcW.8é(9ÈÉ<`Áǧ2´ÅÆà¦D늈ˆˆˆH-TAÐÜ&,eÅ”¾ÆÖJîÕýæñänÃx*h/øˆMϼÎÉO|]W¬Šö“’þŠ1 qU5üê}­L™“¸XãÁpÉݑȺ]®õcëÁç*§:ÔrŒ°ŸâÍkÙ_~œ=C#_œNB *DDDDD¤:U´v;Ö‚/ÙûòjRW×–@·9l)Žc¼ºú9vÏÇZõîƒMsz#¯|ð%î/j¿LÁñ÷Ù°Ò-9@ ŽADDDDD¤TA íØyjýòš¡‚ “¿8„°S¼ù˵ì7vŠˆˆˆˆH§¦ iŸ¼Â5ýÌ`whb}C•9ì`îOüü1Æ.éäTA íŒkÝŠyú6޾œÊ¦ÌZçdˆSìÃ<ùà­XÊ<¼Ã†ˆˆˆˆˆt\ª vËnËçè¶g”ð€#óEÖmû‚›9‘ªTA """""""ª %DDDDDDDD A A A ‘N* žEIK`ìèà:ëv‹ˆˆˆˆ4€ns(ÒŠ¢&/fæ¸HÍæŠ¶Ü‰¬ÛU%¬e™2'q!±^Ù¼ûÌZví0FtX¦ˆi,Z8ž0²ywõ³ì.6Fˆˆˆˆˆt^J´o3M$.Ì‹®Ž2Î.bÍ'vNƒéãz0=ƛ€o(>{‘ÿQÆÞoŒ‘Òž…NOâñ;‚ŒÍ­ž ¿8„H™^É«':FrÀä3øÙ3uKëRVn4†T0ÅÌãgÿq^9ÛI]›aìé´” hfÞÁ>¬žDMÌײ/ðïÿ¸V¥í᩽™Ö¥JÖBÖ¼RÂÆvi§âx(e>ƒ-—9ºu›ö7´ Ÿ KùÙ”¾”}ú«¶d»ÛSÏ¡Lš1“QÑ~TÔdÔ“ 07…‡ã,äíHgÍ®"c·ˆˆˆˆH§Ô©× ÈóS-Œ4v4AiA ÅÖRï:Dz 9ÌÝCÚ+×€®}}Yص26<&ï†uR+;ßtÆ.ú[>gJ¿ß@fìÔ‡§ƒ ÂÇä|xÃ’0”iw÷Ålû‚·ßhÿÉ€á3æ1.Ú³=ŸÌ×>"ÏP‹“[vrÚn&ìîi 2vŠˆˆˆˆtR]z†„ÿ·±±³¸=Ú‡Q˜ó-W²®r²™ª­ÿq¸” ¿ÅêzüÕ×e„öñ'ªû·X³®ð¡ÍÙ>ÿÎúw¿Êm_ó‡‹Î¶«6ÿ8©ƒ½ ôºÆ‘£v *^ÙéáÉá$Ç—Q%%ü£ÐÐ)mT?F܃ÿ¥d|rÆØYÉ+šIóòà÷¾GBBï‰gTl7Ÿ;DÎåëL£g1}X—37òÆáòwgUQsSøù¼» È:Šeò"y`–óµÇ ¥gÉ1Žä•ŸrC7E1¼k&›Ö¼ÈÞÜP†ßƒÿµ d|xÈZÕõ³äõÉèÈp|­ðù¹êû«ÜØ…©<:;žû>Ín[Û/""""Òœ:u‚ ë|)‡.ÝĘþŒ¾ÅLT~){KŒQÍcÔ@¢ºÛ9•YÊ¿\³ æŒ÷' ¸˜ç?«LøûtågñDzß ^ßPúyÝ_xè;p3þNµó•¡_Úˆ~óxòÉ0åž&Þƒ?€ ïI¨üRy2kÈœŸ?Êèðî˜+ft¡[÷žô5šž9{9Rè:‘õ$ÖeĽ3x‰C[Þæx-ïó€[ïæöP CÆÛ' òµÍ~ôé_“Uu–Ìuîs>úì$Å*“0 I%Å! ¿£/=»^âƒÏλ]â™:{þ˜ñïáEÆÞÃÆ‘£Ó×°ŸÌ*fékœÃ›Q÷…ò«è›Œ!ÍbHP|…ÜN̼lßpÄõxáäPÖÍ å¶&WKW‚ý+ãË9ëœûÒV-y í× çë ö‚¼ùL )ɉ<•¶ž]96ÀØÉ“ëMT°¬9­·ߌÅbÆ–ó>RII]Í®óv 71·—¿7;€¼S\°9¸¿±ÇM‡²l€¼Ì"""""J§® (g¿êàÌK ‰dÐ`?&YJÙ–[{ɱ§NíC¬ï5¼[Ìk¥®Æ æ ¶@~)×Ãø¯„žÜâ×°sîh>^¶ô-Ç*§$”;xÊÊkŸ[y§@ëK¶iŇØóÞN2ÞÛIÆ{bî‰Á?g;)¿YçjÛév¥;Ž„é#æo¥ÿ™ý—ó]®_»HÖ'ù„»à ›q¼·bËÝÆ]Scð¿ø[öÕ>ÅÁYAÐ {Îv~µv§óʼÃJ–×`&€+'Øs¸q‹ú9§/Ü_µ‚âžFõ<Õè׬ʳ øŠ>#ˆ¼jØWUý,ƒŒ÷2تæi"""""E§¯ p—º%—Ö›ÆówÔpgFxxr8“¾åÌ®|Vº/&PX kÿìMW¾åÊÙ|~ÿ—<~¶×Ýœÿ¾]·:ì$\‹æcµµ0s"ÏÚÏÓXƒjñ5»p¤sÜþ¯Ä|éÌ” p“4+‚Û|¿¥øhþ«égæKîïÍÔ>7qfÏy~q¢úÕ~g1Á·\»XȦ͹<òN{]ÿ¬¿ÏMÀ5 .U}Žtn <¿‡Úb-¾Æ–Vsfó R’«ý­Ú|ï¨ÐfLˆˆˆˆˆ4•€·¿…§çGp[€gyto§VIšÁwBàÌ®óüâËêÉ€sÅW9˜QÂî‹ÆùwghP|•ýnÍÒ‘ÙpذŒ2ž´šâˆ 3ƒ½€³§=-—M‘ð d˜{s§6P_ÀZÄnc—ˆˆˆˆH'Ôézû±zv0½±±ÿ­ üWVÍ'óžXþ@·|ãLÔP9PîƒlàŨ©<ìãló÷鯝¦À·œû²´bCwOgó‚p~Ó2 *Êð1‡rí`îϽ‹§1(À “Ï@î]<‹Á°çp%Œ<‰-—Å™;XÂV¥£ó J„/Ø N{ª6?…ié$/›ÉcBFDDDD¤¹iЭ#k?ƒíàîÙ“…qÞtµó—¿_æíf˜]ÍÓ[ÎWít‰š›Â‚8?rw$²n—[Ç„¥¬˜ÒëÁçnì”#׸je=@ÊÊÆVBg%ñø_No]Î ûj«Šæ{IK¸Ý53#w["ëÊ?¼"""""L§® ((ù†Ò¼Ò_ižä@c¬|%—g˨¸µ¼Ãι£y¬¬%9€nsØqgðÂ3›ù,§{y›ÝFÁñ÷Ùn8á÷$Öűo7'm;­úÔ„ÎÆÇÄ!A`;ÆÞZ“Yìü »rwöý&""""Z§® él&<ÁO¦ô¦ìàzVm>fìî4¢f¥°`„…Üm+Y··îÛö½€‡f —|ö>“ÎÛyÆ‘Ž¡SWˆt6Å»~GÆy;¾q0'¦s^7ÅÌã~ØsvÖ™ˆšë\{àуðŠ>}SÉéД édv¿´“\»±ó—roDçJ˜zÆóÈüÛðµ`ÓúZÖ‚¨Â޵àKöþuOoé¼""""Ò9hŠHgÏ¢%C9ú\ÍëtXu»EDDDD@ ÑQ‚@DDDDDDD” ” ” ” ” ” é\î×‹Í "xþŽ.Æ.éä” é,ü½™=ØDþÅkÜÄOü"""""Ò™)A ÒI,™ÐƒüB~¿õ"‡K-Ü9ÁÂcˆˆˆˆˆtZJˆtÞ‘Œ ¹Æ=6Nâàö\æZHt“1TDDDDD:)%D:ÿ¼Ó²‹XYè|\šSÌyÐ{dw5F‹ˆˆˆˆHg¤H7îŽ^ õ¾ÂÞ=ת´¯ßSH~7_¾;A›Ý:ò[c£ˆˆˆˆˆˆˆt.ª %DDDDDDDD Q‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDP‚@DDDDDDDntëÈo"mW4s’–ë v{!gßÛÈ »rŒA""""""â!%Z€©çHîŸ=‰a‘A˜ì…äfîdÝ–OŒ¡˜|?{&£n Âb=@ÊÊÆ©P™ p²“»m%ëöZ«†‰ˆˆˆˆˆˆG” hf¦˜yüì?n£âüÕ=k ©ë?®xlê9”I3f2*ÚÏ™HP‚ ÁLOä¡;B0ç¼AÊÚÝ""""""â­AÐÌç)¶æstÛzR’IINädcÌÑc˜äS;|Æ<ÆEûa¶ç“ùÚG乿ÔëäïrÒT¦WDDDDDD¤‘” hnŬ[™Î¦½Ç*š.¼ó,û | ® ÝŸyŠ¢¬÷Ù°2W?+ÂQÙU§± SY‘–Ê¢ Æ®NÆŠ½¡;MDDDDDDê¤A«rPVæöpßž^ÿ6gÜÛêϰh `!âŽIÆÎÎÉdlO)AЦ ãïMžGÁ¡,`#/³³Ï»Ï¢Ì„ßÅC£Ã""""""â%ZÁøùcæ2™[ß4v5ÊGë—“’¼œ5ïœ7vu:ooýˆ<›ƒg,cEZ:OÎ6†ˆˆˆˆˆˆH(AÐÂÆ/N%!rw<Ë«'4a¾Ù•9¼vƒˆˆˆˆˆˆÔN ‚–bŠäÞ¥i$DšÈݱšu»ŠŒÒ â§'Âr™Ì—SIINdÕæ,cˆˆˆˆˆˆˆ4€-Ák(s–ý˜qá»#u» ŒÒ,¢ ðÎÈ«™Vc§ˆˆˆˆˆˆx@ ‚æ0’‡–ýØ@›+9Ðü•ºÍ¡æˆˆˆˆˆˆ4ÙMƒnù­±Q/jn âüŒÍ•¬HY¹Ñùß–²bJ_cD%÷Ø ñ,J›J@Ñ~RÒ_1ôw&ÑÌIZBlÑvRÖf;EDDDDDĪ hwt›Ci~ª vl <5‹~yª i*UHûäΨù“èg»Ãfì©‚@Ú׺¾åm}9•M™Z©PDDDDD¤)TA í–Ý–ÏÑmÏ(9 """""Ò TA """""""ª %DDDDDDDD A A A A·9ˆfNÒb}Án/äì{yaWŽ1HDDDDDD:8%Z€©çHîŸ=‰a‘A˜ì…äfîdÝ–OŒ¡ŶŒÊ“Üm+Y·×Z5LDDDDDD:4%š™)f?ûÛ¨8ßvcÏÚBêú+{ÛLOä¡;B0ç¼AÊÚÝ""""""Òuéþ߯Fi¼ë×{24ÖŸó;ÿ?üåÿÈxo'GM€9ÐÓ¿>&ëšç±­¡ðØz‹%¸4‹ŒOλEDDDDD¤Ó"…Í­8ƒu+ÓÙ´÷XEÓ…wže€/ÁŒu3va*+ÒRY4!ÐØÕDVìc›ˆˆˆˆˆˆtJ´*eeƶÚÔϰh `!âŽIÆÎæa26ˆˆˆˆˆˆHG§Ak˜FL0PtŒ¿ç; êÍàP– °‘—ÙÜëdQæÂïâ¡ÑáÆNéÀ´Ha+¿4„p™^É«'ê®á÷$¶%˜bf²èÁ±„Yœ­ŸcÕæ,c˜ˆˆˆˆˆˆt0ª haã§’¹;ž­÷„ß“ØSæàýË""""""r©‚ ¥˜"¹wñbÆ…CîŽÕ¬ÛU`Œ¨äIl ›´4 á—É|ùw¼ši5v‹ˆˆˆˆˆH¥ ‚–à5”9Ë~ì:áO¯û„ß“ØM€/pþC%DDDDDD:%š[ÀHZöCbm®þ"cD%ObÝ´Üm]4Ç@DDDDD¤Óуf57…q~ÆæJÖ¤¬ÜÆVŠgQÚT"Šö“’þŠ¡¿)¢™“´„Ø¢í¤¬Í0vŠˆˆˆˆˆH¦ ‚v§%os(""""""•*ÄÍyjýòTA """""ÒÙ¨‚@œ¼Â5ýÌ`wØŒ½""""""ÒÁ©‚ Ós­;à[þØÆÑ—SÙ”©• EDDDDD:UH»-Ÿ£ÛžQr@DDDDD¤Rˆˆˆˆˆˆˆ¨‚@DDDDDDD” àÿ­O^ÌòPIEND®B`‚cpptrace-1.0.4/sonar-project.properties000066400000000000000000000005421504061443700202260ustar00rootroot00000000000000sonar.organization=jeremy-rifkin sonar.projectKey=jeremy-rifkin_cpptrace # relative paths to source directories. More details and properties are described # in https://sonarcloud.io/documentation/project-administration/narrowing-the-focus/ sonar.sources=src, include sonar.sourceEncoding=UTF-8 sonar.cfamily.compile-commands=build/compile_commands.json cpptrace-1.0.4/src/000077500000000000000000000000001504061443700141105ustar00rootroot00000000000000cpptrace-1.0.4/src/binary/000077500000000000000000000000001504061443700153745ustar00rootroot00000000000000cpptrace-1.0.4/src/binary/elf.cpp000066400000000000000000000462011504061443700166510ustar00rootroot00000000000000#include "binary/elf.hpp" #include "utils/error.hpp" #include "utils/io/base_file.hpp" #include "utils/io/memory_file_view.hpp" #include "utils/optional.hpp" #include "utils/io/file.hpp" #include "utils/string_view.hpp" #if IS_LINUX #include #include #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { elf::elf( std::unique_ptr file, bool is_little_endian, bool is_64 ) : file(std::move(file)), is_little_endian(is_little_endian), is_64(is_64) {} Result elf::open(std::unique_ptr file) { // Initial checks/metadata auto magic = file->read>(0); if(magic.is_error()) { return std::move(magic).unwrap_error(); } if(magic.unwrap_value() != (std::array{0x7F, 'E', 'L', 'F'})) { return internal_error("File is not ELF {}", file->path()); } auto ei_class = file->read(4); if(ei_class.is_error()) { return std::move(ei_class).unwrap_error(); } bool is_64 = ei_class.unwrap_value() == 2; auto ei_data = file->read(5); if(ei_data.is_error()) { return std::move(ei_data).unwrap_error(); } bool is_little_endian = ei_data.unwrap_value() == 1; auto ei_version = file->read(6); if(ei_version.is_error()) { return std::move(ei_version).unwrap_error(); } if(ei_version.unwrap_value() != 1) { return internal_error("Unexpected ELF version {}", file->path()); } return elf(std::move(file), is_little_endian, is_64); } Result elf::open(cstring_view object_path) { auto file_res = file::open(object_path); if(!file_res) { return internal_error("Unable to read object file {}", object_path); } auto& file = file_res.unwrap_value(); return open(make_unique(std::move(file))); } Result elf::open(cbspan object) { return open(make_unique(object)); } Result elf::get_module_image_base() { // get image base if(is_64) { return get_module_image_base_impl<64>(); } else { return get_module_image_base_impl<32>(); } } template Result elf::get_module_image_base_impl() { static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); using PHeader = typename std::conditional::type; auto header = get_header_info(); if(header.is_error()) { return std::move(header).unwrap_error(); } const auto& header_info = header.unwrap_value(); // PT_PHDR will occur at most once // Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011 // It should occur at the beginning but may as well loop just in case for(unsigned i = 0; i < header_info.e_phnum; i++) { auto loaded_ph = file->read(header_info.e_phoff + header_info.e_phentsize * i); if(loaded_ph.is_error()) { return std::move(loaded_ph).unwrap_error(); } const PHeader& program_header = loaded_ph.unwrap_value(); if(byteswap_if_needed(program_header.p_type) == PT_PHDR) { return byteswap_if_needed(program_header.p_vaddr) - byteswap_if_needed(program_header.p_offset); } } // Apparently some objects like shared objects can end up missing this header. 0 as a base seems correct. return 0; } optional elf::lookup_symbol(frame_ptr pc) { if(auto symtab = get_symtab()) { if(auto symbol = lookup_symbol(pc, symtab.unwrap_value())) { return symbol; } } if(auto dynamic_symtab = get_dynamic_symtab()) { if(auto symbol = lookup_symbol(pc, dynamic_symtab.unwrap_value())) { return symbol; } } return nullopt; } optional elf::lookup_symbol(frame_ptr pc, const optional& maybe_symtab) { if(!maybe_symtab) { return nullopt; } auto& symtab = maybe_symtab.unwrap(); if(symtab.strtab_link == SHN_UNDEF) { return nullopt; } auto strtab_ = get_strtab(symtab.strtab_link); if(strtab_.is_error()) { return nullopt; } auto& strtab = strtab_.unwrap_value(); auto it = first_less_than_or_equal( symtab.entries.begin(), symtab.entries.end(), pc, [] (frame_ptr pc, const symtab_entry& entry) { return pc < entry.st_value; } ); if(it == symtab.entries.end()) { return nullopt; } if(pc <= it->st_value + it->st_size) { return strtab.data() + it->st_name; } return nullopt; } Result, internal_error> elf::get_pc_ranges() { std::vector vec; auto header_info_ = get_header_info(); if(header_info_.is_error()) { return header_info_.unwrap_error(); } auto& header_info = header_info_.unwrap_value(); auto strtab_ = get_strtab(header_info.e_shstrndx); if(strtab_.is_error()) { return strtab_.unwrap_error(); } auto& strtab = strtab_.unwrap_value(); auto sections_res = get_sections(); if(!sections_res) { return sections_res.unwrap_error(); } const auto& sections = sections_res.unwrap_value(); for(const auto& section : sections) { if(string_view(strtab.data() + section.sh_name) == ".text") { vec.push_back( pc_range{to(section.sh_addr), to(section.sh_addr + section.sh_size)} ); } } return vec; } Result>, internal_error> elf::get_symtab_entries() { return resolve_symtab_entries(get_symtab()); } Result>, internal_error> elf::get_dynamic_symtab_entries() { return resolve_symtab_entries(get_dynamic_symtab()); } Result>, internal_error> elf::resolve_symtab_entries( const Result &, internal_error>& symtab ) { if(!symtab) { return symtab.unwrap_error(); } if(!symtab.unwrap_value()) { return nullopt; } const auto& info = symtab.unwrap_value().unwrap(); optional&> strtab; if(info.strtab_link != SHN_UNDEF) { auto strtab_ = get_strtab(info.strtab_link); if(strtab_.is_error()) { return strtab_.unwrap_error(); } strtab = strtab_.unwrap_value(); } std::vector res; for(const auto& entry : info.entries) { res.push_back({ strtab.has_value() ? strtab.unwrap().data() + entry.st_name : "", entry.st_shndx, entry.st_value, entry.st_size }); } return res; } template::value, int>::type> T elf::byteswap_if_needed(T value) { if(detail::is_little_endian() == is_little_endian) { return value; } else { return byteswap(value); } } Result elf::get_header_info() { if(header) { return header.unwrap(); } if(tried_to_load_header) { return internal_error("previous header load failed {}", file->path()); } tried_to_load_header = true; if(is_64) { return get_header_info_impl<64>(); } else { return get_header_info_impl<32>(); } } template Result elf::get_header_info_impl() { static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); using Header = typename std::conditional::type; auto loaded_header = file->read
(0); if(loaded_header.is_error()) { return std::move(loaded_header).unwrap_error(); } const Header& file_header = loaded_header.unwrap_value(); if(file_header.e_ehsize != sizeof(Header)) { return internal_error("ELF file header size mismatch {}", file->path()); } header_info info; info.e_phoff = byteswap_if_needed(file_header.e_phoff); info.e_phnum = byteswap_if_needed(file_header.e_phnum); info.e_phentsize = byteswap_if_needed(file_header.e_phentsize); info.e_shoff = byteswap_if_needed(file_header.e_shoff); info.e_shnum = byteswap_if_needed(file_header.e_shnum); info.e_shentsize = byteswap_if_needed(file_header.e_shentsize); info.e_shstrndx = byteswap_if_needed(file_header.e_shstrndx); header = info; return header.unwrap(); } Result&, internal_error> elf::get_sections() { if(did_load_sections) { return sections; } if(tried_to_load_sections) { return internal_error("previous sections load failed {}", file->path()); } tried_to_load_sections = true; if(is_64) { return get_sections_impl<64>(); } else { return get_sections_impl<32>(); } } template Result&, internal_error> elf::get_sections_impl() { static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); using SHeader = typename std::conditional::type; auto header = get_header_info(); if(header.is_error()) { return std::move(header).unwrap_error(); } const auto& header_info = header.unwrap_value(); for(unsigned i = 0; i < header_info.e_shnum; i++) { auto loaded_sh = file->read(header_info.e_shoff + header_info.e_shentsize * i); if(loaded_sh.is_error()) { return std::move(loaded_sh).unwrap_error(); } const SHeader& section_header = loaded_sh.unwrap_value(); section_info info; info.sh_name = byteswap_if_needed(section_header.sh_name); info.sh_type = byteswap_if_needed(section_header.sh_type); info.sh_addr = byteswap_if_needed(section_header.sh_addr); info.sh_offset = byteswap_if_needed(section_header.sh_offset); info.sh_size = byteswap_if_needed(section_header.sh_size); info.sh_entsize = byteswap_if_needed(section_header.sh_entsize); info.sh_link = byteswap_if_needed(section_header.sh_link); sections.push_back(info); } did_load_sections = true; return sections; } Result&, internal_error> elf::get_strtab(std::size_t index) { auto res = strtab_entries.insert({index, {}}); auto it = res.first; auto did_insert = res.second; auto& entry = it->second; if(!did_insert) { if(entry.did_load_strtab) { return entry.data; } if(entry.tried_to_load_strtab) { return internal_error("previous strtab load failed {}", file->path()); } } entry.tried_to_load_strtab = true; auto sections_ = get_sections(); if(sections_.is_error()) { return std::move(sections_).unwrap_error(); } const auto& sections = sections_.unwrap_value(); if(index >= sections.size()) { return internal_error("requested strtab section index out of range"); } const auto& section = sections[index]; if(section.sh_type != SHT_STRTAB) { return internal_error("requested strtab section not a strtab (requested {} of {})", index, file->path()); } entry.data.resize(section.sh_size + 1); auto read_res = file->read_bytes( span{entry.data.data(), to(section.sh_size)}, section.sh_offset ); if(!read_res) { return read_res.unwrap_error(); } entry.data[section.sh_size] = 0; // just out of an abundance of caution entry.did_load_strtab = true; return entry.data; } Result&, internal_error> elf::get_symtab() { if(did_load_symtab) { return symtab; } if(tried_to_load_symtab) { return internal_error("previous symtab load failed {}", file->path()); } tried_to_load_symtab = true; if(is_64) { auto res = get_symtab_impl<64>(false); if(res.has_value()) { symtab = std::move(res).unwrap_value(); did_load_symtab = true; return symtab; } else { return std::move(res).unwrap_error(); } } else { auto res = get_symtab_impl<32>(false); if(res.has_value()) { symtab = std::move(res).unwrap_value(); did_load_symtab = true; return symtab; } else { return std::move(res).unwrap_error(); } } } Result&, internal_error> elf::get_dynamic_symtab() { if(did_load_dynamic_symtab) { return dynamic_symtab; } if(tried_to_load_dynamic_symtab) { return internal_error("previous dynamic symtab load failed {}", file->path()); } tried_to_load_dynamic_symtab = true; if(is_64) { auto res = get_symtab_impl<64>(true); if(res.has_value()) { dynamic_symtab = std::move(res).unwrap_value(); did_load_dynamic_symtab = true; return dynamic_symtab; } else { return std::move(res).unwrap_error(); } } else { auto res = get_symtab_impl<32>(true); if(res.has_value()) { dynamic_symtab = std::move(res).unwrap_value(); did_load_dynamic_symtab = true; return dynamic_symtab; } else { return std::move(res).unwrap_error(); } } } template Result, internal_error> elf::get_symtab_impl(bool dynamic) { // https://refspecs.linuxfoundation.org/elf/elf.pdf // page 66: only one sht_symtab and sht_dynsym section per file // page 32: symtab spec static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); using SymEntry = typename std::conditional::type; auto sections_ = get_sections(); if(sections_.is_error()) { return std::move(sections_).unwrap_error(); } const auto& sections = sections_.unwrap_value(); optional symbol_table; for(const auto& section : sections) { if(section.sh_type == (dynamic ? SHT_DYNSYM : SHT_SYMTAB)) { if(section.sh_entsize != sizeof(SymEntry)) { return internal_error("elf seems corrupted, sym entry mismatch {}", file->path()); } if(section.sh_size % section.sh_entsize != 0) { return internal_error("elf seems corrupted, sym entry vs section size mismatch {}", file->path()); } std::vector buffer(section.sh_size / section.sh_entsize); auto res = file->read_span(make_span(buffer.begin(), buffer.end()), section.sh_offset); if(!res) { return res.unwrap_error(); } symbol_table = symtab_info{}; symbol_table.unwrap().entries.reserve(buffer.size()); for(const auto& entry : buffer) { symtab_entry normalized; normalized.st_name = byteswap_if_needed(entry.st_name); normalized.st_info = byteswap_if_needed(entry.st_info); normalized.st_other = byteswap_if_needed(entry.st_other); normalized.st_shndx = byteswap_if_needed(entry.st_shndx); normalized.st_value = byteswap_if_needed(entry.st_value); normalized.st_size = byteswap_if_needed(entry.st_size); // on arm I've observed zero-size symbols that overlap with symbols we care about // this interferes with some symbol lookup - that could be fixed by enhancing the logic there but // also it's easy to just exclude zero-size symbols here // 1413: 00000000000349e0 0 NOTYPE LOCAL DEFAULT 13 $x // 32341: 00000000000349e0 220 FUNC GLOBAL DEFAULT 13 _Z33stacktrace_from_current_rethrow_3RSt6vectorIiSaIiEE if(normalized.st_size != 0) { symbol_table.unwrap().entries.push_back(normalized); } } std::sort( symbol_table.unwrap().entries.begin(), symbol_table.unwrap().entries.end(), [] (const symtab_entry& a, const symtab_entry& b) { return a.st_value < b.st_value; } ); symbol_table.unwrap().strtab_link = section.sh_link; break; } } return symbol_table; } Result, internal_error> open_elf_cached(const std::string& object_path) { if(object_path.empty()) { return internal_error{"empty object_path"}; } if(get_cache_mode() == cache_mode::prioritize_memory) { return elf::open(object_path) .transform([](elf&& obj) { return maybe_owned{detail::make_unique(std::move(obj))}; }); } else { static std::mutex m; std::unique_lock lock{m}; // TODO: Re-evaluate storing the error static std::unordered_map> cache; auto it = cache.find(object_path); if(it == cache.end()) { auto res = cache.emplace(object_path, elf::open(object_path)); VERIFY(res.second); it = res.first; } return it->second.transform([](elf& obj) { return maybe_owned(&obj); }); } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/binary/elf.hpp000066400000000000000000000110731504061443700166550ustar00rootroot00000000000000#ifndef ELF_HPP #define ELF_HPP #include "cpptrace/forward.hpp" #include "utils/common.hpp" #include "utils/io/base_file.hpp" #include "utils/span.hpp" #include "utils/utils.hpp" #if IS_LINUX #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { // TODO: make methods const and a bunch of members mutable class elf { std::unique_ptr file; bool is_little_endian; bool is_64; struct header_info { uint64_t e_phoff; uint32_t e_phnum; uint32_t e_phentsize; uint64_t e_shoff; uint32_t e_shnum; uint32_t e_shentsize; uint16_t e_shstrndx; }; bool tried_to_load_header = false; optional header; struct section_info { uint32_t sh_name; uint32_t sh_type; uint64_t sh_addr; uint64_t sh_offset; uint64_t sh_size; uint64_t sh_entsize; uint32_t sh_link; }; bool tried_to_load_sections = false; bool did_load_sections = false; std::vector sections; struct strtab_entry { bool tried_to_load_strtab = false; bool did_load_strtab = false; std::vector data; }; std::unordered_map strtab_entries; struct symtab_entry { uint32_t st_name; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; uint64_t st_value; uint64_t st_size; }; struct symtab_info { std::vector entries; std::size_t strtab_link = 0; }; bool tried_to_load_symtab = false; bool did_load_symtab = false; optional symtab; bool tried_to_load_dynamic_symtab = false; bool did_load_dynamic_symtab = false; optional dynamic_symtab; elf(std::unique_ptr file, bool is_little_endian, bool is_64); static NODISCARD Result open(std::unique_ptr file); public: static NODISCARD Result open(cstring_view object_path); static NODISCARD Result open(cbspan object); elf(elf&&) = default; public: Result get_module_image_base(); private: template Result get_module_image_base_impl(); public: optional lookup_symbol(frame_ptr pc); private: optional lookup_symbol(frame_ptr pc, const optional& maybe_symtab); public: struct pc_range { frame_ptr low; frame_ptr high; // not inclusive }; // for in-memory JIT elves Result, internal_error> get_pc_ranges(); struct symbol_entry { std::string st_name; uint16_t st_shndx; uint64_t st_value; uint64_t st_size; }; Result>, internal_error> get_symtab_entries(); Result>, internal_error> get_dynamic_symtab_entries(); private: Result>, internal_error> resolve_symtab_entries( const Result &, internal_error>& ); private: template::value, int>::type = 0> T byteswap_if_needed(T value); Result get_header_info(); template Result get_header_info_impl(); Result&, internal_error> get_sections(); template Result&, internal_error> get_sections_impl(); Result&, internal_error> get_strtab(std::size_t index); Result&, internal_error> get_symtab(); Result&, internal_error> get_dynamic_symtab(); template Result, internal_error> get_symtab_impl(bool dynamic); }; NODISCARD Result, internal_error> open_elf_cached(const std::string& object_path); } CPPTRACE_END_NAMESPACE #endif #endif cpptrace-1.0.4/src/binary/mach-o.cpp000066400000000000000000000676621504061443700172650ustar00rootroot00000000000000#include "binary/mach-o.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include "utils/io/file.hpp" #include "utils/io/memory_file_view.hpp" #if IS_APPLE // A number of mach-o functions are deprecated as of macos 13 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { bool is_mach_o(std::uint32_t magic) { switch(magic) { case FAT_MAGIC: case FAT_CIGAM: case MH_MAGIC: case MH_CIGAM: case MH_MAGIC_64: case MH_CIGAM_64: return true; default: return false; } } bool file_is_mach_o(cstring_view object_path) noexcept { auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter); if(file == nullptr) { return false; } auto magic = load_bytes(file, 0); if(magic) { return is_mach_o(magic.unwrap_value()); } else { return false; } } bool is_fat_magic(std::uint32_t magic) { return magic == FAT_MAGIC || magic == FAT_CIGAM; } // Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c // and https://lowlevelbits.org/parsing-mach-o-files/ bool is_magic_64(std::uint32_t magic) { return magic == MH_MAGIC_64 || magic == MH_CIGAM_64; } bool should_swap_bytes(std::uint32_t magic) { return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM; } void swap_mach_header(mach_header_64& header) { swap_mach_header_64(&header, NX_UnknownByteOrder); } void swap_mach_header(mach_header& header) { swap_mach_header(&header, NX_UnknownByteOrder); } void swap_segment_command(segment_command_64& segment) { swap_segment_command_64(&segment, NX_UnknownByteOrder); } void swap_segment_command(segment_command& segment) { swap_segment_command(&segment, NX_UnknownByteOrder); } void swap_nlist(struct nlist& entry) { swap_nlist(&entry, 1, NX_UnknownByteOrder); } void swap_nlist(struct nlist_64& entry) { swap_nlist_64(&entry, 1, NX_UnknownByteOrder); } #ifdef __LP64__ #define LP(x) x##_64 #else #define LP(x) x #endif Result mach_o::symtab_info_data::get_string(std::size_t index) const { if(stringtab && index < symtab.strsize) { return stringtab.unwrap().data() + index; } else { return internal_error("can't retrieve symbol from symtab"); } } Result mach_o::load() { if(magic == FAT_MAGIC || magic == FAT_CIGAM) { return load_fat_mach(); } else { fat_index = 0; if(is_magic_64(magic)) { return load_mach<64>(); } else { return load_mach<32>(); } } } Result mach_o::open(std::unique_ptr file) { auto magic = file->read(0); if(!magic) { return magic.unwrap_error(); } if(!is_mach_o(magic.unwrap_value())) { return internal_error("File is not mach-o {}", file->path()); } mach_o obj(std::move(file), magic.unwrap_value()); auto result = obj.load(); if(result.is_error()) { return result.unwrap_error(); } else { return obj; } } Result mach_o::open(cstring_view object_path) { auto file_res = file::open(object_path); if(!file_res) { return internal_error("Unable to read object file {}", object_path); } auto& file = file_res.unwrap_value(); return open(make_unique(std::move(file))); } Result mach_o::open(cbspan object) { return open(make_unique(object)); } Result mach_o::get_text_vmaddr() { for(const auto& command : load_commands) { if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) { auto segment = command.cmd == LC_SEGMENT_64 ? load_segment_command<64>(command.file_offset) : load_segment_command<32>(command.file_offset); if(segment.is_error()) { return std::move(segment).unwrap_error(); } if(std::strcmp(segment.unwrap_value().segname, "__TEXT") == 0) { return segment.unwrap_value().vmaddr; } } } // somehow no __TEXT section was found... return internal_error("Couldn't find __TEXT section while parsing Mach-O object"); } std::size_t mach_o::get_fat_index() const { VERIFY(fat_index != std::numeric_limits::max()); return fat_index; } void mach_o::print_segments() const { int i = 0; for(const auto& command : load_commands) { if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) { auto segment_load = command.cmd == LC_SEGMENT_64 ? load_segment_command<64>(command.file_offset) : load_segment_command<32>(command.file_offset); fprintf(stderr, "Load command %d\n", i); if(segment_load.is_error()) { fprintf(stderr, " error\n"); segment_load.drop_error(); continue; } auto& segment = segment_load.unwrap_value(); fprintf(stderr, " cmd %u\n", segment.cmd); fprintf(stderr, " cmdsize %u\n", segment.cmdsize); fprintf(stderr, " segname %s\n", segment.segname); fprintf(stderr, " vmaddr 0x%llx\n", segment.vmaddr); fprintf(stderr, " vmsize 0x%llx\n", segment.vmsize); fprintf(stderr, " off 0x%llx\n", segment.fileoff); fprintf(stderr, " filesize %llu\n", segment.filesize); fprintf(stderr, " nsects %u\n", segment.nsects); } i++; } } Result, internal_error> mach_o::get_pc_ranges() { std::vector ranges; for(const auto& command : load_commands) { if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) { auto segment_res = command.cmd == LC_SEGMENT_64 ? load_segment_command<64>(command.file_offset) : load_segment_command<32>(command.file_offset); if(segment_res.is_error()) { return std::move(segment_res).unwrap_error(); } auto& segment = segment_res.unwrap_value(); if(std::strcmp(segment.segname, "__TEXT") == 0) { ranges.push_back({segment.vmaddr, segment.vmaddr + segment.vmsize}); } } } return ranges; } Result>, internal_error> mach_o::get_symtab_info() { if(!symtab_info.has_value() && !tried_to_load_symtab) { // don't try to load the symtab again if for some reason loading here fails tried_to_load_symtab = true; for(const auto& command : load_commands) { if(command.cmd == LC_SYMTAB) { symtab_info_data info; auto symtab = load_symbol_table_command(command.file_offset); if(!symtab) { return std::move(symtab).unwrap_error(); } info.symtab = symtab.unwrap_value(); auto string = load_string_table(info.symtab.stroff, info.symtab.strsize); if(!string) { return std::move(string).unwrap_error(); } info.stringtab = std::move(string).unwrap_value(); symtab_info = std::move(info); break; } } } return std::reference_wrapper>{symtab_info}; } void mach_o::print_symbol_table_entry( const nlist_64& entry, const char* stringtab, std::size_t stringsize, std::size_t j ) const { const char* type = ""; if(entry.n_type & N_STAB) { switch(entry.n_type) { case N_SO: type = "N_SO"; break; case N_OSO: type = "N_OSO"; break; case N_BNSYM: type = "N_BNSYM"; break; case N_ENSYM: type = "N_ENSYM"; break; case N_FUN: type = "N_FUN"; break; } } else if((entry.n_type & N_TYPE) == N_SECT) { type = "N_SECT"; } fprintf( stderr, "%5llu %8llx %2llx %7s %2llu %4llx %16llx %s\n", to_ull(j), to_ull(entry.n_un.n_strx), to_ull(entry.n_type), type, to_ull(entry.n_sect), to_ull(entry.n_desc), to_ull(entry.n_value), stringtab == nullptr ? "Stringtab error" : entry.n_un.n_strx < stringsize ? stringtab + entry.n_un.n_strx : "String index out of bounds" ); } void mach_o::print_symbol_table() { int i = 0; for(const auto& command : load_commands) { if(command.cmd == LC_SYMTAB) { auto symtab_load = load_symbol_table_command(command.file_offset); fprintf(stderr, "Load command %d\n", i); if(symtab_load.is_error()) { fprintf(stderr, " error\n"); symtab_load.drop_error(); continue; } auto& symtab = symtab_load.unwrap_value(); fprintf(stderr, " cmd %llu\n", to_ull(symtab.cmd)); fprintf(stderr, " cmdsize %llu\n", to_ull(symtab.cmdsize)); fprintf(stderr, " symoff 0x%llu\n", to_ull(symtab.symoff)); fprintf(stderr, " nsyms %llu\n", to_ull(symtab.nsyms)); fprintf(stderr, " stroff 0x%llu\n", to_ull(symtab.stroff)); fprintf(stderr, " strsize %llu\n", to_ull(symtab.strsize)); auto stringtab = load_string_table(symtab.stroff, symtab.strsize); if(!stringtab) { stringtab.drop_error(); } for(std::size_t j = 0; j < symtab.nsyms; j++) { auto entry = bits == 32 ? load_symtab_entry<32>(symtab.symoff, j) : load_symtab_entry<64>(symtab.symoff, j); if(!entry) { fprintf(stderr, "error loading symtab entry\n"); entry.drop_error(); continue; } print_symbol_table_entry( entry.unwrap_value(), stringtab ? stringtab.unwrap_value().data() : nullptr, symtab.strsize, j ); } } i++; } } // produce information similar to dsymutil -dump-debug-map Result mach_o::get_debug_map() { // we have a bunch of symbols in our binary we need to pair up with symbols from various .o files // first collect symbols and the objects they come from debug_map debug_map; auto symtab_info_res = get_symtab_info(); if(!symtab_info_res) { return std::move(symtab_info_res).unwrap_error(); } if(!symtab_info_res.unwrap_value().get()) { return internal_error("No symtab info"); } const auto& symtab_info = symtab_info_res.unwrap_value().get().unwrap(); const auto& symtab = symtab_info.symtab; // TODO: Take timestamp into account? std::string current_module; optional current_function; for(std::size_t j = 0; j < symtab.nsyms; j++) { auto load_entry = bits == 32 ? load_symtab_entry<32>(symtab.symoff, j) : load_symtab_entry<64>(symtab.symoff, j); if(!load_entry) { return std::move(load_entry).unwrap_error(); } auto& entry = load_entry.unwrap_value(); // entry.n_type & N_STAB indicates symbolic debug info if(!(entry.n_type & N_STAB)) { continue; } switch(entry.n_type) { case N_SO: // pass - these encode path and filename for the module, if applicable break; case N_OSO: { // sets the module auto str = symtab_info.get_string(entry.n_un.n_strx); if(!str) { return std::move(str).unwrap_error(); } current_module = str.unwrap_value(); } break; case N_BNSYM: break; // pass case N_ENSYM: break; // pass case N_FUN: { auto str = symtab_info.get_string(entry.n_un.n_strx); if(!str) { return std::move(str).unwrap_error(); } if(str.unwrap_value()[0] == 0) { // end of function scope if(!current_function) { /**/ } current_function.unwrap().size = entry.n_value; debug_map[current_module].push_back(std::move(current_function).unwrap()); } else { current_function = debug_map_entry{}; current_function.unwrap().source_address = entry.n_value; current_function.unwrap().name = str.unwrap_value(); } } break; } } return debug_map; } Result&, internal_error> mach_o::symbol_table() { if(symbols) { return symbols.unwrap(); } if(tried_to_load_symbols) { return internal_error("previous symbol table load failed"); } tried_to_load_symbols = true; std::vector symbol_table; // we have a bunch of symbols in our binary we need to pair up with symbols from various .o files // first collect symbols and the objects they come from auto symtab_info_res = get_symtab_info(); if(!symtab_info_res) { return std::move(symtab_info_res).unwrap_error(); } if(!symtab_info_res.unwrap_value().get()) { return internal_error("No symtab info"); } const auto& symtab_info = symtab_info_res.unwrap_value().get().unwrap(); const auto& symtab = symtab_info.symtab; // TODO: Take timestamp into account? for(std::size_t j = 0; j < symtab.nsyms; j++) { auto load_entry = bits == 32 ? load_symtab_entry<32>(symtab.symoff, j) : load_symtab_entry<64>(symtab.symoff, j); if(!load_entry) { return std::move(load_entry).unwrap_error(); } auto& entry = load_entry.unwrap_value(); if(entry.n_type & N_STAB) { continue; } if((entry.n_type & N_TYPE) == N_SECT) { auto str = symtab_info.get_string(entry.n_un.n_strx); if(!str) { return std::move(str).unwrap_error(); } symbol_table.push_back({ entry.n_value, str.unwrap_value() }); } } std::sort( symbol_table.begin(), symbol_table.end(), [] (const symbol_entry& a, const symbol_entry& b) { return a.address < b.address; } ); symbols = std::move(symbol_table); return symbols.unwrap(); } optional mach_o::lookup_symbol(frame_ptr pc) { auto symtab_ = symbol_table(); if(!symtab_) { return nullopt; } const auto& symtab = symtab_.unwrap_value();; auto it = first_less_than_or_equal( symtab.begin(), symtab.end(), pc, [] (frame_ptr pc, const symbol_entry& entry) { return pc < entry.address; } ); if(it == symtab.end()) { return nullopt; } ASSERT(pc >= it->address); // TODO: We subtracted one from the address so name + diff won't show up in the objdump, decide if desirable // to have an easier offset to lookup return microfmt::format("{} + {}", it->name, pc - it->address); } // produce information similar to dsymutil -dump-debug-map void mach_o::print_debug_map(const debug_map& debug_map) { for(const auto& entry : debug_map) { std::cout< Result mach_o::load_mach() { static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); bits = Bits; using Mach_Header = typename std::conditional::type; std::size_t header_size = sizeof(Mach_Header); auto load_header = file->read(load_base); if(!load_header) { return load_header.unwrap_error(); } Mach_Header& header = load_header.unwrap_value(); magic = header.magic; if(should_swap()) { swap_mach_header(header); } cputype = header.cputype; cpusubtype = header.cpusubtype; filetype = header.filetype; n_load_commands = header.ncmds; sizeof_load_commands = header.sizeofcmds; flags = header.flags; // handle load commands std::uint32_t ncmds = header.ncmds; std::uint32_t load_commands_offset = load_base + header_size; // iterate load commands std::uint32_t actual_offset = load_commands_offset; for(std::uint32_t i = 0; i < ncmds; i++) { auto load_cmd = file->read(actual_offset); if(!load_cmd) { return load_cmd.unwrap_error(); } load_command& cmd = load_cmd.unwrap_value(); if(should_swap()) { swap_load_command(&cmd, NX_UnknownByteOrder); } load_commands.push_back({ actual_offset, cmd.cmd, cmd.cmdsize }); actual_offset += cmd.cmdsize; } return monostate{}; } Result mach_o::load_fat_mach() { std::size_t header_size = sizeof(fat_header); std::size_t arch_size = sizeof(fat_arch); auto load_header = file->read(0); if(!load_header) { return load_header.unwrap_error(); } fat_header& header = load_header.unwrap_value(); if(should_swap()) { swap_fat_header(&header, NX_UnknownByteOrder); } // thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader(); // off_t arch_offset = (off_t)header_size; // for(std::size_t i = 0; i < header.nfat_arch; i++) { // fat_arch arch = load_bytes(file, arch_offset); // if(should_swap()) { // swap_fat_arch(&arch, 1, NX_UnknownByteOrder); // } // off_t mach_header_offset = (off_t)arch.offset; // arch_offset += arch_size; // std::uint32_t magic = load_bytes(file, mach_header_offset); // std::cerr<<"xxx: "<cputype<(mhp->cpusubtype & ~CPU_SUBTYPE_MASK)<cputype && // static_cast(mhp->cpusubtype & ~CPU_SUBTYPE_MASK) == arch.cpusubtype // ) { // load_base = mach_header_offset; // fat_index = i; // if(is_magic_64(magic)) { // load_mach<64>(true); // } else { // load_mach<32>(true); // } // return; // } // } std::vector fat_arches; fat_arches.reserve(header.nfat_arch); off_t arch_offset = (off_t)header_size; for(std::size_t i = 0; i < header.nfat_arch; i++) { auto load_arch = file->read(arch_offset); if(!load_arch) { return load_arch.unwrap_error(); } fat_arch& arch = load_arch.unwrap_value(); if(should_swap()) { swap_fat_arch(&arch, 1, NX_UnknownByteOrder); } fat_arches.push_back(arch); arch_offset += arch_size; } thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader(); fat_arch* best = NXFindBestFatArch( mhp->cputype, mhp->cpusubtype, fat_arches.data(), header.nfat_arch ); if(best) { off_t mach_header_offset = (off_t)best->offset; auto magic = file->read(mach_header_offset); if(!magic) { return magic.unwrap_error(); } load_base = mach_header_offset; fat_index = best - fat_arches.data(); if(is_magic_64(magic.unwrap_value())) { load_mach<64>(); } else { load_mach<32>(); } return monostate{}; } // If this is reached... something went wrong. The cpu we're on wasn't found. return internal_error("Couldn't find appropriate architecture in fat Mach-O"); } template Result mach_o::load_segment_command(std::uint32_t offset) const { using Segment_Command = typename std::conditional::type; auto load_segment = file->read(offset); if(!load_segment) { return load_segment.unwrap_error(); } Segment_Command& segment = load_segment.unwrap_value(); ASSERT(segment.cmd == LC_SEGMENT_64 || segment.cmd == LC_SEGMENT); if(should_swap()) { swap_segment_command(segment); } // fields match just u64 instead of u32 segment_command_64 common; common.cmd = segment.cmd; common.cmdsize = segment.cmdsize; static_assert(sizeof common.segname == 16 && sizeof segment.segname == 16, "xx"); memcpy(common.segname, segment.segname, 16); common.vmaddr = segment.vmaddr; common.vmsize = segment.vmsize; common.fileoff = segment.fileoff; common.filesize = segment.filesize; common.maxprot = segment.maxprot; common.initprot = segment.initprot; common.nsects = segment.nsects; common.flags = segment.flags; return common; } Result mach_o::load_symbol_table_command(std::uint32_t offset) const { auto load_symtab = file->read(offset); if(!load_symtab) { return load_symtab.unwrap_error(); } symtab_command& symtab = load_symtab.unwrap_value(); ASSERT(symtab.cmd == LC_SYMTAB); if(should_swap()) { swap_symtab_command(&symtab, NX_UnknownByteOrder); } return symtab; } template Result mach_o::load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const { using Nlist = typename std::conditional::type; uint32_t offset = load_base + symbol_base + index * sizeof(Nlist); auto load_entry = file->read(offset); if(!load_entry) { return load_entry.unwrap_error(); } Nlist& entry = load_entry.unwrap_value(); if(should_swap()) { swap_nlist(entry); } // fields match just u64 instead of u32 nlist_64 common; common.n_un.n_strx = entry.n_un.n_strx; common.n_type = entry.n_type; common.n_sect = entry.n_sect; common.n_desc = entry.n_desc; common.n_value = entry.n_value; return common; } Result, internal_error> mach_o::load_string_table(std::uint32_t offset, std::uint32_t byte_count) const { std::vector buffer(byte_count + 1); auto read_res = file->read_bytes(span{buffer.data(), byte_count}, load_base + offset); if(!read_res) { return read_res.unwrap_error(); } buffer[byte_count] = 0; // just out of an abundance of caution return buffer; } bool mach_o::should_swap() const { return should_swap_bytes(magic); } Result macho_is_fat(cstring_view object_path) { auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter); if(file == nullptr) { return internal_error("Unable to read object file {}", object_path); } auto magic = load_bytes(file, 0); if(!magic) { return magic.unwrap_error(); } else { return is_fat_magic(magic.unwrap_value()); } } Result, internal_error> open_mach_o_cached(const std::string& object_path) { if(object_path.empty()) { return internal_error{"empty object_path"}; } if(get_cache_mode() == cache_mode::prioritize_memory) { return mach_o::open(object_path) .transform([](mach_o&& obj) { return maybe_owned{detail::make_unique(std::move(obj))}; }); } else { static std::mutex m; std::unique_lock lock{m}; // TODO: Re-evaluate storing the error static std::unordered_map> cache; auto it = cache.find(object_path); if(it == cache.end()) { auto res = cache.insert({ object_path, mach_o::open(object_path) }); VERIFY(res.second); it = res.first; } return it->second.transform([](mach_o& obj) { return maybe_owned(&obj); }); } } } CPPTRACE_END_NAMESPACE #pragma GCC diagnostic pop #endif cpptrace-1.0.4/src/binary/mach-o.hpp000066400000000000000000000106251504061443700172550ustar00rootroot00000000000000#ifndef MACHO_HPP #define MACHO_HPP #include "utils/common.hpp" #include "utils/utils.hpp" #include "utils/span.hpp" #include "utils/io/base_file.hpp" #if IS_APPLE #include #include #include #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { bool file_is_mach_o(cstring_view object_path) noexcept; struct load_command_entry { std::uint32_t file_offset; std::uint32_t cmd; std::uint32_t cmdsize; }; class mach_o { public: struct debug_map_entry { uint64_t source_address; uint64_t size; std::string name; }; struct symbol_entry { uint64_t address; std::string name; }; // map from object file to a vector of symbols to resolve using debug_map = std::unordered_map>; private: std::unique_ptr file; std::uint32_t magic; cpu_type_t cputype; cpu_subtype_t cpusubtype; std::uint32_t filetype; std::uint32_t n_load_commands; std::uint32_t sizeof_load_commands; std::uint32_t flags; std::size_t bits = 0; // 32 or 64 once load_mach is called std::size_t load_base = 0; std::size_t fat_index = std::numeric_limits::max(); std::vector load_commands; struct symtab_info_data { symtab_command symtab; optional> stringtab; Result get_string(std::size_t index) const; }; bool tried_to_load_symtab = false; optional symtab_info; bool tried_to_load_symbols = false; optional> symbols; mach_o(std::unique_ptr file, std::uint32_t magic) : file(std::move(file)), magic(magic) {} Result load(); static NODISCARD Result open(std::unique_ptr file); public: static NODISCARD Result open(cstring_view object_path); static NODISCARD Result open(cbspan object); mach_o(mach_o&&) = default; ~mach_o() = default; Result get_text_vmaddr(); std::size_t get_fat_index() const; void print_segments() const; struct pc_range { frame_ptr low; frame_ptr high; // not inclusive }; // for in-memory JIT mach-o's Result, internal_error> get_pc_ranges(); Result>, internal_error> get_symtab_info(); void print_symbol_table_entry( const nlist_64& entry, const char* stringtab, std::size_t stringsize, std::size_t j ) const; void print_symbol_table(); // produce information similar to dsymutil -dump-debug-map Result get_debug_map(); Result&, internal_error> symbol_table(); optional lookup_symbol(frame_ptr pc); // produce information similar to dsymutil -dump-debug-map static void print_debug_map(const debug_map& debug_map); private: template Result load_mach(); Result load_fat_mach(); template Result load_segment_command(std::uint32_t offset) const; Result load_symbol_table_command(std::uint32_t offset) const; template Result load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const; Result, internal_error> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const; bool should_swap() const; }; Result macho_is_fat(cstring_view object_path); NODISCARD Result, internal_error> open_mach_o_cached(const std::string& object_path); } CPPTRACE_END_NAMESPACE #endif #endif cpptrace-1.0.4/src/binary/module_base.cpp000066400000000000000000000072041504061443700203620ustar00rootroot00000000000000#include "binary/module_base.hpp" #include "platform/platform.hpp" #include "utils/utils.hpp" #include #include #include #if IS_LINUX || IS_APPLE #include #include #if IS_APPLE #include "binary/mach-o.hpp" #else #include "binary/elf.hpp" #endif #elif IS_WINDOWS #include "binary/pe.hpp" #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_LINUX Result get_module_image_base(const std::string& object_path) { static std::mutex mutex; std::lock_guard lock(mutex); static std::unordered_map cache; auto it = cache.find(object_path); if(it == cache.end()) { // arguably it'd be better to release the lock while computing this, but also arguably it's good to not // have two threads try to do the same computation auto elf_object = open_elf_cached(object_path); // TODO: Cache the error if(!elf_object) { return elf_object.unwrap_error(); } auto base = elf_object.unwrap_value()->get_module_image_base(); if(base.is_error()) { return std::move(base).unwrap_error(); } cache.insert(it, {object_path, base.unwrap_value()}); return base; } else { return it->second; } } #elif IS_APPLE Result get_module_image_base(const std::string& object_path) { // We have to parse the Mach-O to find the offset of the text section..... // I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for // now that there is only one, and I'm using only the first section entry within that load command. static std::mutex mutex; std::lock_guard lock(mutex); static std::unordered_map cache; auto it = cache.find(object_path); if(it == cache.end()) { // arguably it'd be better to release the lock while computing this, but also arguably it's good to not // have two threads try to do the same computation auto mach_o_object = open_mach_o_cached(object_path); // TODO: Cache the error if(!mach_o_object) { return mach_o_object.unwrap_error(); } auto base = mach_o_object.unwrap_value()->get_text_vmaddr(); if(!base) { return std::move(base).unwrap_error(); } cache.insert(it, {object_path, base.unwrap_value()}); return base; } else { return it->second; } } #else // Windows Result get_module_image_base(const std::string& object_path) { static std::mutex mutex; std::lock_guard lock(mutex); static std::unordered_map cache; auto it = cache.find(object_path); if(it == cache.end()) { // arguably it'd be better to release the lock while computing this, but also arguably it's good to not // have two threads try to do the same computation auto base = pe_get_module_image_base(object_path); // TODO: Cache the error if(!base) { return std::move(base).unwrap_error(); } cache.insert(it, {object_path, base.unwrap_value()}); return base; } else { return it->second; } } #endif } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/binary/module_base.hpp000066400000000000000000000004561504061443700203710ustar00rootroot00000000000000#ifndef IMAGE_MODULE_BASE_HPP #define IMAGE_MODULE_BASE_HPP #include "utils/utils.hpp" #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { Result get_module_image_base(const std::string& object_path); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/binary/object.cpp000066400000000000000000000157751504061443700173650ustar00rootroot00000000000000#include "binary/object.hpp" #include "platform/platform.hpp" #include "utils/utils.hpp" #include "binary/module_base.hpp" #include "logging.hpp" #include #include #include #include #include #if IS_LINUX || IS_APPLE #include #include #if IS_LINUX #include // needed for dladdr1's link_map info #endif #elif IS_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_LINUX || IS_APPLE #if defined(CPPTRACE_HAS_DL_FIND_OBJECT) || defined(CPPTRACE_HAS_DLADDR1) std::string resolve_l_name(const char* l_name) { if(l_name != nullptr && l_name[0] != 0) { return l_name; } else { // empty l_name, this means it's the currently running executable // TODO: Caching and proper handling char buffer[CPPTRACE_PATH_MAX + 1]{}; auto res = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX); if(res == -1) { return ""; // TODO } else { return buffer; } } } #endif // dladdr queries are needed to get pre-ASLR addresses and targets to run symbol resolution on // _dl_find_object is preferred if at all possible as it is much faster (added in glibc 2.35) // dladdr1 is preferred if possible because it allows for a more accurate object path to be resolved (glibc 2.3.3) #ifdef CPPTRACE_HAS_DL_FIND_OBJECT // we don't even check for this on apple object_frame get_frame_object_info(frame_ptr address) { // Use _dl_find_object when we can, it's orders of magnitude faster object_frame frame; frame.raw_address = address; frame.object_address = 0; dl_find_object result; if(_dl_find_object(reinterpret_cast(address), &result) == 0) { // thread safe frame.object_path = resolve_l_name(result.dlfo_link_map->l_name); frame.object_address = address - to_frame_ptr(result.dlfo_link_map->l_addr); } return frame; } #elif defined(CPPTRACE_HAS_DLADDR1) object_frame get_frame_object_info(frame_ptr address) { // https://github.com/bminor/glibc/blob/91695ee4598b39d181ab8df579b888a8863c4cab/elf/dl-addr.c#L26 Dl_info info; link_map* link_map_info; object_frame frame; frame.raw_address = address; frame.object_address = 0; if( // thread safe dladdr1(reinterpret_cast(address), &info, reinterpret_cast(&link_map_info), RTLD_DL_LINKMAP) ) { frame.object_path = resolve_l_name(link_map_info->l_name); auto base = get_module_image_base(frame.object_path); if(base.has_value()) { frame.object_address = address - reinterpret_cast(info.dli_fbase) + base.unwrap_value(); } else { if(!should_absorb_trace_exceptions()) { base.drop_error(); } } } return frame; } #else // glibc dladdr may not return an accurate dli_fname as it uses argv[0] for addresses in the main executable // https://github.com/bminor/glibc/blob/caed1f5c0b2e31b5f4e0f21fea4b2c9ecd3b5b30/elf/dl-addr.c#L33-L36 // macos doesn't have dladdr1 but its dli_fname behaves more sensibly, same with some other libc's like musl object_frame get_frame_object_info(frame_ptr address) { // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c Dl_info info; object_frame frame; frame.raw_address = address; frame.object_address = 0; if(dladdr(reinterpret_cast(address), &info)) { // thread safe frame.object_path = info.dli_fname; auto base = get_module_image_base(info.dli_fname); if(base.has_value()) { frame.object_address = address - reinterpret_cast(info.dli_fbase) + base.unwrap_value(); } else { if(!should_absorb_trace_exceptions()) { base.drop_error(); } } } return frame; } #endif #else std::string get_module_name(HMODULE handle) { static std::mutex mutex; std::lock_guard lock(mutex); static std::unordered_map cache; auto it = cache.find(handle); if(it == cache.end()) { char path[MAX_PATH]; if(GetModuleFileNameA(handle, path, sizeof(path))) { cache.insert(it, {handle, path}); return path; } else { log::error(std::system_error(GetLastError(), std::system_category()).what()); cache.insert(it, {handle, ""}); return ""; } } else { return it->second; } } object_frame get_frame_object_info(frame_ptr address) { object_frame frame; frame.raw_address = address; frame.object_address = 0; HMODULE handle; // Multithread safe as long as another thread doesn't come along and free the module if(GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(address), &handle )) { frame.object_path = get_module_name(handle); auto base = get_module_image_base(frame.object_path); if(base.has_value()) { frame.object_address = address - reinterpret_cast(handle) + base.unwrap_value(); } else { if(!should_absorb_trace_exceptions()) { base.drop_error(); } } } else { log::error(std::system_error(GetLastError(), std::system_category()).what()); } return frame; } #endif std::vector get_frames_object_info(const std::vector& addresses) { std::vector frames; frames.reserve(addresses.size()); for(const frame_ptr address : addresses) { frames.push_back(get_frame_object_info(address)); } return frames; } object_frame resolve_safe_object_frame(const safe_object_frame& frame) { std::string object_path = frame.object_path; if(object_path.empty()) { return { frame.raw_address, 0, "" }; } return { frame.raw_address, frame.address_relative_to_object_start, std::move(object_path) }; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/binary/object.hpp000066400000000000000000000006431504061443700173560ustar00rootroot00000000000000#ifndef OBJECT_HPP #define OBJECT_HPP #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { object_frame get_frame_object_info(frame_ptr address); std::vector get_frames_object_info(const std::vector& addresses); object_frame resolve_safe_object_frame(const safe_object_frame& frame); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/binary/pe.cpp000066400000000000000000000100351504061443700165030ustar00rootroot00000000000000#include "binary/pe.hpp" #include "platform/platform.hpp" #include "utils/error.hpp" #include "utils/utils.hpp" #if IS_WINDOWS #include #include #include #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include CPPTRACE_BEGIN_NAMESPACE namespace detail { template::value, int>::type = 0> T pe_byteswap_if_needed(T value) { // PE header values are little endian, I think dos e_lfanew should be too if(!is_little_endian()) { return byteswap(value); } else { return value; } } Result pe_get_module_image_base(cstring_view object_path) { // https://drive.google.com/file/d/0B3_wGJkuWLytbnIxY1J5WUs4MEk/view?pli=1&resourcekey=0-n5zZ2UW39xVTH8ZSu6C2aQ // https://0xrick.github.io/win-internals/pe3/ // Endianness should always be little for dos and pe headers std::FILE* file_ptr; errno_t ret = fopen_s(&file_ptr, object_path.c_str(), "rb"); auto file = raii_wrap(std::move(file_ptr), file_deleter); if(ret != 0 || file == nullptr) { return internal_error("Unable to read object file {}", object_path); } auto magic = load_bytes>(file, 0); if(!magic) { return std::move(magic).unwrap_error(); } if(std::memcmp(magic.unwrap_value().data(), "MZ", 2) != 0) { return internal_error("File is not a PE file {}", object_path); } auto e_lfanew = load_bytes(file, 0x3c); // dos header + 0x3c if(!e_lfanew) { return std::move(e_lfanew).unwrap_error(); } DWORD nt_header_offset = pe_byteswap_if_needed(e_lfanew.unwrap_value()); auto signature = load_bytes>(file, nt_header_offset); // nt header + 0 if(!signature) { return std::move(signature).unwrap_error(); } if(std::memcmp(signature.unwrap_value().data(), "PE\0\0", 4) != 0) { return internal_error("File is not a PE file {}", object_path); } auto size_of_optional_header_raw = load_bytes(file, nt_header_offset + 4 + 0x10); // file header + 0x10 if(!size_of_optional_header_raw) { return std::move(size_of_optional_header_raw).unwrap_error(); } WORD size_of_optional_header = pe_byteswap_if_needed(size_of_optional_header_raw.unwrap_value()); if(size_of_optional_header == 0) { return internal_error("Unexpected optional header size for PE file"); } auto optional_header_magic_raw = load_bytes(file, nt_header_offset + 0x18); // optional header + 0x0 if(!optional_header_magic_raw) { return std::move(optional_header_magic_raw).unwrap_error(); } WORD optional_header_magic = pe_byteswap_if_needed(optional_header_magic_raw.unwrap_value()); VERIFY( optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC, ("PE file does not match expected bit-mode " + std::string(object_path)).c_str() ); // finally get image base if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // 32 bit auto bytes = load_bytes(file, nt_header_offset + 0x18 + 0x1c); // optional header + 0x1c if(!bytes) { return std::move(bytes).unwrap_error(); } return to(pe_byteswap_if_needed(bytes.unwrap_value())); } else { // 64 bit // I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD auto bytes = load_bytes(file, nt_header_offset + 0x18 + 0x18); // optional header + 0x18 if(!bytes) { return std::move(bytes).unwrap_error(); } return to(pe_byteswap_if_needed(bytes.unwrap_value())); } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/binary/pe.hpp000066400000000000000000000005051504061443700165110ustar00rootroot00000000000000#ifndef PE_HPP #define PE_HPP #include "platform/platform.hpp" #include "utils/utils.hpp" #if IS_WINDOWS #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { Result pe_get_module_image_base(cstring_view object_path); } CPPTRACE_END_NAMESPACE #endif #endif cpptrace-1.0.4/src/binary/safe_dl.cpp000066400000000000000000000051671504061443700175060ustar00rootroot00000000000000#include "binary/safe_dl.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include "platform/program_name.hpp" #include #include #include #include #include #ifdef CPPTRACE_HAS_DL_FIND_OBJECT #if IS_LINUX || IS_APPLE #include #include #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { void get_safe_object_frame(frame_ptr address, safe_object_frame* out) { out->raw_address = address; dl_find_object result; if(_dl_find_object(reinterpret_cast(address), &result) == 0) { // thread-safe, signal-safe out->address_relative_to_object_start = address - to_frame_ptr(result.dlfo_link_map->l_addr); if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) { std::size_t path_length = std::strlen(result.dlfo_link_map->l_name); std::memcpy( out->object_path, result.dlfo_link_map->l_name, std::min(path_length + 1, std::size_t(CPPTRACE_PATH_MAX + 1)) ); } else { // empty l_name, this means it's the currently running executable memset(out->object_path, 0, CPPTRACE_PATH_MAX + 1); // signal-safe auto res = readlink("/proc/self/exe", out->object_path, CPPTRACE_PATH_MAX); if(res == -1) { // error handling? } // TODO: Special handling for /proc/pid/exe unlink edge case } } else { out->address_relative_to_object_start = 0; out->object_path[0] = 0; } // TODO: Handle this part of the documentation? // The address can be a code address or data address. On architectures using function descriptors, no attempt is // made to decode the function descriptor. Depending on how these descriptors are implemented, _dl_find_object // may return the object that defines the function descriptor (and not the object that contains the code // implementing the function), or fail to find any object at all. } bool has_get_safe_object_frame() { return true; } } CPPTRACE_END_NAMESPACE #else CPPTRACE_BEGIN_NAMESPACE namespace detail { void get_safe_object_frame(frame_ptr address, safe_object_frame* out) { out->raw_address = address; out->address_relative_to_object_start = 0; out->object_path[0] = 0; } bool has_get_safe_object_frame() { return false; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/binary/safe_dl.hpp000066400000000000000000000004051504061443700175010ustar00rootroot00000000000000#ifndef SAFE_DL_HPP #define SAFE_DL_HPP #include "utils/common.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { void get_safe_object_frame(frame_ptr address, safe_object_frame* out); bool has_get_safe_object_frame(); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/cpptrace.cpp000066400000000000000000000244111504061443700164170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "cpptrace/basic.hpp" #include "jit/jit_objects.hpp" #include "symbols/symbols.hpp" #include "unwind/unwind.hpp" #include "demangle/demangle.hpp" #include "utils/common.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" #include "binary/object.hpp" #include "binary/safe_dl.hpp" #include "snippets/snippet.hpp" #include "options.hpp" CPPTRACE_BEGIN_NAMESPACE CPPTRACE_FORCE_NO_INLINE raw_trace raw_trace::current(std::size_t skip) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_raw_trace(skip + 1); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return raw_trace{}; } } CPPTRACE_FORCE_NO_INLINE raw_trace raw_trace::current(std::size_t skip, std::size_t max_depth) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_raw_trace(skip + 1, max_depth); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return raw_trace{}; } } object_trace raw_trace::resolve_object_trace() const { try { return object_trace{detail::get_frames_object_info(frames)}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return object_trace{}; } } stacktrace raw_trace::resolve() const { try { std::vector trace = detail::resolve_frames(frames); for(auto& frame : trace) { frame.symbol = detail::demangle(frame.symbol, true); } return {std::move(trace)}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return stacktrace{}; } } void raw_trace::clear() { frames.clear(); } bool raw_trace::empty() const noexcept { return frames.empty(); } CPPTRACE_FORCE_NO_INLINE object_trace object_trace::current(std::size_t skip) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_object_trace(skip + 1); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return object_trace{}; } } CPPTRACE_FORCE_NO_INLINE object_trace object_trace::current(std::size_t skip, std::size_t max_depth) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_object_trace(skip + 1, max_depth); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return object_trace{}; } } stacktrace object_trace::resolve() const { try { std::vector trace = detail::resolve_frames(frames); for(auto& frame : trace) { frame.symbol = detail::demangle(frame.symbol, true); } return {std::move(trace)}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return stacktrace(); } } void object_trace::clear() { frames.clear(); } bool object_trace::empty() const noexcept { return frames.empty(); } object_frame stacktrace_frame::get_object_info() const { return detail::get_frame_object_info(raw_address); } std::string stacktrace_frame::to_string() const { return to_string(false); } std::string stacktrace_frame::to_string(bool color) const { return get_default_formatter().format(*this, color); } std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame) { return stream << frame.to_string(); } CPPTRACE_FORCE_NO_INLINE stacktrace stacktrace::current(std::size_t skip) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_trace(skip + 1); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return stacktrace{}; } } CPPTRACE_FORCE_NO_INLINE stacktrace stacktrace::current(std::size_t skip, std::size_t max_depth) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_trace(skip + 1, max_depth); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return stacktrace{}; } } void stacktrace::print() const { get_default_formatter().print(*this); } void stacktrace::print(std::ostream& stream) const { get_default_formatter().print(stream, *this); } void stacktrace::print(std::ostream& stream, bool color) const { get_default_formatter().print(stream, *this, color); } namespace detail { const formatter& get_default_snippet_formatter() { static formatter snippet_formatter = formatter{}.snippets(true); return snippet_formatter; } } void stacktrace::print_with_snippets() const { detail::get_default_snippet_formatter().print(*this); } void stacktrace::print_with_snippets(std::ostream& stream) const { detail::get_default_snippet_formatter().print(stream, *this); } void stacktrace::print_with_snippets(std::ostream& stream, bool color) const { detail::get_default_snippet_formatter().print(stream, *this, color); } void stacktrace::clear() { frames.clear(); } bool stacktrace::empty() const noexcept { return frames.empty(); } std::string stacktrace::to_string(bool color) const { return get_default_formatter().format(*this, color); } std::ostream& operator<<(std::ostream& stream, const stacktrace& trace) { get_default_formatter().print(stream, trace); return stream; } CPPTRACE_FORCE_NO_INLINE raw_trace generate_raw_trace(std::size_t skip) { try { return raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return raw_trace{}; } } CPPTRACE_FORCE_NO_INLINE raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth) { try { return raw_trace{detail::capture_frames(skip + 1, max_depth)}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return raw_trace{}; } } CPPTRACE_FORCE_NO_INLINE std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip) { try { // try/catch can never be hit but it's needed to prevent TCO return detail::safe_capture_frames(buffer, size, skip + 1, SIZE_MAX); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return 0; } } CPPTRACE_FORCE_NO_INLINE std::size_t safe_generate_raw_trace( frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth ) { try { // try/catch can never be hit but it's needed to prevent TCO return detail::safe_capture_frames(buffer, size, skip + 1, max_depth); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return 0; } } CPPTRACE_FORCE_NO_INLINE object_trace generate_object_trace(std::size_t skip) { try { return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, SIZE_MAX))}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return object_trace{}; } } CPPTRACE_FORCE_NO_INLINE object_trace generate_object_trace(std::size_t skip, std::size_t max_depth) { try { return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return object_trace{}; } } CPPTRACE_FORCE_NO_INLINE stacktrace generate_trace(std::size_t skip) { try { // try/catch can never be hit but it's needed to prevent TCO return generate_trace(skip + 1, SIZE_MAX); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return stacktrace{}; } } CPPTRACE_FORCE_NO_INLINE stacktrace generate_trace(std::size_t skip, std::size_t max_depth) { try { std::vector frames = detail::capture_frames(skip + 1, max_depth); std::vector trace = detail::resolve_frames(frames); for(auto& frame : trace) { frame.symbol = detail::demangle(frame.symbol, true); } return {std::move(trace)}; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return stacktrace(); } } object_frame safe_object_frame::resolve() const { return detail::resolve_safe_object_frame(*this); } void get_safe_object_frame(frame_ptr address, safe_object_frame* out) { detail::get_safe_object_frame(address, out); } bool can_signal_safe_unwind() { return detail::has_safe_unwind(); } bool can_get_safe_object_frame() { return detail::has_get_safe_object_frame(); } void register_jit_object(const char* ptr, std::size_t size) { detail::register_jit_object(ptr, size); } void unregister_jit_object(const char* ptr) { detail::unregister_jit_object(ptr); } void clear_all_jit_objects() { detail::clear_all_jit_objects(); } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/cpptrace.cppm000066400000000000000000000077241504061443700166040ustar00rootroot00000000000000module; #include #include #include #include #include #include export module cpptrace; CPPTRACE_BEGIN_NAMESPACE // cpptrace/basic export using cpptrace::raw_trace; export using cpptrace::object_frame; export using cpptrace::object_trace; export using cpptrace::nullable; export using cpptrace::stacktrace_frame; export using cpptrace::stacktrace; export using cpptrace::generate_raw_trace; export using cpptrace::generate_object_trace; export using cpptrace::generate_trace; export using cpptrace::safe_generate_raw_trace; export using cpptrace::safe_object_frame; export using cpptrace::can_get_safe_object_frame; export using cpptrace::can_signal_safe_unwind; export using cpptrace::can_get_safe_object_frame; export using cpptrace::register_jit_object; export using cpptrace::unregister_jit_object; export using cpptrace::clear_all_jit_objects; // cpptrace/exceptions export using cpptrace::exception; export using cpptrace::lazy_exception; export using cpptrace::exception_with_message; export using cpptrace::logic_error; export using cpptrace::domain_error; export using cpptrace::invalid_argument; export using cpptrace::length_error; export using cpptrace::out_of_range; export using cpptrace::runtime_error; export using cpptrace::range_error; export using cpptrace::overflow_error; export using cpptrace::underflow_error; export using cpptrace::nested_exception; export using cpptrace::system_error; export using cpptrace::rethrow_and_wrap_if_needed; // cpptrace/formatting export using cpptrace::basename; export using cpptrace::prettify_symbol; export using cpptrace::formatter; export using cpptrace::get_default_formatter; // cpptrace/forward export using cpptrace::frame_ptr; // cpptrace/from_current.hpp export using cpptrace::raw_trace_from_current_exception; export using cpptrace::from_current_exception; export using cpptrace::raw_trace_from_current_exception_rethrow; export using cpptrace::from_current_exception_rethrow; export using cpptrace::current_exception_was_rethrown; export using cpptrace::rethrow; export using cpptrace::clear_current_exception_traces; export using cpptrace::try_catch; namespace detail { #ifdef _MSC_VER export using cpptrace::detail::argument; export using cpptrace::detail::exception_filter; #else export using cpptrace::detail::unwind_interceptor; export using cpptrace::detail::unwind_interceptor_for; export using cpptrace::detail::nop; #endif } // cpptrace/io export using cpptrace::operator<<; // FIXME: make hidden friend // cpptrace/utils export using cpptrace::demangle; export using cpptrace::prune_symbol; export using cpptrace::get_snippet; export using cpptrace::isatty; export using cpptrace::stdin_fileno; export using cpptrace::stderr_fileno; export using cpptrace::stdout_fileno; export using cpptrace::register_terminate_handler; export using cpptrace::absorb_trace_exceptions; export using cpptrace::enable_inlined_call_resolution; export using cpptrace::cache_mode; export using cpptrace::log_level; export using cpptrace::set_log_level; export using cpptrace::set_log_callback; export using cpptrace::use_default_stderr_logger; export using cpptrace::to_string; export using cpptrace::cache_mode; namespace experimental { export using cpptrace::experimental::set_cache_mode; export using cpptrace::experimental::set_dwarf_resolver_line_table_cache_size; export using cpptrace::experimental::set_dwarf_resolver_disable_aranges; } #ifdef _WIN32 export using cpptrace::load_symbols_for_file; #endif CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/ctrace.cpp000066400000000000000000000401461504061443700160620ustar00rootroot00000000000000#include #include #include #include "symbols/symbols.hpp" #include "unwind/unwind.hpp" #include "demangle/demangle.hpp" #include "platform/exception_type.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include "binary/object.hpp" #include "binary/safe_dl.hpp" #include "utils/string_view.hpp" #define ESC "\033[" #define RESET ESC "0m" #define RED ESC "31m" #define GREEN ESC "32m" #define YELLOW ESC "33m" #define BLUE ESC "34m" #define MAGENTA ESC "35m" #define CYAN ESC "36m" #if defined(__GNUC__) && ((__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6)) # define CTRACE_GNU_FORMAT(...) __attribute__((format(__VA_ARGS__))) #elif defined(__clang__) // Probably requires llvm >3.5? Not exactly sure. # define CTRACE_GNU_FORMAT(...) __attribute__((format(__VA_ARGS__))) #else # define CTRACE_GNU_FORMAT(...) #endif #if defined(__clang__) # define CTRACE_FORMAT_PROLOGUE \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wformat-security\"") # define CTRACE_FORMAT_EPILOGUE \ _Pragma("clang diagnostic pop") #elif defined(__GNUC_MINOR__) # define CTRACE_FORMAT_PROLOGUE \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wformat-security\"") # define CTRACE_FORMAT_EPILOGUE \ _Pragma("GCC diagnostic pop") #else # define CTRACE_FORMAT_PROLOGUE # define CTRACE_FORMAT_EPILOGUE #endif namespace ctrace { static constexpr std::uint32_t invalid_pos = ~0U; CTRACE_FORMAT_PROLOGUE template CTRACE_GNU_FORMAT(printf, 2, 0) static void ffprintf(std::FILE* f, const char fmt[], Args&&...args) { (void)std::fprintf(f, fmt, args...); (void)fflush(f); } CTRACE_FORMAT_EPILOGUE static bool is_empty(std::uint32_t pos) noexcept { return pos == invalid_pos; } static bool is_empty(const char* str) noexcept { return !str || std::char_traits::length(str) == 0; } static ctrace_owning_string generate_owning_string(cpptrace::detail::string_view raw_string) noexcept { // Returns length to the null terminator. char* new_string = new char[raw_string.size() + 1]; std::char_traits::copy(new_string, raw_string.data(), raw_string.size()); new_string[raw_string.size()] = '\0'; return { new_string }; } static void free_owning_string(const char* owned_string) noexcept { if(!owned_string) return; // Not necessary but eh delete[] owned_string; } static void free_owning_string(ctrace_owning_string& owned_string) noexcept { free_owning_string(owned_string.data); } static ctrace_object_frame convert_object_frame(const cpptrace::object_frame& frame) { const char* new_path = generate_owning_string(frame.object_path).data; return { frame.raw_address, frame.object_address, new_path }; } static ctrace_object_trace c_convert(const std::vector& trace) { std::size_t count = trace.size(); auto* frames = new ctrace_object_frame[count]; std::transform(trace.begin(), trace.end(), frames, convert_object_frame); return { frames, count }; } static ctrace_stacktrace_frame convert_stacktrace_frame(const cpptrace::stacktrace_frame& frame) { ctrace_stacktrace_frame new_frame; new_frame.raw_address = frame.raw_address; new_frame.object_address = frame.object_address; new_frame.line = frame.line.value_or(invalid_pos); new_frame.column = frame.column.value_or(invalid_pos); new_frame.filename = generate_owning_string(frame.filename).data; new_frame.symbol = generate_owning_string(cpptrace::detail::demangle(frame.symbol, true)).data; new_frame.is_inline = ctrace_bool(frame.is_inline); return new_frame; } static cpptrace::stacktrace_frame convert_stacktrace_frame(const ctrace_stacktrace_frame& frame) { using nullable_type = cpptrace::nullable; static constexpr auto null_v = nullable_type::null().raw_value; cpptrace::stacktrace_frame new_frame; new_frame.raw_address = frame.raw_address; new_frame.object_address = frame.object_address; new_frame.line = nullable_type{is_empty(frame.line) ? null_v : frame.line}; new_frame.column = nullable_type{is_empty(frame.column) ? null_v : frame.column}; new_frame.filename = frame.filename; new_frame.symbol = frame.symbol; new_frame.is_inline = bool(frame.is_inline); return new_frame; } static ctrace_stacktrace c_convert(const std::vector& trace) { std::size_t count = trace.size(); auto* frames = new ctrace_stacktrace_frame[count]; std::transform( trace.begin(), trace.end(), frames, static_cast(convert_stacktrace_frame) ); return { frames, count }; } static cpptrace::stacktrace cpp_convert(const ctrace_stacktrace* ptrace) { if(!ptrace || !ptrace->frames) { return { }; } std::vector new_frames; new_frames.reserve(ptrace->count); for(std::size_t i = 0; i < ptrace->count; ++i) { new_frames.push_back(convert_stacktrace_frame(ptrace->frames[i])); } return cpptrace::stacktrace{std::move(new_frames)}; } } extern "C" { // ctrace::string ctrace_owning_string ctrace_generate_owning_string(const char* raw_string) { return ctrace::generate_owning_string(raw_string); } void ctrace_free_owning_string(ctrace_owning_string* string) { if(!string) { return; } ctrace::free_owning_string(*string); string->data = nullptr; } // ctrace::generation: CTRACE_FORCE_NO_INLINE ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth) { try { std::vector trace = cpptrace::detail::capture_frames(skip + 1, max_depth); std::size_t count = trace.size(); auto* frames = new ctrace_frame_ptr[count]; std::copy(trace.data(), trace.data() + count, frames); return { frames, count }; } catch(...) { // Don't check rethrow condition, it's risky. return { nullptr, 0 }; } } CTRACE_FORCE_NO_INLINE ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth) { try { std::vector trace = cpptrace::detail::get_frames_object_info( cpptrace::detail::capture_frames(skip + 1, max_depth) ); return ctrace::c_convert(trace); } catch(...) { // NOSONAR // Don't check rethrow condition, it's risky. return { nullptr, 0 }; } } CTRACE_FORCE_NO_INLINE ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth) { try { std::vector frames = cpptrace::detail::capture_frames(skip + 1, max_depth); std::vector trace = cpptrace::detail::resolve_frames(frames); return ctrace::c_convert(trace); } catch(...) { // NOSONAR // Don't check rethrow condition, it's risky. return { nullptr, 0 }; } } // ctrace::freeing: void ctrace_free_raw_trace(ctrace_raw_trace* trace) { if(!trace) { return; } ctrace_frame_ptr* frames = trace->frames; delete[] frames; trace->frames = nullptr; trace->count = 0; } void ctrace_free_object_trace(ctrace_object_trace* trace) { if(!trace || !trace->frames) { return; } ctrace_object_frame* frames = trace->frames; for(std::size_t i = 0; i < trace->count; ++i) { const char* path = frames[i].obj_path; ctrace::free_owning_string(path); } delete[] frames; trace->frames = nullptr; trace->count = 0; } void ctrace_free_stacktrace(ctrace_stacktrace* trace) { if(!trace || !trace->frames) { return; } ctrace_stacktrace_frame* frames = trace->frames; for(std::size_t i = 0; i < trace->count; ++i) { ctrace::free_owning_string(frames[i].filename); ctrace::free_owning_string(frames[i].symbol); } delete[] frames; trace->frames = nullptr; trace->count = 0; } // ctrace::resolve: ctrace_stacktrace ctrace_resolve_raw_trace(const ctrace_raw_trace* trace) { if(!trace || !trace->frames) { return { nullptr, 0 }; } try { std::vector frames(trace->count, 0); std::copy(trace->frames, trace->frames + trace->count, frames.begin()); std::vector resolved = cpptrace::detail::resolve_frames(frames); return ctrace::c_convert(resolved); } catch(...) { // NOSONAR // Don't check rethrow condition, it's risky. return { nullptr, 0 }; } } ctrace_object_trace ctrace_resolve_raw_trace_to_object_trace(const ctrace_raw_trace* trace) { if(!trace || !trace->frames) { return { nullptr, 0 }; } try { std::vector frames(trace->count, 0); std::copy(trace->frames, trace->frames + trace->count, frames.begin()); std::vector obj = cpptrace::detail::get_frames_object_info(frames); return ctrace::c_convert(obj); } catch(...) { // NOSONAR // Don't check rethrow condition, it's risky. return { nullptr, 0 }; } } ctrace_stacktrace ctrace_resolve_object_trace(const ctrace_object_trace* trace) { if(!trace || !trace->frames) { return { nullptr, 0 }; } try { std::vector frames(trace->count, 0); std::transform( trace->frames, trace->frames + trace->count, frames.begin(), [] (const ctrace_object_frame& frame) -> cpptrace::frame_ptr { return frame.raw_address; } ); std::vector resolved = cpptrace::detail::resolve_frames(frames); return ctrace::c_convert(resolved); } catch(...) { // NOSONAR // Don't check rethrow condition, it's risky. return { nullptr, 0 }; } } // ctrace::safe: size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth) { return cpptrace::safe_generate_raw_trace(buffer, size, skip, max_depth); } void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out) { // TODO: change this? static_assert(sizeof(cpptrace::safe_object_frame) == sizeof(ctrace_safe_object_frame), ""); cpptrace::get_safe_object_frame(address, reinterpret_cast(out)); } ctrace_bool ctrace_can_signal_safe_unwind() { return cpptrace::can_signal_safe_unwind(); } ctrace_bool ctrace_can_get_safe_object_frame(void) { return cpptrace::can_get_safe_object_frame(); } // ctrace::io: ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color) { if(!trace || !trace->frames) { return ctrace::generate_owning_string(""); } auto cpp_trace = ctrace::cpp_convert(trace); std::string trace_string = cpp_trace.to_string(bool(use_color)); return ctrace::generate_owning_string(trace_string); } void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color) { if( use_color && ( (to == stdout && cpptrace::isatty(cpptrace::stdout_fileno)) || (to == stderr && cpptrace::isatty(cpptrace::stderr_fileno)) ) ) { cpptrace::detail::enable_virtual_terminal_processing_if_needed(); } ctrace::ffprintf(to, "Stack trace (most recent call first):\n"); if(trace->count == 0 || !trace->frames) { ctrace::ffprintf(to, "\n"); return; } const auto reset = use_color ? ESC "0m" : ""; const auto green = use_color ? ESC "32m" : ""; const auto yellow = use_color ? ESC "33m" : ""; const auto blue = use_color ? ESC "34m" : ""; const auto frame_number_width = cpptrace::detail::n_digits(cpptrace::detail::to(trace->count - 1)); ctrace_stacktrace_frame* frames = trace->frames; for(std::size_t i = 0; i < trace->count; ++i) { static constexpr auto ptr_len = 2 * sizeof(cpptrace::frame_ptr); ctrace::ffprintf(to, "#%-*llu ", int(frame_number_width), i); if(frames[i].is_inline) { (void)std::fprintf(to, "%*s", int(ptr_len + 2), "(inlined)"); } else { (void)std::fprintf(to, "%s0x%0*llx%s", blue, int(ptr_len), cpptrace::detail::to_ull(frames[i].raw_address), reset); } if(!ctrace::is_empty(frames[i].symbol)) { (void)std::fprintf(to, " in %s%s%s", yellow, frames[i].symbol, reset); } if(!ctrace::is_empty(frames[i].filename)) { (void)std::fprintf(to, " at %s%s%s", green, frames[i].filename, reset); if(ctrace::is_empty(frames[i].line)) { ctrace::ffprintf(to, "\n"); continue; } (void)std::fprintf(to, ":%s%llu%s", blue, cpptrace::detail::to_ull(frames[i].line), reset); if(ctrace::is_empty(frames[i].column)) { ctrace::ffprintf(to, "\n"); continue; } (void)std::fprintf(to, ":%s%llu%s", blue, cpptrace::detail::to_ull(frames[i].column), reset); } // always print newline at end :M ctrace::ffprintf(to, "\n"); } } // utility::demangle: ctrace_owning_string ctrace_demangle(const char* mangled) { if(!mangled) { return ctrace::generate_owning_string(""); } std::string demangled = cpptrace::demangle(mangled); return ctrace::generate_owning_string(demangled); } // utility::io int ctrace_stdin_fileno(void) { return cpptrace::stdin_fileno; } int ctrace_stderr_fileno(void) { return cpptrace::stderr_fileno; } int ctrace_stdout_fileno(void) { return cpptrace::stdout_fileno; } ctrace_bool ctrace_isatty(int fd) { return cpptrace::isatty(fd); } // utility::cache: void ctrace_set_cache_mode(ctrace_cache_mode mode) { static constexpr auto cache_max = cpptrace::cache_mode::prioritize_speed; if(mode > unsigned(cache_max)) { return; } auto cache_mode = static_cast(mode); cpptrace::experimental::set_cache_mode(cache_mode); } void ctrace_enable_inlined_call_resolution(ctrace_bool enable) { cpptrace::enable_inlined_call_resolution(enable); } ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame) { try { cpptrace::object_frame new_frame = cpptrace::detail::get_frame_object_info(frame->raw_address); return ctrace::convert_object_frame(new_frame); } catch(...) { return {0, 0, nullptr}; } } } cpptrace-1.0.4/src/demangle/000077500000000000000000000000001504061443700156645ustar00rootroot00000000000000cpptrace-1.0.4/src/demangle/demangle.hpp000066400000000000000000000003621504061443700201520ustar00rootroot00000000000000#ifndef DEMANGLE_HPP #define DEMANGLE_HPP #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::string demangle(const std::string& name, bool check_prefix); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/demangle/demangle_with_cxxabi.cpp000066400000000000000000000041771504061443700225460ustar00rootroot00000000000000#include "utils/microfmt.hpp" #ifdef CPPTRACE_DEMANGLE_WITH_CXXABI #include "demangle/demangle.hpp" #include "utils/utils.hpp" #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::string demangle(const std::string& name, bool check_prefix) { // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler // Check both _Z and __Z, apple prefixes all symbols with an underscore if(check_prefix && !(starts_with(name, "_Z") || starts_with(name, "__Z"))) { return name; } // Apple clang demangles __Z just fine but gcc doesn't, so just offset the leading underscore std::size_t offset = 0; if(starts_with(name, "__Z")) { offset = 1; } // Mangled names don't have spaces, we might add a space and some extra info somewhere but we still want it to // be demanglable. Look for a space, if there is one swap it with a null terminator briefly. auto end = name.find(' '); std::string name_copy; std::reference_wrapper to_demangle = name; std::string rest; if(end != std::string::npos) { name_copy = name.substr(0, end); rest = name.substr(end); to_demangle = name_copy; } // presumably thread-safe // it appears safe to pass nullptr for status however the docs don't explicitly say it's safe so I don't // want to rely on it int status; auto demangled = raii_wrap( abi::__cxa_demangle(to_demangle.get().c_str() + offset, nullptr, nullptr, &status), [] (char* str) { std::free(str); } ); // demangled will always be nullptr on non-zero status, and if __cxa_demangle ever fails for any reason // we'll just quietly return the mangled name if(demangled.get()) { std::string str = demangled.get(); if(!rest.empty()) { str += rest; } return str; } else { return name; } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/demangle/demangle_with_nothing.cpp000066400000000000000000000003761504061443700227330ustar00rootroot00000000000000#ifdef CPPTRACE_DEMANGLE_WITH_NOTHING #include "demangle/demangle.hpp" #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::string demangle(const std::string& name, bool) { return name; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/demangle/demangle_with_winapi.cpp000066400000000000000000000014171504061443700225510ustar00rootroot00000000000000#ifdef CPPTRACE_DEMANGLE_WITH_WINAPI #include "demangle/demangle.hpp" #include "platform/dbghelp_utils.hpp" #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::string demangle(const std::string& name, bool) { // Dbghelp is is single-threaded, so acquire a lock. auto lock = get_dbghelp_lock(); char buffer[500]; auto ret = UnDecorateSymbolName(name.c_str(), buffer, sizeof(buffer) - 1, 0); if(ret == 0) { return name; } else { buffer[ret] = 0; // just in case, ms' docs unclear if null terminator inserted return buffer; } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/exceptions.cpp000066400000000000000000000147401504061443700170030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "platform/exception_type.hpp" #include "utils/common.hpp" #include "options.hpp" #include "logging.hpp" #include "utils/error.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { lazy_trace_holder::lazy_trace_holder(const lazy_trace_holder& other) : resolved(other.resolved) { if(other.resolved) { new (&resolved_trace) stacktrace(other.resolved_trace); } else { new (&trace) raw_trace(other.trace); } } lazy_trace_holder::lazy_trace_holder(lazy_trace_holder&& other) noexcept : resolved(other.resolved) { if(other.resolved) { new (&resolved_trace) stacktrace(std::move(other.resolved_trace)); } else { new (&trace) raw_trace(std::move(other.trace)); } } lazy_trace_holder& lazy_trace_holder::operator=(const lazy_trace_holder& other) { clear(); resolved = other.resolved; if(other.resolved) { new (&resolved_trace) stacktrace(other.resolved_trace); } else { new (&trace) raw_trace(other.trace); } return *this; } lazy_trace_holder& lazy_trace_holder::operator=(lazy_trace_holder&& other) noexcept { clear(); resolved = other.resolved; if(other.resolved) { new (&resolved_trace) stacktrace(std::move(other.resolved_trace)); } else { new (&trace) raw_trace(std::move(other.trace)); } return *this; } lazy_trace_holder::~lazy_trace_holder() { clear(); } // access const raw_trace& lazy_trace_holder::get_raw_trace() const { if(resolved) { throw std::logic_error( "cpptrace::detail::lazy_trace_holder::get_resolved_trace called on resolved holder" ); } return trace; } stacktrace& lazy_trace_holder::get_resolved_trace() { if(!resolved) { raw_trace old_trace = std::move(trace); *this = lazy_trace_holder(stacktrace{}); try { if(!old_trace.empty()) { resolved_trace = old_trace.resolve(); } } catch(const std::exception& e) { if(!should_absorb_trace_exceptions()) { log::error( "Exception occurred while resolving trace in cpptrace::detail::lazy_trace_holder: {}", e.what() ); } } } return resolved_trace; } const stacktrace& lazy_trace_holder::get_resolved_trace() const { if(!resolved) { throw std::logic_error( "cpptrace::detail::lazy_trace_holder::get_resolved_trace called on unresolved const holder" ); } return resolved_trace; } bool lazy_trace_holder::is_resolved() const { return resolved; } void lazy_trace_holder::clear() { if(resolved) { resolved_trace.~stacktrace(); } else { trace.~raw_trace(); } } CPPTRACE_FORCE_NO_INLINE raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth) { try { return generate_raw_trace(skip + 1, max_depth); } catch(const std::exception& e) { if(!should_absorb_trace_exceptions()) { log::error( "Exception occurred while resolving trace in cpptrace::exception object: {}", e.what() ); } return raw_trace{}; } } CPPTRACE_FORCE_NO_INLINE raw_trace get_raw_trace_and_absorb(std::size_t skip) { try { // try/catch can never be hit but it's needed to prevent TCO return get_raw_trace_and_absorb(skip + 1, SIZE_MAX); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return raw_trace{}; } } } const char* lazy_exception::what() const noexcept { if(what_string.empty()) { what_string = message() + std::string(":\n") + trace_holder.get_resolved_trace().to_string(); } return what_string.c_str(); } const char* lazy_exception::message() const noexcept { return "cpptrace::lazy_exception"; } const stacktrace& lazy_exception::trace() const noexcept { return trace_holder.get_resolved_trace(); } const char* exception_with_message::message() const noexcept { return user_message.c_str(); } system_error::system_error(int error_code, std::string&& message_arg, raw_trace&& trace) noexcept : runtime_error( message_arg + ": " + std::error_code(error_code, std::generic_category()).message(), std::move(trace) ), ec(std::error_code(error_code, std::generic_category())) {} const std::error_code& system_error::code() const noexcept { return ec; } const char* nested_exception::message() const noexcept { if(message_value.empty()) { try { std::rethrow_exception(ptr); } catch(std::exception& e) { message_value = std::string("Nested exception: ") + e.what(); } catch(...) { message_value = "Nested exception holding instance of " + detail::exception_type_name(); } } return message_value.c_str(); } std::exception_ptr nested_exception::nested_ptr() const noexcept { return ptr; } CPPTRACE_FORCE_NO_INLINE void rethrow_and_wrap_if_needed(std::size_t skip) { try { std::rethrow_exception(std::current_exception()); } catch(cpptrace::exception&) { throw; // already a cpptrace::exception } catch(...) { throw nested_exception(std::current_exception(), detail::get_raw_trace_and_absorb(skip + 1)); } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/formatting.cpp000066400000000000000000000524661504061443700170030ustar00rootroot00000000000000#include #include #include "utils/optional.hpp" #include "utils/utils.hpp" #include "utils/replace_all.hpp" #include "snippets/snippet.hpp" #include #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE std::string basename(const std::string& path) { return detail::basename(path, true); } std::string prettify_symbol(std::string symbol) { // > > -> >> replacement // could put in analysis:: but the replacement is basic and this is more convenient for // using in the stringifier too detail::replace_all_dynamic(symbol, "> >", ">>"); // "," -> ", " and " ," -> ", " static const std::regex comma_re(R"(\s*,\s*)"); detail::replace_all(symbol, comma_re, ", "); // class C -> C for msvc static const std::regex class_re(R"(\b(class|struct)\s+)"); detail::replace_all(symbol, class_re, ""); // `anonymous namespace' -> (anonymous namespace) for msvc // this brings it in-line with other compilers and prevents any tokenization/highlighting issues static const std::regex msvc_anonymous_namespace("`anonymous namespace'"); detail::replace_all(symbol, msvc_anonymous_namespace, "(anonymous namespace)"); // rules to replace std::basic_string -> std::string and std::basic_string_view -> std::string // rule to replace ", std::allocator" static const std::pair basic_string = { std::regex(R"(std(::[a-zA-Z0-9_]+)?::basic_string basic_string_view = { std::regex(R"(std(::[a-zA-Z0-9_]+)?::basic_string_view allocator = { std::regex(R"(,\s*std(::[a-zA-Z0-9_]+)?::allocator<)"), "" }; detail::replace_all_template(symbol, allocator); static const std::pair default_delete = { std::regex(R"(,\s*std(::[a-zA-Z0-9_]+)?::default_delete<)"), "" }; detail::replace_all_template(symbol, default_delete); // replace std::__cxx11 -> std:: for gcc dual abi // https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html detail::replace_all_dynamic(symbol, "std::__cxx11::", "std::"); return symbol; } class formatter::impl { struct { std::string header = "Stack trace (most recent call first):"; color_mode color = color_mode::automatic; address_mode addresses = address_mode::raw; path_mode paths = path_mode::full; bool snippets = false; bool break_before_filename = false; int context_lines = 2; bool columns = true; symbol_mode symbols = symbol_mode::full; bool show_filtered_frames = true; bool hide_exception_machinery = true; std::function filter; std::function transform; } options; public: void header(std::string header) { options.header = std::move(header); } void colors(formatter::color_mode mode) { options.color = mode; } void addresses(formatter::address_mode mode) { options.addresses = mode; } void paths(path_mode mode) { options.paths = mode; } void snippets(bool snippets) { options.snippets = snippets; } void snippet_context(int lines) { options.context_lines = lines; } void columns(bool columns) { options.columns = columns; } void symbols(symbol_mode mode) { options.symbols = mode; } void filtered_frame_placeholders(bool show) { options.show_filtered_frames = show; } void filter(std::function filter) { options.filter = filter; } void transform(std::function transform) { options.transform = std::move(transform); } void break_before_filename(bool do_break) { options.break_before_filename = do_break; } void hide_exception_machinery(bool do_hide) { options.hide_exception_machinery = do_hide; } std::string format( const stacktrace_frame& frame, detail::optional color_override = detail::nullopt, size_t filename_indent = 0 ) const { std::ostringstream oss; print_internal(oss, frame, color_override.value_or(options.color == color_mode::always), filename_indent); return std::move(oss).str(); } std::string format(const stacktrace& trace, detail::optional color_override = detail::nullopt) const { std::ostringstream oss; print_internal(oss, trace, color_override.value_or(options.color == color_mode::always)); return std::move(oss).str(); } void print(const stacktrace_frame& frame, detail::optional color_override = detail::nullopt) const { print(std::cout, frame, color_override); } void print( std::ostream& stream, const stacktrace_frame& frame, detail::optional color_override = detail::nullopt, size_t filename_indent = 0 ) const { print_internal(stream, frame, color_override, filename_indent); stream << "\n"; } void print( std::FILE* file, const stacktrace_frame& frame, detail::optional color_override = detail::nullopt, size_t filename_indent = 0 ) const { auto str = format(frame, color_override, filename_indent); str += "\n"; std::fwrite(str.data(), 1, str.size(), file); } void print(const stacktrace& trace, detail::optional color_override = detail::nullopt) const { print(std::cout, trace, color_override); } void print( std::ostream& stream, const stacktrace& trace, detail::optional color_override = detail::nullopt ) const { print_internal(stream, trace, color_override); stream << "\n"; } void print( std::FILE* file, const stacktrace& trace, detail::optional color_override = detail::nullopt ) const { auto str = format(trace, color_override); str += "\n"; std::fwrite(str.data(), 1, str.size(), file); } private: struct color_setting { bool color; color_setting(bool color) : color(color) {} detail::string_view reset() const { return color ? RESET : ""; } detail::string_view green() const { return color ? GREEN : ""; } detail::string_view yellow() const { return color ? YELLOW : ""; } detail::string_view blue() const { return color ? BLUE : ""; } }; bool stream_is_tty(std::ostream& stream) const { // not great, but it'll have to do return (&stream == &std::cout && isatty(stdout_fileno)) || (&stream == &std::cerr && isatty(stderr_fileno)); } void maybe_ensure_virtual_terminal_processing(std::ostream& stream, bool color) const { if(color && stream_is_tty(stream)) { detail::enable_virtual_terminal_processing_if_needed(); } } bool should_do_color(std::ostream& stream, detail::optional color_override) const { bool do_color = options.color == color_mode::always || color_override.value_or(false); if( (options.color == color_mode::automatic || options.color == color_mode::always) && (!color_override || color_override.unwrap() != false) && stream_is_tty(stream) ) { do_color = true; } return do_color; } size_t get_trace_start(const stacktrace& trace) const { if(!options.hide_exception_machinery) { return 0; } // Look for c++ exception machinery and skip it if it's present, otherwise start at the beginning // On itanium this is identifiable by __cxa_throw // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html 2.4.1 // On windows this is identifiable by CxxThrowException (maybe with an underscore?) // https://www.youtube.com/watch?v=COEv2kq_Ht8 40:10 // https://github.com/CppCon/CppCon2018/blob/master/Presentations/unwinding_the_stack_exploring_how_cpp_exceptions_work_on_windows/unwinding_the_stack_exploring_how_cpp_exceptions_work_on_windows__james_mcnellis__cppcon_2018.pdf slide 157 // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/cxxthrowexception?view=msvc-170 auto it = std::find_if(trace.begin(), trace.end(), [] (const stacktrace_frame& frame) { return frame.symbol == "__cxa_throw" || frame.symbol == "CxxThrowException" || frame.symbol == "_CxxThrowException"; }); return it == trace.end() ? 0 : it - trace.begin() + 1; } void print_internal(std::ostream& stream, const stacktrace_frame& input_frame, detail::optional color_override, size_t col_indent) const { bool color = should_do_color(stream, color_override); maybe_ensure_virtual_terminal_processing(stream, color); detail::optional transformed_frame; if(options.transform) { transformed_frame = options.transform(input_frame); } const stacktrace_frame& frame = options.transform ? transformed_frame.unwrap() : input_frame; write_frame(stream, frame, color, col_indent); } void print_internal(std::ostream& stream, const stacktrace& trace, detail::optional color_override) const { bool color = should_do_color(stream, color_override); maybe_ensure_virtual_terminal_processing(stream, color); write_trace(stream, trace, color); } void write_trace(std::ostream& stream, const stacktrace& trace, bool color) const { if(!options.header.empty()) { stream << options.header << '\n'; } const auto& frames = trace.frames; if(frames.empty()) { stream << ""; return; } const auto frame_number_width = detail::n_digits(static_cast(frames.size()) - 1); std::size_t counter = 0; for(size_t i = get_trace_start(trace); i < frames.size(); ++i) { detail::optional transformed_frame; if(options.transform) { transformed_frame = options.transform(frames[i]); } const stacktrace_frame& frame = options.transform ? transformed_frame.unwrap() : frames[i]; bool filter_out_frame = options.filter && !options.filter(frame); if(filter_out_frame && !options.show_filtered_frames) { counter++; continue; } size_t filename_indent = write_frame_number(stream, frame_number_width, counter); if(filter_out_frame) { microfmt::print(stream, "(filtered)"); } else { write_frame(stream, frame, color, filename_indent); if(frame.line.has_value() && !frame.filename.empty() && options.snippets) { auto snippet = detail::get_snippet( frame.filename, frame.line.value(), frame.column, options.context_lines, color ); if(!snippet.empty()) { stream << '\n'; stream << snippet; } } } if(i + 1 != frames.size()) { stream << '\n'; } counter++; } } /// Write the frame number, and return the number of characters written size_t write_frame_number(std::ostream& stream, unsigned int frame_number_width, size_t counter) const { microfmt::print(stream, "#{<{}} ", frame_number_width, counter); return 2 + frame_number_width; } void write_frame(std::ostream& stream, const stacktrace_frame& frame, color_setting color, size_t col) const { col += write_address(stream, frame, color); if(frame.is_inline || options.addresses != address_mode::none) { stream << ' '; col += 1; } if(!frame.symbol.empty()) { write_symbol(stream, frame, color); } if(!frame.symbol.empty() && !frame.filename.empty()) { if(options.break_before_filename) { microfmt::print(stream, "\n{<{}}", col, ""); } else { stream << ' '; } } if(!frame.filename.empty()) { write_source_location(stream, frame, color); } } /// Write the address of the frame, return the number of characters written size_t write_address(std::ostream& stream, const stacktrace_frame& frame, color_setting color) const { if(frame.is_inline) { microfmt::print(stream, "{<{}}", 2 * sizeof(frame_ptr) + 2, "(inlined)"); return 2 * sizeof(frame_ptr) + 2; } else if(options.addresses != address_mode::none) { auto address = options.addresses == address_mode::raw ? frame.raw_address : frame.object_address; microfmt::print(stream, "{}0x{>{}:0h}{}", color.blue(), 2 * sizeof(frame_ptr), address, color.reset()); return 2 * sizeof(frame_ptr) + 2; } return 0; } void write_symbol(std::ostream& stream, const stacktrace_frame& frame, color_setting color) const { detail::optional maybe_stored_string; detail::string_view symbol; switch(options.symbols) { case symbol_mode::full: symbol = frame.symbol; break; case symbol_mode::pruned: maybe_stored_string = prune_symbol(frame.symbol); symbol = maybe_stored_string.unwrap(); break; case symbol_mode::pretty: maybe_stored_string = prettify_symbol(frame.symbol); symbol = maybe_stored_string.unwrap(); break; default: PANIC("Unhandled symbol mode"); } microfmt::print(stream, "in {}{}{}", color.yellow(), symbol, color.reset()); } void write_source_location(std::ostream& stream, const stacktrace_frame& frame, color_setting color) const { microfmt::print( stream, "at {}{}{}", color.green(), options.paths == path_mode::full ? frame.filename : detail::basename(frame.filename, true), color.reset() ); if(frame.line.has_value()) { microfmt::print(stream, ":{}{}{}", color.blue(), frame.line.value(), color.reset()); if(frame.column.has_value() && options.columns) { microfmt::print(stream, ":{}{}{}", color.blue(), frame.column.value(), color.reset()); } } } }; formatter::formatter() : pimpl(new impl) {} formatter::~formatter() { delete pimpl; } formatter::formatter(formatter&& other) : pimpl(detail::exchange(other.pimpl, nullptr)) {} formatter::formatter(const formatter& other) : pimpl(new impl(*other.pimpl)) {} formatter& formatter::operator=(formatter&& other) { if(pimpl) { delete pimpl; } pimpl = detail::exchange(other.pimpl, nullptr); return *this; } formatter& formatter::operator=(const formatter& other) { if(pimpl) { delete pimpl; } pimpl = new impl(*other.pimpl); return *this; } formatter& formatter::header(std::string header) { pimpl->header(std::move(header)); return *this; } formatter& formatter::colors(color_mode mode) { pimpl->colors(mode); return *this; } formatter& formatter::addresses(address_mode mode) { pimpl->addresses(mode); return *this; } formatter& formatter::paths(path_mode mode) { pimpl->paths(mode); return *this; } formatter& formatter::snippets(bool snippets) { pimpl->snippets(snippets); return *this; } formatter& formatter::snippet_context(int lines) { pimpl->snippet_context(lines); return *this; } formatter& formatter::columns(bool columns) { pimpl->columns(columns); return *this; } formatter& formatter::symbols(symbol_mode mode) { pimpl->symbols(mode); return *this; } formatter& formatter::filtered_frame_placeholders(bool show) { pimpl->filtered_frame_placeholders(show); return *this; } formatter& formatter::filter(std::function filter) { pimpl->filter(std::move(filter)); return *this; } formatter& formatter::transform(std::function transform) { pimpl->transform(std::move(transform)); return *this; } formatter& formatter::break_before_filename(bool do_break) { pimpl->break_before_filename(do_break); return *this; } formatter& formatter::hide_exception_machinery(bool do_hide) { pimpl->hide_exception_machinery(do_hide); return *this; } std::string formatter::format(const stacktrace_frame& frame) const { return pimpl->format(frame); } std::string formatter::format(const stacktrace_frame& frame, bool color) const { return pimpl->format(frame, color); } std::string formatter::format(const stacktrace_frame& frame, bool color, size_t filename_indent) const { return pimpl->format(frame, color, filename_indent); } std::string formatter::format(const stacktrace& trace) const { return pimpl->format(trace); } std::string formatter::format(const stacktrace& trace, bool color) const { return pimpl->format(trace, color); } void formatter::print(const stacktrace& trace) const { pimpl->print(trace); } void formatter::print(const stacktrace& trace, bool color) const { pimpl->print(trace, color); } void formatter::print(std::ostream& stream, const stacktrace& trace) const { pimpl->print(stream, trace); } void formatter::print(std::ostream& stream, const stacktrace& trace, bool color) const { pimpl->print(stream, trace, color); } void formatter::print(std::FILE* file, const stacktrace& trace) const { pimpl->print(file, trace); } void formatter::print(std::FILE* file, const stacktrace& trace, bool color) const { pimpl->print(file, trace, color); } void formatter::print(const stacktrace_frame& frame) const { pimpl->print(frame); } void formatter::print(const stacktrace_frame& frame, bool color) const { pimpl->print(frame, color); } void formatter::print(std::ostream& stream, const stacktrace_frame& frame) const { pimpl->print(stream, frame); } void formatter::print(std::ostream& stream, const stacktrace_frame& frame, bool color) const { pimpl->print(stream, frame, color); } void formatter::print(std::ostream& stream, const stacktrace_frame& frame, bool color, size_t filename_indent) const { pimpl->print(stream, frame, color, filename_indent); } void formatter::print(std::FILE* file, const stacktrace_frame& frame) const { pimpl->print(file, frame); } void formatter::print(std::FILE* file, const stacktrace_frame& frame, bool color) const { pimpl->print(file, frame, color); } void formatter::print(std::FILE* file, const stacktrace_frame& frame, bool color, size_t filename_indent) const { pimpl->print(file, frame, color, filename_indent); } const formatter& get_default_formatter() { static formatter formatter; return formatter; } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/from_current.cpp000066400000000000000000000462461504061443700173350ustar00rootroot00000000000000#include #include #include #include #include #include #include "platform/platform.hpp" #include "platform/memory_mapping.hpp" #include "utils/error.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" #include "logging.hpp" #include "unwind/unwind.hpp" #ifndef _MSC_VER #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { thread_local lazy_trace_holder current_exception_trace; thread_local lazy_trace_holder saved_rethrow_trace; bool& get_rethrow_switch() { static thread_local bool rethrow_switch = false; return rethrow_switch; } void save_current_trace(raw_trace trace) { if(get_rethrow_switch()) { saved_rethrow_trace = lazy_trace_holder(std::move(trace)); } else { current_exception_trace = lazy_trace_holder(std::move(trace)); saved_rethrow_trace = lazy_trace_holder(); } } #if defined(_MSC_VER) && defined(CPPTRACE_UNWIND_WITH_DBGHELP) CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip, EXCEPTION_POINTERS* exception_ptrs) { try { #if defined(_M_IX86) || defined(__i386__) (void)skip; // don't skip any frames, the context record is at the throw point auto trace = raw_trace{detail::capture_frames(0, SIZE_MAX, exception_ptrs)}; #else (void)exception_ptrs; auto trace = raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)}; #endif save_current_trace(std::move(trace)); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); } } #else CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) { try { auto trace = raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)}; save_current_trace(std::move(trace)); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); } } #endif #ifdef _MSC_VER // https://www.youtube.com/watch?v=COEv2kq_Ht8 // https://github.com/tpn/pdfs/blob/master/2018%20CppCon%20Unwinding%20the%20Stack%20-%20Exploring%20how%20C%2B%2B%20Exceptions%20work%20on%20Windows%20-%20James%20McNellis.pdf // https://github.com/ecatmur/stacktrace-from-exception/blob/main/stacktrace-from-exception.cpp // https://github.com/wine-mirror/wine/blob/7f833db11ffea4f3f4fa07be31d30559aff9c5fb/dlls/msvcrt/except.c#L371 // https://github.com/facebook/folly/blob/d17bf897cb5bbf8f07b122a614e8cffdc38edcde/folly/lang/Exception.cpp // ClangCL doesn't define ThrowInfo so we use our own // sources: // - https://github.com/ecatmur/stacktrace-from-exception/blob/main/stacktrace-from-exception.cpp // - https://github.com/catboost/catboost/blob/master/contrib/libs/cxxsupp/libcxx/src/support/runtime/exception_pointer_msvc.ipp // - https://www.geoffchappell.com/studies/msvc/language/predefined/index.htm #pragma pack(push, 4) struct CatchableType { std::uint32_t properties; std::int32_t pType; std::uint32_t non_virtual_adjustment; // these next three are from _PMD std::uint32_t offset_to_virtual_base_ptr; std::uint32_t virtual_base_table_index; std::uint32_t sizeOrOffset; std::int32_t copyFunction; }; struct ThrowInfo { std::uint32_t attributes; std::int32_t pmfnUnwind; std::int32_t pForwardCompat; std::int32_t pCatchableTypeArray; }; #pragma warning(push) #pragma warning(disable:4200) #if IS_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wc99-extensions" #endif struct CatchableTypeArray { uint32_t nCatchableTypes; int32_t arrayOfCatchableTypes[]; }; #if IS_CLANG #pragma clang diagnostic pop #endif #pragma warning(pop) #pragma pack(pop) static constexpr unsigned EH_MAGIC_NUMBER1 = 0x19930520; // '?msc' version magic, see ehdata.h static constexpr unsigned EH_EXCEPTION_NUMBER = 0xE06D7363; // '?msc', 'msc' | 0xE0000000 using catchable_type_array_t = decltype(ThrowInfo::pCatchableTypeArray); class catchable_type_info { HMODULE module_pointer = nullptr; const CatchableTypeArray* catchable_types = nullptr; public: catchable_type_info(const HMODULE module_pointer, catchable_type_array_t catchable_type_array) : module_pointer(module_pointer) { catchable_types = rtti_rva(catchable_type_array); } class iterator { const catchable_type_info& info; std::size_t i; public: iterator(const catchable_type_info& info, std::size_t i) : info(info), i(i) {} const std::type_info& operator*() const { return info.get_type_info(i); } bool operator!=(const iterator& other) const { return i != other.i; } iterator& operator++() { i++; return *this; } }; using const_iterator = iterator; const_iterator begin() const { return {*this, 0}; } const_iterator end() const { return {*this, catchable_types ? catchable_types->nCatchableTypes : std::size_t{}}; } private: template T rtti_rva(A address) const { #ifdef _WIN64 return reinterpret_cast((uintptr_t)module_pointer + (uintptr_t)address); #else (void)module_pointer; return reinterpret_cast(address); #endif } const std::type_info& get_type_info(std::size_t i) const { return *rtti_rva(get_catchable_type(i)->pType); } const CatchableType* get_catchable_type(std::size_t i) const { return rtti_rva( reinterpret_cast(catchable_types->arrayOfCatchableTypes)[i] ); } }; catchable_type_info get_catchable_types(const EXCEPTION_RECORD* exception_record) { static_assert(EXCEPTION_MAXIMUM_PARAMETERS >= 4, "Unexpected EXCEPTION_MAXIMUM_PARAMETERS"); // ExceptionInformation will contain // [0] EH_MAGIC_NUMBER1 // [1] ExceptionObject // [2] ThrowInfo HMODULE module_pointer = nullptr; catchable_type_array_t catchable_type_array{}; // will be either an int or pointer if( exception_record->ExceptionInformation[0] == EH_MAGIC_NUMBER1 && exception_record->NumberParameters >= 3 ) { if(exception_record->NumberParameters >= 4) { module_pointer = reinterpret_cast(exception_record->ExceptionInformation[3]); } auto throw_info = reinterpret_cast(exception_record->ExceptionInformation[2]); if (throw_info) { catchable_type_array = throw_info->pCatchableTypeArray; } } return {module_pointer, catchable_type_array}; } bool matches_exception(EXCEPTION_RECORD* exception_record, const std::type_info& type_info) { if (type_info == typeid(void)) { return true; } for (const auto& catchable_type : get_catchable_types(exception_record)) { if (catchable_type == type_info) { return true; } } return false; } #endif #ifndef _MSC_VER #if IS_LIBSTDCXX constexpr size_t vtable_size = 11; #elif IS_LIBCXX constexpr size_t vtable_size = 10; #else #warning "Cpptrace from_current: Unrecognized C++ standard library, from_current() won't be supported" constexpr size_t vtable_size = 0; #endif void perform_typeinfo_surgery(const std::type_info& info, bool(*do_catch_function)(const std::type_info*, const std::type_info*, void**, unsigned)) { if(vtable_size == 0) { // set to zero if we don't know what standard library we're working with return; } void* type_info_pointer = const_cast(static_cast(&info)); void** type_info_vtable_pointer = *static_cast(type_info_pointer); // the type info vtable pointer points to two pointers inside the vtable, adjust it back // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components (see offset to top, typeinfo ptr, // and the following bullet point) type_info_vtable_pointer -= 2; // for libstdc++ the class type info vtable looks like // 0x7ffff7f89d18 <_ZTVN10__cxxabiv117__class_type_infoE>: 0x0000000000000000 0x00007ffff7f89d00 // [offset ][typeinfo pointer ] // 0x7ffff7f89d28 <_ZTVN10__cxxabiv117__class_type_infoE+16>: 0x00007ffff7dd65a0 0x00007ffff7dd65c0 // [base destructor ][deleting dtor ] // 0x7ffff7f89d38 <_ZTVN10__cxxabiv117__class_type_infoE+32>: 0x00007ffff7dd8f10 0x00007ffff7dd8f10 // [__is_pointer_p ][__is_function_p ] // 0x7ffff7f89d48 <_ZTVN10__cxxabiv117__class_type_infoE+48>: 0x00007ffff7dd6640 0x00007ffff7dd6500 // [__do_catch ][__do_upcast ] // 0x7ffff7f89d58 <_ZTVN10__cxxabiv117__class_type_infoE+64>: 0x00007ffff7dd65e0 0x00007ffff7dd66d0 // [__do_upcast ][__do_dyncast ] // 0x7ffff7f89d68 <_ZTVN10__cxxabiv117__class_type_infoE+80>: 0x00007ffff7dd6580 0x00007ffff7f8abe8 // [__do_find_public_src][other ] // In libc++ the layout is // [offset ][typeinfo pointer ] // [base destructor ][deleting dtor ] // [noop1 ][noop2 ] // [can_catch ][search_above_dst ] // [search_below_dst ][has_unambiguous_public_base] // Relevant documentation/implementation: // https://itanium-cxx-abi.github.io/cxx-abi/abi.html // libstdc++ // https://github.com/gcc-mirror/gcc/blob/b13e34699c7d27e561fcfe1b66ced1e50e69976f/libstdc%252B%252B-v3/libsupc%252B%252B/typeinfo // https://github.com/gcc-mirror/gcc/blob/b13e34699c7d27e561fcfe1b66ced1e50e69976f/libstdc%252B%252B-v3/libsupc%252B%252B/class_type_info.cc // libc++ // https://github.com/llvm/llvm-project/blob/648f4d0658ab00cf1e95330c8811aaea9481a274/libcxx/include/typeinfo // https://github.com/llvm/llvm-project/blob/648f4d0658ab00cf1e95330c8811aaea9481a274/libcxxabi/src/private_typeinfo.h // shouldn't be anything other than 4096 but out of an abundance of caution auto page_size = get_page_size(); if(page_size <= 0 && is_positive_power_of_two(page_size)) { throw internal_error("getpagesize() is not a power of 2 greater than zero (was {})", page_size); } if(static_cast(page_size) < vtable_size * sizeof(void*)) { throw internal_error( "Page size isn't big enough for a vtable: Needed {}, got {}", vtable_size * sizeof(void*), page_size ); } // allocate a page for the new vtable so it can be made read-only later // the OS cleans this up, no cleanup done here for it void* new_vtable_page = allocate_page(page_size); // Double-check alignment: "This address must have the alignment required for pointers" // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components constexpr auto ptr_align = alignof(void*); static_assert(is_positive_power_of_two(ptr_align), "alignof has to return a power of two"); auto align_mask = ptr_align - 1; if((reinterpret_cast(new_vtable_page) & align_mask) != 0) { throw internal_error("Bad allocation alignment: {}", reinterpret_cast(new_vtable_page)); } // make our own copy of the vtable std::memcpy(new_vtable_page, type_info_vtable_pointer, vtable_size * sizeof(void*)); // ninja in the custom __do_catch interceptor auto new_vtable = static_cast(new_vtable_page); // double cast is done here because older (and some newer gcc versions) warned about it under -Wpedantic new_vtable[6] = reinterpret_cast(reinterpret_cast(do_catch_function)); // make the page read-only mprotect_page(new_vtable_page, page_size, memory_readonly); // make the vtable pointer for unwind_interceptor's type_info point to the new vtable auto type_info_addr = reinterpret_cast(type_info_pointer); auto page_addr = type_info_addr & ~(page_size - 1); // make sure the memory we're going to set is within the page if(type_info_addr - page_addr + sizeof(void*) > static_cast(page_size)) { throw internal_error("pointer crosses page boundaries"); } auto old_protections = mprotect_page_and_return_old_protections( reinterpret_cast(page_addr), page_size, memory_readwrite ); *static_cast(type_info_pointer) = static_cast(new_vtable + 2); mprotect_page(reinterpret_cast(page_addr), page_size, old_protections); } bool can_catch( const std::type_info* type, const std::type_info* throw_type, void** throw_obj, unsigned outer ) { if (*type == typeid(void)) { return true; } // get the vtable for the type_info and call the function pointer in the 6th slot // see below: perform_typeinfo_surgery void* type_info_pointer = const_cast(static_cast(type)); void** type_info_vtable_pointer = *static_cast(type_info_pointer); // the type info vtable pointer points to two pointers inside the vtable, adjust it back type_info_vtable_pointer -= 2; auto* can_catch_fn = #if IS_GCC // error: ISO C++ forbids casting between pointer-to-function and pointer-to-object on old gcc __extension__ #endif reinterpret_cast(type_info_vtable_pointer[6]); return can_catch_fn(type, throw_type, throw_obj, outer); } #endif // called when unwinding starts after rethrowing, after search phase void rethrow_scope_cleanup() { get_rethrow_switch() = false; } scope_guard setup_rethrow() { get_rethrow_switch() = true; // will flip the switch back to true as soon as the search phase completes and the unwinding begins return scope_exit(rethrow_scope_cleanup); } } CPPTRACE_END_NAMESPACE CPPTRACE_BEGIN_NAMESPACE namespace detail { #ifdef _MSC_VER bool matches_exception(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info) { CPPTRACE_PUSH_EXTENSION_WARNINGS __try { auto* exception_record = exception_ptrs->ExceptionRecord; // Check if the SEH exception is a C++ exception if(exception_record->ExceptionCode == EH_EXCEPTION_NUMBER) { return detail::matches_exception(exception_record, type_info); } } __except(EXCEPTION_EXECUTE_HANDLER) { // pass } CPPTRACE_POP_EXTENSION_WARNINGS return false; } CPPTRACE_FORCE_NO_INLINE int maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, int filter_result) { if(filter_result == EXCEPTION_EXECUTE_HANDLER) { #ifdef CPPTRACE_UNWIND_WITH_DBGHELP collect_current_trace(1, exception_ptrs); #else collect_current_trace(1); (void)exception_ptrs; #endif } return filter_result; } CPPTRACE_FORCE_NO_INLINE void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info) { if(matches_exception(exception_ptrs, type_info)) { #ifdef CPPTRACE_UNWIND_WITH_DBGHELP collect_current_trace(2, exception_ptrs); #else collect_current_trace(2); (void)exception_ptrs; #endif } } #else CPPTRACE_FORCE_NO_INLINE void maybe_collect_trace( const std::type_info* type, const std::type_info* throw_type, void** throw_obj, unsigned outer ) { if(detail::can_catch(type, throw_type, throw_obj, outer)) { collect_current_trace(2); } } void do_prepare_unwind_interceptor(const std::type_info& type_info, bool(*can_catch)(const std::type_info*, const std::type_info*, void**, unsigned)) { try { detail::perform_typeinfo_surgery( type_info, can_catch ); } catch(std::exception& e) { detail::log::error("Exception occurred while preparing from_current support: {}", e.what()); } catch(...) { detail::log::error("Unknown exception occurred while preparing from_current support"); } } #endif } const raw_trace& raw_trace_from_current_exception() { return detail::current_exception_trace.get_raw_trace(); } const stacktrace& from_current_exception() { return detail::current_exception_trace.get_resolved_trace(); } const raw_trace& raw_trace_from_current_exception_rethrow() { return detail::saved_rethrow_trace.get_raw_trace(); } const stacktrace& from_current_exception_rethrow() { return detail::saved_rethrow_trace.get_resolved_trace(); } bool current_exception_was_rethrown() { if(detail::saved_rethrow_trace.is_resolved()) { return !detail::saved_rethrow_trace.get_resolved_trace().empty(); } else { return !detail::saved_rethrow_trace.get_raw_trace().empty(); } } // The non-argument overload is to serve as room for possible future optimization under Microsoft's STL CPPTRACE_FORCE_NO_INLINE void rethrow() { auto guard = detail::setup_rethrow(); std::rethrow_exception(std::current_exception()); } CPPTRACE_FORCE_NO_INLINE void rethrow(std::exception_ptr exception) { auto guard = detail::setup_rethrow(); std::rethrow_exception(exception); } void clear_current_exception_traces() { detail::current_exception_trace = detail::lazy_trace_holder{raw_trace{}}; detail::saved_rethrow_trace = detail::lazy_trace_holder{raw_trace{}}; } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/jit/000077500000000000000000000000001504061443700146765ustar00rootroot00000000000000cpptrace-1.0.4/src/jit/jit_objects.cpp000066400000000000000000000105521504061443700177040ustar00rootroot00000000000000#include "jit/jit_objects.hpp" #include "cpptrace/forward.hpp" #include "utils/error.hpp" #include "utils/optional.hpp" #include "utils/span.hpp" #include "binary/elf.hpp" #include "binary/mach-o.hpp" #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_LINUX || IS_APPLE class jit_object_manager { struct object_entry { const char* object_start; std::unique_ptr object; }; std::vector objects; struct range_entry { frame_ptr low; frame_ptr high; // not inclusive const char* object_start; jit_object_type* object; bool operator<(const range_entry& other) const { return low < other.low; } }; // TODO: Maybe use a set... std::vector range_list; public: void add_jit_object(cbspan object) { auto object_res = jit_object_type::open(object); if(object_res.is_error()) { if(!should_absorb_trace_exceptions()) { object_res.drop_error(); } return; } objects.push_back({object.data(), make_unique(std::move(object_res).unwrap_value())}); auto* object_file = objects.back().object.get(); auto ranges_res = object_file->get_pc_ranges(); if(ranges_res.is_error()) { if(!should_absorb_trace_exceptions()) { ranges_res.drop_error(); } return; } auto& ranges = ranges_res.unwrap_value(); for(auto range : ranges) { range_entry entry{range.low, range.high, object.data(), object_file}; // TODO: Perf range_list.insert(std::upper_bound(range_list.begin(), range_list.end(), entry), entry); } } void remove_jit_object(const char* ptr) { // TODO: Perf objects.erase( std::remove_if( objects.begin(), objects.end(), [&](const object_entry& entry) { return entry.object_start == ptr; } ), objects.end() ); range_list.erase( std::remove_if( range_list.begin(), range_list.end(), [&](const range_entry& entry) { return entry.object_start == ptr; } ), range_list.end() ); } optional lookup(frame_ptr pc) const { auto it = first_less_than_or_equal( range_list.begin(), range_list.end(), pc, [](frame_ptr pc, const range_entry& entry) { return pc < entry.low; } ); if(it == range_list.end()) { return nullopt; } ASSERT(pc >= it->low); if(pc < it->high) { return jit_object_lookup_result{*it->object, it->low}; } else { return nullopt; } } void clear_all_jit_objects() { objects.clear(); range_list.clear(); } }; #else class jit_object_manager { public: void add_jit_object(cbspan) {} void remove_jit_object(const char*) {} void clear_all_jit_objects() {} }; #endif jit_object_manager& get_jit_object_manager() { static jit_object_manager manager; return manager; } void register_jit_object(const char* ptr, std::size_t size) { auto& manager = get_jit_object_manager(); manager.add_jit_object(make_span(ptr, size)); } void unregister_jit_object(const char* ptr) { auto& manager = get_jit_object_manager(); manager.remove_jit_object(ptr); } void clear_all_jit_objects() { auto& manager = get_jit_object_manager(); manager.clear_all_jit_objects(); } #if IS_LINUX || IS_APPLE optional lookup_jit_object(frame_ptr pc) { return get_jit_object_manager().lookup(pc); } #endif } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/jit/jit_objects.hpp000066400000000000000000000013511504061443700177060ustar00rootroot00000000000000#ifndef JIT_OBJECTS_HPP #define JIT_OBJECTS_HPP #include "binary/elf.hpp" #include "binary/mach-o.hpp" #include "cpptrace/forward.hpp" #include "utils/optional.hpp" #include "platform/platform.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { void register_jit_object(const char*, std::size_t); void unregister_jit_object(const char*); void clear_all_jit_objects(); #if IS_LINUX || IS_APPLE #if IS_LINUX using jit_object_type = elf; #elif IS_APPLE using jit_object_type = mach_o; #endif struct jit_object_lookup_result { jit_object_type& object; frame_ptr base; }; optional lookup_jit_object(frame_ptr pc); #endif } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/logging.cpp000066400000000000000000000037101504061443700162430ustar00rootroot00000000000000#include "logging.hpp" #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::atomic current_log_level(log_level::warning); // NOSONAR void default_null_logger(log_level, const char*) {} void default_stderr_logger(log_level level, const char* message) { microfmt::print(std::cerr, "[cpptrace {}] {}\n", to_string(level), message); } std::function& log_callback() { static std::function callback{default_null_logger}; return callback; } void do_log(log_level level, const char* message) { if(level < current_log_level) { return; } log_callback()(level, message); } namespace log { void error(const char* message) { do_log(log_level::error, message); } void warn(const char* message) { do_log(log_level::warning, message); } void info(const char* message) { do_log(log_level::info, message); } void debug(const char* message) { do_log(log_level::debug, message); } } } CPPTRACE_END_NAMESPACE CPPTRACE_BEGIN_NAMESPACE void set_log_level(log_level level) { detail::current_log_level = level; } void set_log_callback(std::function callback) { detail::log_callback() = std::move(callback); } void use_default_stderr_logger() { detail::log_callback() = detail::default_stderr_logger; } const char* to_string(log_level level) { switch(level) { case log_level::debug: return "debug"; case log_level::info: return "info"; case log_level::warning: return "warning"; case log_level::error: return "error"; default: return "unknown log level"; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/logging.hpp000066400000000000000000000017511504061443700162530ustar00rootroot00000000000000#ifndef LOGGING_HPP #define LOGGING_HPP #include #include "utils/microfmt.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace log { // exported for test purposes CPPTRACE_EXPORT void error(const char*); template void error(const char* format, Args&&... args) { error(microfmt::format(format, args...).c_str()); } CPPTRACE_EXPORT void warn(const char*); template void warn(const char* format, Args&&... args) { warn(microfmt::format(format, args...).c_str()); } CPPTRACE_EXPORT void info(const char*); template void info(const char* format, Args&&... args) { info(microfmt::format(format, args...).c_str()); } CPPTRACE_EXPORT void debug(const char*); template void debug(const char* format, Args&&... args) { debug(microfmt::format(format, args...).c_str()); } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/options.cpp000066400000000000000000000017651504061443700163200ustar00rootroot00000000000000#include #include "options.hpp" #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::atomic_bool absorb_trace_exceptions(true); // NOSONAR std::atomic_bool resolve_inlined_calls(true); // NOSONAR std::atomic current_cache_mode(cache_mode::prioritize_speed); // NOSONAR bool should_absorb_trace_exceptions() { return absorb_trace_exceptions; } bool should_resolve_inlined_calls() { return resolve_inlined_calls; } cache_mode get_cache_mode() { return current_cache_mode; } } CPPTRACE_END_NAMESPACE CPPTRACE_BEGIN_NAMESPACE void absorb_trace_exceptions(bool absorb) { detail::absorb_trace_exceptions = absorb; } void enable_inlined_call_resolution(bool enable) { detail::resolve_inlined_calls = enable; } namespace experimental { void set_cache_mode(cache_mode mode) { detail::current_cache_mode = mode; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/options.hpp000066400000000000000000000004741504061443700163210ustar00rootroot00000000000000#ifndef OPTIONS_HPP #define OPTIONS_HPP #include CPPTRACE_BEGIN_NAMESPACE namespace detail { // exported for test purposes CPPTRACE_EXPORT bool should_absorb_trace_exceptions(); bool should_resolve_inlined_calls(); cache_mode get_cache_mode(); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/platform/000077500000000000000000000000001504061443700157345ustar00rootroot00000000000000cpptrace-1.0.4/src/platform/dbghelp_utils.cpp000066400000000000000000000126261504061443700212740ustar00rootroot00000000000000#include "platform/platform.hpp" #if IS_WINDOWS #include "platform/dbghelp_utils.hpp" #if defined(CPPTRACE_UNWIND_WITH_DBGHELP) \ || defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \ || defined(CPPTRACE_DEMANGLE_WITH_WINAPI) #include "utils/error.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { dbghelp_syminit_info::dbghelp_syminit_info(void* handle, bool should_sym_cleanup, bool should_close_handle) : handle(handle), should_sym_cleanup(should_sym_cleanup), should_close_handle(should_close_handle) {} dbghelp_syminit_info::~dbghelp_syminit_info() { release(); } void dbghelp_syminit_info::release() { if(!handle) { return; } if(should_sym_cleanup) { if(!SymCleanup(handle)) { throw internal_error("SymCleanup failed with code {}\n", GetLastError()); } } if(should_close_handle) { if(!CloseHandle(handle)) { throw internal_error("CloseHandle failed with code {}\n", GetLastError()); } } } dbghelp_syminit_info dbghelp_syminit_info::make_not_owned(void* handle) { return dbghelp_syminit_info(handle, false, false); } dbghelp_syminit_info dbghelp_syminit_info::make_owned(void* handle, bool should_close_handle) { return dbghelp_syminit_info(handle, true, should_close_handle); } dbghelp_syminit_info::dbghelp_syminit_info(dbghelp_syminit_info&& other) { handle = exchange(other.handle, nullptr); should_sym_cleanup = other.should_sym_cleanup; should_close_handle = other.should_close_handle; } dbghelp_syminit_info& dbghelp_syminit_info::operator=(dbghelp_syminit_info&& other) { release(); handle = exchange(other.handle, nullptr); should_sym_cleanup = other.should_sym_cleanup; should_close_handle = other.should_close_handle; return *this; } void* dbghelp_syminit_info::get_process_handle() const { return handle; } dbghelp_syminit_info dbghelp_syminit_info::make_non_owning_view() const { return make_not_owned(handle); } std::unordered_map& get_syminit_cache() { static std::unordered_map syminit_cache; return syminit_cache; } dbghelp_syminit_info ensure_syminit() { auto lock = get_dbghelp_lock(); // locking around the entire access of the cache unordered_map HANDLE proc = GetCurrentProcess(); if(get_cache_mode() == cache_mode::prioritize_speed) { auto& syminit_cache = get_syminit_cache(); auto it = syminit_cache.find(proc); if(it != syminit_cache.end()) { return it->second.make_non_owning_view(); } } auto duplicated_handle = raii_wrap(nullptr, [] (void* handle) { if(handle) { if(!CloseHandle(handle)) { throw internal_error("CloseHandle failed with code {}\n", GetLastError()); } } }); // https://github.com/jeremy-rifkin/cpptrace/issues/204 // https://github.com/jeremy-rifkin/cpptrace/pull/206 // https://learn.microsoft.com/en-us/windows/win32/debug/initializing-the-symbol-handler // Apparently duplicating the process handle is the idiomatic thing to do and this avoids issues of // SymInitialize being called twice. // DuplicateHandle requires the PROCESS_DUP_HANDLE access right. If for some reason DuplicateHandle we fall back // to calling SymInitialize on the process handle. optional maybe_duplicate_handle_error_code; if(!DuplicateHandle(proc, proc, proc, &duplicated_handle.get(), 0, FALSE, DUPLICATE_SAME_ACCESS)) { maybe_duplicate_handle_error_code = GetLastError(); } if(!SymInitialize(maybe_duplicate_handle_error_code ? proc : duplicated_handle.get(), NULL, TRUE)) { if(maybe_duplicate_handle_error_code) { throw internal_error( "SymInitialize failed with error code {} after DuplicateHandle failed with error code {}", GetLastError(), maybe_duplicate_handle_error_code.unwrap() ); } else { throw internal_error("SymInitialize failed with error code {}", GetLastError()); } } auto info = dbghelp_syminit_info::make_owned( maybe_duplicate_handle_error_code ? proc : exchange(duplicated_handle.get(), nullptr), !maybe_duplicate_handle_error_code ); // either cache and return a view or return the owning wrapper if(get_cache_mode() == cache_mode::prioritize_speed) { auto& syminit_cache = get_syminit_cache(); auto pair = syminit_cache.insert({proc, std::move(info)}); VERIFY(pair.second); return pair.first->second.make_non_owning_view(); } else { return info; } } std::unique_lock get_dbghelp_lock() { static std::recursive_mutex mutex; return std::unique_lock{mutex}; } } CPPTRACE_END_NAMESPACE #endif #endif cpptrace-1.0.4/src/platform/dbghelp_utils.hpp000066400000000000000000000037511504061443700213000ustar00rootroot00000000000000#ifndef DBGHELP_UTILS_HPP #define DBGHELP_UTILS_HPP #if defined(CPPTRACE_UNWIND_WITH_DBGHELP) \ || defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \ || defined(CPPTRACE_DEMANGLE_WITH_WINAPI) #include "utils/common.hpp" #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { class dbghelp_syminit_info { // `void*` is used to avoid including the (expensive) windows.h header here void* handle = nullptr; bool should_sym_cleanup; // true if cleanup is not managed by the syminit cache bool should_close_handle; // true if cleanup is not managed by the syminit cache and the handle was duplicated dbghelp_syminit_info(void* handle, bool should_sym_cleanup, bool should_close_handle); public: ~dbghelp_syminit_info(); void release(); NODISCARD static dbghelp_syminit_info make_not_owned(void* handle); NODISCARD static dbghelp_syminit_info make_owned(void* handle, bool should_close_handle); dbghelp_syminit_info(const dbghelp_syminit_info&) = delete; dbghelp_syminit_info(dbghelp_syminit_info&&); dbghelp_syminit_info& operator=(const dbghelp_syminit_info&) = delete; dbghelp_syminit_info& operator=(dbghelp_syminit_info&&); void* get_process_handle() const; dbghelp_syminit_info make_non_owning_view() const; }; // Ensure SymInitialize is called on the process. This function either // - Finds that SymInitialize has been called for a handle to the current process already, in which case it returns // a non-owning dbghelp_syminit_info instance holding the handle // - Calls SymInitialize a handle to the current process, caches it, and returns a non-owning dbghelp_syminit_info // - Calls SymInitialize and returns an owning dbghelp_syminit_info which will handle cleanup dbghelp_syminit_info ensure_syminit(); NODISCARD std::unique_lock get_dbghelp_lock(); } CPPTRACE_END_NAMESPACE #endif #endif cpptrace-1.0.4/src/platform/exception_type.hpp000066400000000000000000000013021504061443700215000ustar00rootroot00000000000000#ifndef EXCEPTION_TYPE_HPP #define EXCEPTION_TYPE_HPP #include #include "platform/platform.hpp" // libstdc++ and libc++ #if defined(CPPTRACE_HAS_CXX_EXCEPTION_TYPE) && (IS_LIBSTDCXX || IS_LIBCXX) #include #include #include "demangle/demangle.hpp" #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { inline std::string exception_type_name() { #if defined(CPPTRACE_HAS_CXX_EXCEPTION_TYPE) && (IS_LIBSTDCXX || IS_LIBCXX) const std::type_info* t = abi::__cxa_current_exception_type(); return t ? detail::demangle(t->name(), false) : ""; #else return ""; #endif } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/platform/memory_mapping.cpp000066400000000000000000000221261504061443700214660ustar00rootroot00000000000000#include "platform/memory_mapping.hpp" #include "utils/optional.hpp" #include "utils/utils.hpp" #ifndef _MSC_VER #if IS_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #else #include #include #if IS_APPLE #include #ifdef CPPTRACE_HAS_MACH_VM #include #endif #else #include #include #endif #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_WINDOWS int get_page_size() { SYSTEM_INFO info; GetSystemInfo(&info); return info.dwPageSize; } int mprotect_page_and_return_old_protections(void* page, int page_size, int protections) { DWORD old_protections; if(!VirtualProtect(page, page_size, protections, &old_protections)) { throw internal_error( "VirtualProtect call failed: {}", std::system_error(GetLastError(), std::system_category()).what() ); } return old_protections; } void mprotect_page(void* page, int page_size, int protections) { mprotect_page_and_return_old_protections(page, page_size, protections); } void* allocate_page(int page_size) { auto page = VirtualAlloc(nullptr, page_size, MEM_COMMIT | MEM_RESERVE, memory_readwrite); if(!page) { throw internal_error( "VirtualAlloc call failed: {}", std::system_error(GetLastError(), std::system_category()).what() ); } return page; } #else int get_page_size() { #if defined(_SC_PAGESIZE) return sysconf(_SC_PAGESIZE); #else return getpagesize(); #endif } #if IS_APPLE int get_page_protections(void* page) { // https://stackoverflow.com/a/12627784/15675011 #ifdef CPPTRACE_HAS_MACH_VM mach_vm_size_t vmsize; mach_vm_address_t address = (mach_vm_address_t)page; #else vm_size_t vmsize; vm_address_t address = (vm_address_t)page; #endif vm_region_basic_info_data_t info; mach_msg_type_number_t info_count = sizeof(size_t) == 8 ? VM_REGION_BASIC_INFO_COUNT_64 : VM_REGION_BASIC_INFO_COUNT; memory_object_name_t object; kern_return_t status = #ifdef CPPTRACE_HAS_MACH_VM mach_vm_region #else vm_region_64 #endif ( mach_task_self(), &address, &vmsize, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &info_count, &object ); if(status == KERN_INVALID_ADDRESS) { throw internal_error("vm_region failed with KERN_INVALID_ADDRESS"); } int perms = 0; if(info.protection & VM_PROT_READ) { perms |= PROT_READ; } if(info.protection & VM_PROT_WRITE) { perms |= PROT_WRITE; } if(info.protection & VM_PROT_EXECUTE) { perms |= PROT_EXEC; } return perms; } #else // Code for reading /proc/self/maps // Unfortunately this is the canonical and only way to get memory permissions on linux // It comes with some surprising behaviors. Because it's a pseudo-file and maps could update at any time, reads of // the file can tear. The surprising observable behavior here is overlapping ranges: // - https://unix.stackexchange.com/questions/704987/overlapping-address-ranges-in-proc-maps // - https://stackoverflow.com/questions/59737950/what-is-the-correct-way-to-get-a-consistent-snapshot-of-proc-pid-smaps // Additional info: // Note: reading /proc/PID/maps or /proc/PID/smaps is inherently racy (consistent // output can be achieved only in the single read call). // This typically manifests when doing partial reads of these files while the // memory map is being modified. Despite the races, we do provide the following // guarantees: // // 1) The mapped addresses never go backwards, which implies no two // regions will ever overlap. // 2) If there is something at a given vaddr during the entirety of the // life of the smaps/maps walk, there will be some output for it. // // https://www.kernel.org/doc/Documentation/filesystems/proc.txt // Ideally we could do everything as a single read() call but I don't think that's practical, especially given that // the kernel has limited buffers internally. While we shouldn't be modifying mapped memory while reading // /proc/self/maps here, it's theoretically possible that we could allocate and that could go to the OS for more // pages. // While reading this is inherently racy, as far as I can tell tears don't happen within a line but they can happen // between lines. // The code that writes /proc/pid/maps: // - https://github.com/torvalds/linux/blob/3d0ebc36b0b3e8486ceb6e08e8ae173aaa6d1221/fs/proc/task_mmu.c#L304-L365 struct address_range { uintptr_t low; uintptr_t high; int perms; bool operator<(const address_range& other) const { return low < other.low; } }; // returns nullopt on eof optional read_map_entry(std::ifstream& stream) { uintptr_t start; uintptr_t stop; stream>>start; stream.ignore(1); // dash stream>>stop; if(stream.eof()) { return nullopt; } if(stream.fail()) { throw internal_error("Failure reading /proc/self/maps"); } stream.ignore(1); // space char r, w, x; // there's a private/shared flag after these but we don't need it stream>>r>>w>>x; if(stream.fail() || stream.eof()) { throw internal_error("Failure reading /proc/self/maps"); } int perms = 0; if(r == 'r') { perms |= PROT_READ; } if(w == 'w') { perms |= PROT_WRITE; } if(x == 'x') { perms |= PROT_EXEC; } stream.ignore(std::numeric_limits::max(), '\n'); return address_range{start, stop, perms}; } // returns a vector or nullopt if a tear is detected optional> try_load_mapped_region_info() { std::ifstream stream("/proc/self/maps"); stream>>std::hex; std::vector ranges; while(auto entry = read_map_entry(stream)) { const auto& range = entry.unwrap(); VERIFY(range.low <= range.high); if(!ranges.empty()) { const auto& last_range = ranges.back(); if(range.low < last_range.high) { return nullopt; } } ranges.push_back(range); } return ranges; } // we can allocate during try_load_mapped_region_info, in theory that could cause a tear optional> try_load_mapped_region_info_with_retries(int n) { VERIFY(n > 0); for(int i = 0; i < n; i++) { if(auto info = try_load_mapped_region_info()) { return info; } } throw internal_error("Couldn't successfully load /proc/self/maps after {} retries", n); } const std::vector& load_mapped_region_info() { static std::vector regions; static bool has_loaded = false; if(!has_loaded) { has_loaded = true; if(auto info = try_load_mapped_region_info_with_retries(2)) { regions = std::move(info).unwrap(); } } return regions; } int get_page_protections(void* page) { const auto& mapped_region_info = load_mapped_region_info(); auto it = first_less_than_or_equal( mapped_region_info.begin(), mapped_region_info.end(), reinterpret_cast(page), [](uintptr_t a, const address_range& b) { return a < b.low; } ); if(it == mapped_region_info.end()) { throw internal_error( "Failed to find mapping for {>16:0h} in /proc/self/maps", reinterpret_cast(page) ); } return it->perms; } #endif void mprotect_page(void* page, int page_size, int protections) { if(mprotect(page, page_size, protections) != 0) { throw internal_error("mprotect call failed: {}", strerror(errno)); } } int mprotect_page_and_return_old_protections(void* page, int page_size, int protections) { auto old_protections = get_page_protections(page); mprotect_page(page, page_size, protections); return old_protections; } void* allocate_page(int page_size) { auto page = mmap(nullptr, page_size, memory_readwrite, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if(page == MAP_FAILED) { throw internal_error("mmap call failed: {}", strerror(errno)); } return page; } #endif } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/platform/memory_mapping.hpp000066400000000000000000000013601504061443700214700ustar00rootroot00000000000000#include "utils/common.hpp" #ifndef _MSC_VER #if IS_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #else #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_WINDOWS constexpr auto memory_readonly = PAGE_READONLY; constexpr auto memory_readwrite = PAGE_READWRITE; #else constexpr auto memory_readonly = PROT_READ; constexpr auto memory_readwrite = PROT_READ | PROT_WRITE; #endif int get_page_size(); int mprotect_page_and_return_old_protections(void* page, int page_size, int protections); void mprotect_page(void* page, int page_size, int protections); void* allocate_page(int page_size); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/platform/path.hpp000066400000000000000000000023071504061443700174030ustar00rootroot00000000000000#ifndef PATH_HPP #define PATH_HPP #include "platform/platform.hpp" #include "utils/string_view.hpp" #include #include #if IS_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_WINDOWS constexpr char PATH_SEP = '\\'; inline bool is_absolute(string_view path) { // I don't want to bring in shlwapi as a dependency just for PathIsRelativeA so I'm following the guidance of // https://stackoverflow.com/a/71941552/15675011 and // https://github.com/wine-mirror/wine/blob/b210a204137dec8d2126ca909d762454fd47e963/dlls/kernelbase/path.c#L982 if(path.empty() || IsDBCSLeadByte(path[0])) { return false; } if(path[0] == '\\') { return true; } if(path.size() >= 2 && std::isalpha(path[0]) && path[1] == ':') { return true; } return false; } #else constexpr char PATH_SEP = '/'; inline bool is_absolute(string_view path) { if(path.empty()) { return false; } return path[0] == '/'; } #endif } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/platform/platform.hpp000066400000000000000000000015151504061443700202730ustar00rootroot00000000000000#ifndef PLATFORM_HPP #define PLATFORM_HPP #define IS_WINDOWS 0 #define IS_LINUX 0 #define IS_APPLE 0 #if defined(_WIN32) #undef IS_WINDOWS #define IS_WINDOWS 1 #elif defined(__linux) #undef IS_LINUX #define IS_LINUX 1 #elif defined(__APPLE__) #undef IS_APPLE #define IS_APPLE 1 #else #error "Unexpected platform" #endif #define IS_CLANG 0 #define IS_GCC 0 #define IS_MSVC 0 #if defined(__clang__) #undef IS_CLANG #define IS_CLANG 1 #elif defined(__GNUC__) || defined(__GNUG__) #undef IS_GCC #define IS_GCC 1 #elif defined(_MSC_VER) #undef IS_MSVC #define IS_MSVC 1 #else #error "Unsupported compiler" #endif #define IS_LIBSTDCXX 0 #define IS_LIBCXX 0 #if defined(__GLIBCXX__) || defined(__GLIBCPP__) #undef IS_LIBSTDCXX #define IS_LIBSTDCXX 1 #elif defined(_LIBCPP_VERSION) #undef IS_LIBCXX #define IS_LIBCXX 1 #endif #endif cpptrace-1.0.4/src/platform/program_name.hpp000066400000000000000000000050251504061443700211160ustar00rootroot00000000000000#ifndef PROGRAM_NAME_HPP #define PROGRAM_NAME_HPP #include #include #include "platform/platform.hpp" #if IS_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #define CPPTRACE_MAX_PATH MAX_PATH CPPTRACE_BEGIN_NAMESPACE namespace detail { inline const char* program_name() { static std::mutex mutex; const std::lock_guard lock(mutex); static std::string name; static bool did_init = false; static bool valid = false; if(!did_init) { did_init = true; char buffer[MAX_PATH + 1]; int res = GetModuleFileNameA(nullptr, buffer, MAX_PATH); if(res) { name = buffer; valid = true; } } return valid && !name.empty() ? name.c_str() : nullptr; } } CPPTRACE_END_NAMESPACE #elif IS_APPLE #include #include #include #define CPPTRACE_MAX_PATH CPPTRACE_PATH_MAX CPPTRACE_BEGIN_NAMESPACE namespace detail { inline const char* program_name() { static std::mutex mutex; const std::lock_guard lock(mutex); static std::string name; static bool did_init = false; static bool valid = false; if(!did_init) { did_init = true; char buffer[CPPTRACE_PATH_MAX + 1]; std::uint32_t bufferSize = sizeof buffer; if(_NSGetExecutablePath(buffer, &bufferSize) == 0) { name.assign(buffer, bufferSize); valid = true; } } return valid && !name.empty() ? name.c_str() : nullptr; } } CPPTRACE_END_NAMESPACE #elif IS_LINUX #include #include #define CPPTRACE_MAX_PATH CPPTRACE_PATH_MAX CPPTRACE_BEGIN_NAMESPACE namespace detail { inline const char* program_name() { static std::mutex mutex; const std::lock_guard lock(mutex); static std::string name; static bool did_init = false; static bool valid = false; if(!did_init) { did_init = true; char buffer[CPPTRACE_PATH_MAX + 1]; const ssize_t size = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX); if(size == -1) { return nullptr; } buffer[size] = 0; name = buffer; valid = true; } return valid && !name.empty() ? name.c_str() : nullptr; } } CPPTRACE_END_NAMESPACE #endif #endif cpptrace-1.0.4/src/prune_symbol.cpp000066400000000000000000001152071504061443700173400ustar00rootroot00000000000000#include "cpptrace/forward.hpp" #include #include #include #include "utils/error.hpp" #include "utils/optional.hpp" #include "utils/string_view.hpp" #include "utils/utils.hpp" // Docs // https://itanium-cxx-abi.github.io/cxx-abi/abi.html // https://en.wikiversity.org/wiki/Visual_C%2B%2B_name_mangling // Demangling // https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/demangle/ItaniumDemangle.h // https://github.com/gcc-mirror/gcc/blob/b76f1fb7bf8a7b66b8acd469309257f8b18c0c51/libiberty/cp-demangle.c#L6794 // https://github.com/wine-mirror/wine/blob/3295365ba5654d6ff2da37c1ffa84aed81291fc1/dlls/msvcrt/undname.c#L1476 // Mangling // https://github.com/llvm/llvm-project/blob/1463da8c4063cf1f1513aa5dbcedb44d2099c87f/clang/include/clang/AST/Mangle.h // https://github.com/llvm/llvm-project/blob/1463da8c4063cf1f1513aa5dbcedb44d2099c87f/clang/lib/AST/MicrosoftMangle.cpp#L1709-L1721 // Test cases // https://github.com/llvm/llvm-project/tree/d1b0b4bb4405c144e23be3d5c0459b03f95bd5ac/llvm/test/Demangle // https://github.com/llvm/llvm-project/blob/d1b0b4bb4405c144e23be3d5c0459b03f95bd5ac/libcxxabi/test/DemangleTestCases.inc // https://github.com/llvm/llvm-project/blob/d1b0b4bb4405c144e23be3d5c0459b03f95bd5ac/libcxxabi/test/test_demangle.pass.cpp // https://github.com/wine-mirror/wine/blob/3295365ba5654d6ff2da37c1ffa84aed81291fc1/dlls/msvcrt/tests/cpp.c#L108 // https://github.com/wine-mirror/wine/blob/3295365ba5654d6ff2da37c1ffa84aed81291fc1/dlls/ucrtbase/tests/cpp.c#L57 CPPTRACE_BEGIN_NAMESPACE namespace detail { template bool is_any(const T& value, const Arg& arg) { return value == arg; } template bool is_any(const T& value, const Arg& arg, const Args&... args) { return (value == arg) || is_any(value, args...); } // http://eel.is/c++draft/lex.name#nt:identifier bool is_identifier_start(char c) { return isalpha(c) || c == '$' || c == '_'; } bool is_identifier_continue(char c) { return isdigit(c) || is_identifier_start(c); } bool is_hex_digit(char c) { return isdigit(c) || is_any(c, 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F'); } bool is_octal_digit(char c) { return is_any(c, '0', '1', '2', '3', '4', '5', '6', '7'); } bool is_simple_escape_char(char c) { return is_any(c, '\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'); } // http://eel.is/c++draft/lex.operators#nt:operator-or-punctuator const std::vector punctuators_and_operators = []() { std::vector vec{ "{", "}", "[", "]", "(", ")", "<:", ":>", "<%", "%>", ";", ":", "...", "?", "::", ".", ".*", "->", "->*", "~", "!", "+", "-", "*", "/", "%", "^", "&", "|", "=", "+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", "==", "!=", "<", ">", "<=", ">=", "<=>", "&&", "||", "<<", ">>", "<<=", ">>=", "++", "--", ",", // "and", "or", "xor", "not", "bitand", "bitor", "compl", // "and_eq", "or_eq", "xor_eq", "not_eq", "#", // extension for {lambda()#1} }; std::sort(vec.begin(), vec.end(), [](string_view a, string_view b) { return a.size() > b.size(); }); return vec; } (); const std::array anonymous_namespace_spellings = {"(anonymous namespace)", "`anonymous namespace'"}; bool is_opening_punctuation(string_view token) { return token == "(" || token == "[" || token == "{" || token == "<"; } bool is_closing_punctuation(string_view token) { return token == ")" || token == "]" || token == "}" || token == ">"; } string_view get_corresponding_punctuation(string_view token) { if(token == "(") { return ")"; } else if(token == "[") { return "]"; } else if(token == "{") { return "}"; } else if(token == "<") { return ">"; } PANIC(); } bool is_ignored_identifier(string_view string) { return is_any(string, "const", "volatile", "decltype", "noexcept"); } bool is_microsoft_calling_convention(string_view string) { return is_any( string, "__cdecl", "__clrcall", "__stdcall", "__fastcall", "__thiscall", "__vectorcall" ); } // There are five kinds of tokens in C++: identifiers, keywords, literals, operators, and other separators // We tokenize a mostly-subset of this: // - identifiers/keywords // - literals: char, string, int, float. Msvc `strings' too. // - punctuation // Additionally we tokenize a few things that are useful // - anonymous namespace tags enum class token_type { identifier, punctuation, literal, anonymous_namespace }; struct token { token_type type; string_view str; bool operator==(const token& other) const { return type == other.type && str == other.str; } }; bool is_pointer_ref(const token& token) { return token.type == token_type::punctuation && is_any(token.str, "*", "&", "&&"); } struct parse_error { int x; // this works around a gcc bug with warn_unused_result and empty structs explicit parse_error() = default; string_view what() const { return "Parse error"; } }; #define CONCAT_IMPL(X, Y) X##Y #define CONCAT(X, Y) CONCAT_IMPL(X, Y) #define UNIQUE(X) CONCAT(X, __COUNTER__) #define TRY_PARSE_IMPL(ACTION, SUCCESS, RES) \ Result RES = (ACTION); \ if((RES).is_error()) { \ return std::move((RES)).unwrap_error(); \ } else if((RES).unwrap_value()) { \ SUCCESS; \ } #define TRY_PARSE(ACTION, SUCCESS) TRY_PARSE_IMPL(ACTION, SUCCESS, UNIQUE(res)) #define TRY_TOK_IMPL(RES, ACTION, TMP) \ const auto TMP = (ACTION); \ if((TMP).is_error()) { \ return std::move((TMP)).unwrap_error(); \ } \ const auto RES = std::move((TMP)).unwrap_value() #define TRY_TOK(RES, ACTION) TRY_TOK_IMPL(RES, ACTION, UNIQUE(tmp)) class symbol_tokenizer { private: string_view source; optional next_token; bool peek(string_view text, size_t pos = 0) const { return text == source.substr(pos, text.size()); } NODISCARD Result, parse_error> peek_anonymous_namespace() const { for(const auto& spelling : anonymous_namespace_spellings) { if(peek(spelling)) { return token{token_type::anonymous_namespace, {source.begin(), spelling.size()}}; } } return nullopt; } NODISCARD Result, parse_error> peek_number() const { // More or less following pp-number https://eel.is/c++draft/lex.ppnumber auto cursor = source.begin(); if(cursor != source.end() && std::isdigit(*cursor)) { while( cursor != source.end() && ( std::isdigit(*cursor) || is_identifier_continue(*cursor) || is_any(*cursor, '\'', '-', '+', '.') ) ) { cursor++; } } if(cursor == source.begin()) { return nullopt; } return token{token_type::literal, {source.begin(), cursor}}; } NODISCARD Result, parse_error> peek_msvc_string() const { // msvc strings look like `this' // they nest, e.g.: ``int main(void)'::`2'::::operator()(void)const' // TODO: Escapes? auto cursor = source.begin(); if(cursor != source.end() && *cursor == '`') { int depth = 0; do { if(*cursor == '`') { depth++; } else if(*cursor == '\'') { depth--; } cursor++; } while(cursor != source.end() && depth != 0); if(depth != 0) { return parse_error{}; } } if(cursor == source.begin()) { return nullopt; } return token{token_type::literal, {source.begin(), cursor}}; } NODISCARD Result, parse_error> parse_quoted_string() const { auto cursor = source.begin(); if(cursor != source.end() && is_any(*cursor, '\'', '"')) { auto closing_quote = *cursor; cursor++; while(cursor != source.end() && *cursor != closing_quote) { if(*cursor == '\\') { if(cursor + 1 == source.end()) { return parse_error{}; } cursor += 2; } cursor++; } if(cursor == source.end() || *cursor != closing_quote) { return parse_error{}; } cursor++; } if(cursor == source.begin()) { return nullopt; } return token{token_type::literal, {source.begin(), cursor}}; } NODISCARD Result, parse_error> peek_literal() const { TRY_TOK(number, peek_number()); if(number) { return number; } TRY_TOK(msvc_string, peek_msvc_string()); if(msvc_string) { return msvc_string; } TRY_TOK(quoted_string, parse_quoted_string()); if(quoted_string) { return quoted_string; } return nullopt; } NODISCARD Result, parse_error> peek_punctuation(size_t pos = 0) const { for(const auto punctuation : punctuators_and_operators) { if(peek(punctuation, pos)) { return token{token_type::punctuation, {source.begin() + pos, punctuation.size()}}; } } return nullopt; } NODISCARD Result, parse_error> peek_identifier(size_t pos = 0) const { auto start = source.begin() + std::min(pos, source.size());; auto cursor = start; if(cursor != source.end() && is_identifier_start(*cursor)) { while(cursor != source.end() && is_identifier_continue(*cursor)) { cursor++; } } if(cursor == start) { return nullopt; } return token{token_type::identifier, {start, cursor}}; } NODISCARD token peek_misc() const { ASSERT(!source.empty()); return token{token_type::punctuation, {source.begin(), 1}}; } Result maybe_load_next_token() { if(next_token.has_value()) { return monostate{}; } while(!source.empty() && std::isspace(source[0])) { source.advance(1); } if(source.empty()) { return monostate{}; } TRY_TOK(anon, peek_anonymous_namespace()); if(anon) { next_token = anon.unwrap(); return monostate{}; } TRY_TOK(literal, peek_literal()); if(literal) { next_token = literal.unwrap(); return monostate{}; } TRY_TOK(punctuation, peek_punctuation()); if(punctuation) { next_token = punctuation.unwrap(); return monostate{}; } TRY_TOK(identifier, peek_identifier()); if(identifier) { next_token = identifier.unwrap(); return monostate{}; } next_token = peek_misc(); return monostate{}; } optional get_adjusted_next_token(bool in_template_argument_list) { // https://eel.is/c++draft/temp.names#4 decompose >> to > when we think we're in a template argument list. // We don't have to do this for >>= or >=. if(next_token && in_template_argument_list && next_token.unwrap() == token{token_type::punctuation, ">>"}) { auto copy = next_token.unwrap(); copy.str = copy.str.substr(0, 1); // ">" return copy; } return next_token; } public: symbol_tokenizer(string_view source) : source(source) {} NODISCARD Result, parse_error> peek(bool in_template_argument_list = false) { auto res = maybe_load_next_token(); if(res.is_error()) { return res.unwrap_error(); } return get_adjusted_next_token(in_template_argument_list); } Result, parse_error> advance(bool in_template_argument_list = false) { TRY_TOK(next, peek(in_template_argument_list)); if(!next) { return nullopt; } source.advance(next.unwrap().str.size()); next_token.reset(); return next; } NODISCARD Result, parse_error> accept(token_type type, bool in_template_argument_list = false) { TRY_TOK(next, peek(in_template_argument_list)); if(next && next.unwrap().type == type) { advance(); return next; } return nullopt; } NODISCARD Result, parse_error> accept(token token, bool in_template_argument_list = false) { TRY_TOK(next, peek(in_template_argument_list)); if(next && next.unwrap() == token) { advance(); return next; } return nullopt; } }; std::string prune_symbol(string_view symbol); /* Approximate grammar, very hacky: full-symbol := { symbol } symbol := symbol-fragment { "::" ["*"] symbol-fragment } symbol-fragment := symbol-base [ pointer-refs ] [ punctuation-and-trailing-modifiers ] symbol-base := symbol-term function-pointer | symbol-term | function-pointer punctuation-and-trailing-modifiers := { [ balanced-punctuation ] [ ignored-identifier ] [ pointer-refs ] } symbol-term := anonymous-namespace | operator | name | lambda | unnamed function-pointer := "(" pointer-refs-junk symbol ")" pointer-refs-junk := { function-pointer-modifier } { pointer-refs } { ignored-identifier } anonymous-namespace := "(anonymous namespace)" | "`anonymous namespace'" operator := "operator" operator-type operator-type := new-delete | special-decltype | "co_await" | "\"\"" IDENTIFIER | OPERATOR | matching-punctuation | conversion-operator new-delete := ( "new" | "delete" ) [ "[" "]" ] special-decltype := "decltype" "(" ( "auto" | "nullptr" ) ")" matching-punctuation := "(" ")" | "[" "]" conversion-operator := symbol [ symbol ] // kind of a hack name := [ "~" ] IDENTIFIER lambda := "{" "lambda" { PUNCTUATION } "#" LITERAL "}" | LITERAL // 'lambda*' or `symbol' | "<" IDENTIFIER ">" // lambda_* unnamed := "{" "unnamed" "#" LITERAL "}" | LITERAL // 'unnamed*' balanced-punctuation := "(" balanced-punctuation-innards ")" | "[" balanced-punctuation-innards "]" | "<" balanced-punctuation-innards ">" | "{" balanced-punctuation-innards "}" balanced-punctuation-innards := { ANY-TOKEN } | balanced-punctuation pointer-refs := { "*" | "&" | "&&" } ignored-identifier := "const" | "volatile" | "decltype" | "noexcept" function-pointer-modifier := "__cdecl" | "__clrcall" | "__stdcall" | "__fastcall" | "__thiscall" | "__vectorcall" */ class symbol_parser { symbol_tokenizer& tokenizer; std::string name_output; bool last_was_identifier = false; bool reset_output_flag = false; void append_output(token token) { auto is_identifier = token.type == token_type::identifier; if(reset_output_flag) { name_output.clear(); reset_output_flag = false; last_was_identifier = false; } else if(is_identifier && last_was_identifier) { name_output += ' '; } name_output += token.str; last_was_identifier = is_identifier; } NODISCARD Result accept_pointer_ref(bool append) { bool matched = false; while(true) { TRY_TOK(token, tokenizer.peek()); if(!token || !is_pointer_ref(token.unwrap())) { break; } matched = true; if(append) { append_output(token.unwrap()); } tokenizer.advance(); } return matched; } NODISCARD Result consume_balanced(string_view opening_punctuation) { // priority means only consider this punctuation type and no priority means considers each independently bool is_in_template_list = opening_punctuation == "<"; int depth = 1; auto closing_punctuation = get_corresponding_punctuation(opening_punctuation); while(depth > 0) { // first try to accept an operator, this is for things like void foo<&S::operator>(S const&)>()::test if(is_in_template_list) { TRY_PARSE(accept_operator(true), continue); } // otherwise handle arbitrary token TRY_TOK(next, tokenizer.advance(is_in_template_list)); if(!next) { break; } if(next.unwrap().type == token_type::punctuation) { if(next.unwrap().str == opening_punctuation) { depth++; } else if(next.unwrap().str == closing_punctuation) { depth--; } else if(is_in_template_list && is_opening_punctuation(next.unwrap().str)) { // If we're in a template list, recurse into any balanced punctuation we see. This handles cases // like foo<(S > S)> TRY_PARSE(consume_balanced(next.unwrap().str), (void)0); } } } return true; } NODISCARD Result accept_balanced_punctuation() { TRY_TOK(token, tokenizer.peek()); if(token && token.unwrap().type == token_type::punctuation && is_opening_punctuation(token.unwrap().str)) { tokenizer.advance(); TRY_PARSE(consume_balanced(token.unwrap().str), (void)0); return true; } return false; } NODISCARD Result accept_ignored_identifier() { TRY_TOK(token, tokenizer.peek()); if(token && token.unwrap().type == token_type::identifier && is_ignored_identifier(token.unwrap().str)) { tokenizer.advance(); return true; } return false; } NODISCARD Result accept_anonymous_namespace() { TRY_TOK(token, tokenizer.accept(token_type::anonymous_namespace)); if(token) { append_output({ token_type::identifier, "(anonymous namespace)" }); return true; } return false; } NODISCARD Result accept_new_delete() { TRY_TOK(token, tokenizer.peek()); if(token && token.unwrap().type == token_type::identifier && is_any(token.unwrap().str, "new", "delete")) { tokenizer.advance(); append_output(token.unwrap()); TRY_TOK(op, tokenizer.accept({token_type::punctuation, "["})); if(op) { append_output(op.unwrap()); TRY_TOK(op2, tokenizer.accept({token_type::punctuation, "]"})); if(!op2) { return parse_error{}; } append_output(op2.unwrap()); } return true; } return false; } NODISCARD Result accept_special_decltype() { TRY_TOK(token, tokenizer.accept({token_type::identifier, "decltype"})); if(token) { append_output(token.unwrap()); TRY_TOK(op, tokenizer.accept({token_type::punctuation, "("})); if(!op) { return parse_error{}; } append_output(op.unwrap()); TRY_TOK(ident, tokenizer.accept(token_type::identifier)); if(!ident) { return parse_error{}; } if(!is_any(ident.unwrap().str, "auto", "nullptr")) { return parse_error{}; } append_output(ident.unwrap()); TRY_TOK(op2, tokenizer.accept({token_type::punctuation, ")"})); if(!op2) { return parse_error{}; } append_output(op2.unwrap()); return true; } return false; } NODISCARD Result accept_operator(bool is_in_template_list = false) { // If we're in a template argument list we skip new/delete/auto/co_await/literal/conversion and only handle // the operator punctuation case. We also don't append for template lists. TRY_TOK(token, tokenizer.accept({token_type::identifier, "operator"})); if(token) { if(!is_in_template_list) { append_output(token.unwrap()); TRY_PARSE(accept_new_delete(), return true); TRY_PARSE(accept_special_decltype(), return true); TRY_TOK(coawait, tokenizer.accept({token_type::identifier, "co_await"})); if(coawait) { append_output(coawait.unwrap()); return true; } TRY_TOK(literal, tokenizer.accept({token_type::literal, "\"\""})); if(literal) { TRY_TOK(name, tokenizer.accept(token_type::identifier)); if(!name) { return parse_error{}; } append_output(literal.unwrap()); append_output(name.unwrap()); return true; } } TRY_TOK(op, tokenizer.accept(token_type::punctuation)); if(op) { if(!is_in_template_list) { append_output(op.unwrap()); } if(is_any(op.unwrap().str, "(", "[")) { TRY_TOK(op2, tokenizer.accept(token_type::punctuation)); if(!op2 || op2.unwrap().str != get_corresponding_punctuation(op.unwrap().str)) { return parse_error{}; } if(!is_in_template_list) { append_output(op2.unwrap()); } } return true; } if(!is_in_template_list) { // Otherwise try to parse a symbol, in the case of a conversion operator // There is a bit of a grammer hack here, it doesn't properly "nest," but it works TRY_PARSE(parse_symbol(), (void)0); // In the case of a member function pointer, there will be a type symbol followed by a symbol for // the type that the member pointer points to TRY_TOK(maybe_ident, tokenizer.peek()); if(maybe_ident && maybe_ident.unwrap().type == token_type::identifier) { TRY_PARSE(parse_symbol(), (void)0); } return true; } } return false; } NODISCARD Result accept_identifier_token() { bool expect = false; TRY_TOK(complement, tokenizer.accept({token_type::punctuation, "~"})); if(complement) { append_output(complement.unwrap()); expect = true; } TRY_TOK(token, tokenizer.accept(token_type::identifier)); if(token) { append_output(token.unwrap()); return true; } else if(expect) { return parse_error{}; } return false; } NODISCARD Result consume_punctuation() { bool did_consume = false; while(true) { TRY_TOK(token, tokenizer.peek()); if( !token || !( token.unwrap().type == token_type::punctuation && token.unwrap().str != "::" && token.unwrap().str != "#" ) ) { break; } TRY_PARSE(accept_balanced_punctuation(), {did_consume = true; continue;}); // otherwise, if not balanced punctuation, just consume and drop tokenizer.advance(); did_consume = true; } return did_consume; } NODISCARD Result accept_lambda_or_unnamed() { // LLVM does main::'lambda'<...>(...)::operator()<...>(...) -- apparently this can be 'lambda' // GCC does main::{lambda<...>(...)#1}::operator()<...>(...) // MSVC does `int main(void)'::`2'::::operator()<...>(...) // https://github.com/llvm/llvm-project/blob/90beda2aba3cac34052827c560449fcb184c7313/libcxxabi/src/demangle/ItaniumDemangle.h#L1848-L1850 TODO: What about the count? // https://github.com/gcc-mirror/gcc/blob/b76f1fb7bf8a7b66b8acd469309257f8b18c0c51/libiberty/cp-demangle.c#L6210-L6251 TODO: What special characters can appear? TRY_TOK(opening_brace, tokenizer.accept({token_type::punctuation, "{"})); if(opening_brace) { token token1{}; token token2{}; bool two_tokens = false; // this awfulness to work around gcc's maybe-uninitialized analysis TRY_TOK(lambda_token, tokenizer.accept({token_type::identifier, "lambda"})); if(lambda_token) { token1 = lambda_token.unwrap(); } else { TRY_TOK(unnamed_token, tokenizer.accept({token_type::identifier, "unnamed"})); if(!unnamed_token) { return parse_error{}; } TRY_TOK(type_token, tokenizer.accept({token_type::identifier, "type"})); if(!type_token) { return parse_error{}; } token1 = unnamed_token.unwrap(); token2 = type_token.unwrap(); two_tokens = true; } TRY_PARSE(consume_punctuation(), (void)0); TRY_TOK(hash_token, tokenizer.accept({token_type::punctuation, "#"})); if(!hash_token) { return parse_error{}; } TRY_TOK(discriminator_token, tokenizer.accept(token_type::literal)); if(!discriminator_token) { return parse_error{}; } TRY_TOK(closing_brace, tokenizer.accept({token_type::punctuation, "}"})); if(!closing_brace) { return parse_error{}; } append_output({token_type::punctuation, "<"}); append_output(token1); if(two_tokens) { append_output(token2); } append_output(hash_token.unwrap()); append_output(discriminator_token.unwrap()); append_output({token_type::punctuation, ">"}); return true; } TRY_TOK(maybe_literal_token, tokenizer.peek()); if( maybe_literal_token && maybe_literal_token.unwrap().type == token_type::literal && ( maybe_literal_token.unwrap().str.starts_with("'lambda") || maybe_literal_token.unwrap().str.starts_with("'unnamed") ) && maybe_literal_token.unwrap().str.ends_with("'") ) { tokenizer.advance(); append_output({token_type::punctuation, "<"}); auto str = maybe_literal_token.unwrap().str; append_output({token_type::punctuation, str.substr(1, str.size() - 2)}); append_output({token_type::punctuation, ">"}); return true; } if( maybe_literal_token && maybe_literal_token.unwrap().type == token_type::literal && maybe_literal_token.unwrap().str.starts_with("`") && maybe_literal_token.unwrap().str.ends_with("'") ) { tokenizer.advance(); // append_output(maybe_literal_token.unwrap()); // This string is going to be another symbol, recursively reduce it append_output({token_type::punctuation, "`"}); auto symbol = maybe_literal_token.unwrap().str; ASSERT(symbol.size() >= 2); auto name = detail::prune_symbol(symbol.substr(1, symbol.size() - 2)); append_output({token_type::literal, name}); append_output({token_type::punctuation, "'"}); return true; } TRY_TOK(opening_bracket, tokenizer.accept({token_type::punctuation, "<"})); if(opening_bracket) { TRY_TOK(lambda_token, tokenizer.accept(token_type::identifier)); if(!lambda_token || !lambda_token.unwrap().str.starts_with("lambda_")) { return parse_error{}; } TRY_TOK(closing_bracket, tokenizer.accept({token_type::punctuation, ">"})); if(!closing_bracket) { return parse_error{}; } append_output(opening_bracket.unwrap()); append_output(lambda_token.unwrap()); append_output(closing_bracket.unwrap()); return true; } return false; } // lookahead to match "(*", "(__cdecl*", etc NODISCARD bool lookahead_is_function_pointer() const { auto res = [] (symbol_tokenizer tokenizer_copy) -> Result { TRY_TOK(maybe_opening_parenthesis, tokenizer_copy.accept({token_type::punctuation, "("})); if(maybe_opening_parenthesis) { while(true) { auto res = tokenizer_copy.advance(); if(res.is_error()) { return false; } auto token = res.unwrap_value(); if(token && is_pointer_ref(token.unwrap())) { return true; } else if( token && token.unwrap().type == token_type::identifier && is_microsoft_calling_convention(token.unwrap().str) ) { // pass } else { break; } } return false; } return false; } (tokenizer); if(res.is_error()) { return false; } return res.unwrap_value(); } NODISCARD Result parse_function_pointer() { ASSERT(lookahead_is_function_pointer()); TRY_TOK(opening_parenthesis, tokenizer.accept({token_type::punctuation, "("})); if(opening_parenthesis) { bool saw_pointer = false; while(true) { TRY_TOK(next, tokenizer.peek()); if(next && is_pointer_ref(next.unwrap())) { tokenizer.advance(); saw_pointer = true; } else if( next && next.unwrap().type == token_type::identifier && ( is_microsoft_calling_convention(next.unwrap().str) || is_ignored_identifier(next.unwrap().str) ) ) { tokenizer.advance(); } else { break; } } if(!saw_pointer) { return parse_error{}; } bool did_parse = false; reset_output_flag = true; TRY_PARSE(parse_symbol(), did_parse = true); if(!did_parse) { return parse_error{}; } TRY_TOK(closing_parenthesis, tokenizer.accept({token_type::punctuation, ")"})); if(!closing_parenthesis) { return parse_error{}; } return true; } return false; } NODISCARD Result parse_symbol_term() { TRY_PARSE(accept_anonymous_namespace(), return true); TRY_PARSE(accept_operator(), return true); TRY_PARSE(accept_identifier_token(), return true); TRY_PARSE(accept_lambda_or_unnamed(), return true); return false; } NODISCARD Result consume_punctuation_and_trailing_modifiers() { bool did_consume = false; while(true) { TRY_TOK(token, tokenizer.peek()); if(!token) { break; } TRY_PARSE(accept_balanced_punctuation(), {did_consume = true; continue;}); TRY_PARSE(accept_ignored_identifier(), {did_consume = true; continue;}); TRY_PARSE(accept_pointer_ref(false), {did_consume = true; continue;}); break; } return did_consume; } NODISCARD Result parse_symbol() { bool made_progress = false; while(true) { TRY_TOK(token, tokenizer.peek()); if(!token) { break; } bool did_match_term = false; TRY_PARSE(parse_symbol_term(), did_match_term = true); if(lookahead_is_function_pointer()) { TRY_PARSE(parse_function_pointer(), did_match_term = true); } if(did_match_term) { made_progress = true; } else { break; } TRY_PARSE(accept_pointer_ref(true), made_progress = true); TRY_PARSE(consume_punctuation_and_trailing_modifiers(), made_progress = true); TRY_TOK(scope_resolution, tokenizer.accept({token_type::punctuation, "::"})); if(scope_resolution) { append_output(scope_resolution.unwrap()); made_progress = true; // for pointer to members TRY_TOK(star, tokenizer.accept({token_type::punctuation, "*"})); if(star) { append_output(star.unwrap()); } } else { break; } } return made_progress; } public: symbol_parser(symbol_tokenizer& tokenizer) : tokenizer(tokenizer) {} NODISCARD Result parse() { while(true) { TRY_TOK(token, tokenizer.peek()); if(!token) { break; } reset_output_flag = true; bool made_progress = false; TRY_PARSE(parse_symbol(), made_progress = true); if(!made_progress) { return parse_error{}; } } return monostate{}; } NODISCARD std::string name() && { return std::move(name_output); } }; NODISCARD std::string prune_symbol(string_view symbol) { detail::symbol_tokenizer tokenizer(symbol); detail::symbol_parser parser(tokenizer); auto res = parser.parse(); if(res.is_error()) { // error return std::string(symbol); } auto name = std::move(parser).name(); if(name.empty()) { return std::string(symbol); } return name; } } CPPTRACE_END_NAMESPACE CPPTRACE_BEGIN_NAMESPACE std::string prune_symbol(const std::string& symbol) { try { return detail::prune_symbol(symbol); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); return symbol; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/snippets/000077500000000000000000000000001504061443700157555ustar00rootroot00000000000000cpptrace-1.0.4/src/snippets/snippet.cpp000066400000000000000000000154111504061443700201450ustar00rootroot00000000000000#include "snippets/snippet.hpp" #include #include #include #include #include #include #include #include "utils/common.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { constexpr std::int64_t max_size = 1024 * 1024 * 10; // 10 MiB struct line_range { std::size_t begin; std::size_t end; // one past the end }; class snippet_manager { bool loaded_contents; std::string contents; // 1-based indexing std::vector line_table; public: snippet_manager(const std::string& path) : loaded_contents(false) { std::ifstream file; try { file.open(path, std::ios::ate); if(file.is_open()) { std::ifstream::pos_type size = file.tellg(); if(size == std::ifstream::pos_type(-1) || size > max_size) { return; } // else load file file.seekg(0, std::ios::beg); contents.resize(to(size)); if(!file.read(&contents[0], size)) { // error ... } build_line_table(); loaded_contents = true; } } catch(const std::ifstream::failure&) { // ... } } // takes a 1-index line std::string get_line(std::size_t line) const { if(!loaded_contents || line > line_table.size()) { return ""; } else { return contents.substr(line_table[line].begin, line_table[line].end - line_table[line].begin); } } std::size_t num_lines() const { return line_table.size(); } bool ok() const { return loaded_contents; } private: void build_line_table() { line_table.push_back({0, 0}); std::size_t pos = 0; // stores the start of the current line while(true) { // find the end of the current line std::size_t terminator_pos = contents.find('\n', pos); if(terminator_pos == std::string::npos) { line_table.push_back({pos, contents.size()}); break; } else { std::size_t end_pos = terminator_pos; // one past the end of the current line if(end_pos > 0 && contents[end_pos - 1] == '\r') { end_pos--; } line_table.push_back({pos, end_pos}); pos = terminator_pos + 1; } } } }; const snippet_manager& get_manager(const std::string& path) { static std::mutex snippet_manager_mutex; static std::unordered_map snippet_managers; std::unique_lock lock(snippet_manager_mutex); auto it = snippet_managers.find(path); if(it == snippet_managers.end()) { return snippet_managers.insert({path, snippet_manager(path)}).first->second; } else { return it->second; } } // how wide the margin for the line number should be constexpr std::size_t margin_width = 8; struct snippet_context { std::size_t original_begin; std::size_t begin; std::size_t end; std::vector lines; }; optional get_lines(const std::string& path, std::size_t target_line, std::size_t context_size) { const auto& manager = get_manager(path); if(!manager.ok()) { return nullopt; } auto begin = target_line <= context_size + 1 ? 1 : target_line - context_size; auto original_begin = begin; auto end = std::min(target_line + context_size, manager.num_lines() - 1); std::vector lines; for(auto line = begin; line <= end; line++) { lines.push_back(manager.get_line(line)); } // trim blank lines while(begin < target_line && lines[begin - original_begin].empty()) { begin++; } while(end > target_line && lines[end - original_begin].empty()) { end--; } return snippet_context{original_begin, begin, end, std::move(lines)}; } std::string write_line_number(std::size_t line, std::size_t target_line, bool color) { std::string snippet; auto line_str = std::to_string(line); if(line == target_line) { if(color) { snippet += YELLOW; } auto line_width = line_str.size() + 3; snippet += microfmt::format( "{>{}} > {}: ", line_width > margin_width ? 0 : margin_width - line_width, "", line_str ); if(color) { snippet += RESET; } } else { snippet += microfmt::format("{>{}}: ", margin_width, line_str); } return snippet; } std::string write_carrot(std::uint32_t column, bool color) { std::string snippet; snippet += microfmt::format("\n{>{}}", margin_width + 2 + column - 1, ""); if(color) { snippet += YELLOW; } snippet += "^"; if(color) { snippet += RESET; } return snippet; } std::string write_line( std::size_t line, nullable column, std::size_t target_line, bool color, const snippet_context& context ) { std::string snippet; snippet += write_line_number(line, target_line, color); snippet += context.lines[line - context.original_begin]; if(line == target_line && column.has_value()) { snippet += write_carrot(column.value(), color); } return snippet; } // 1-indexed line std::string get_snippet( const std::string& path, std::size_t target_line, nullable column, std::size_t context_size, bool color ) { const auto context_res = get_lines(path, target_line, context_size); if(!context_res) { return ""; } const auto& context = context_res.unwrap(); std::string snippet; for(auto line = context.begin; line <= context.end; line++) { snippet += write_line(line, column, target_line, color, context); if(line != context.end) { snippet += '\n'; } } return snippet; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/snippets/snippet.hpp000066400000000000000000000006151504061443700201520ustar00rootroot00000000000000#ifndef SNIPPET_HPP #define SNIPPET_HPP #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { // 1-indexed line std::string get_snippet( const std::string& path, std::size_t line, nullable column, std::size_t context_size, bool color ); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/000077500000000000000000000000001504061443700156005ustar00rootroot00000000000000cpptrace-1.0.4/src/symbols/dwarf/000077500000000000000000000000001504061443700167035ustar00rootroot00000000000000cpptrace-1.0.4/src/symbols/dwarf/debug_map_resolver.cpp000066400000000000000000000176311504061443700232630ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF #include "symbols/dwarf/resolver.hpp" #include #include "symbols/symbols.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "binary/object.hpp" #include "binary/mach-o.hpp" #include "utils/utils.hpp" #include #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdwarf { #if IS_APPLE struct target_object { std::string object_path; bool path_ok = true; optional> symbols; std::unique_ptr resolver; target_object(std::string object_path) : object_path(std::move(object_path)) {} std::unique_ptr& get_resolver() { if(!resolver) { // this seems silly but it's an attempt to not repeatedly try to initialize new dwarf_resolvers if // exceptions are thrown, e.g. if the path doesn't exist resolver = detail::make_unique(); resolver = make_dwarf_resolver(object_path); } return resolver; } std::unordered_map& get_symbols() { if(!symbols) { // this is an attempt to not repeatedly try to reprocess mach-o files if exceptions are thrown, e.g. if // the path doesn't exist std::unordered_map symbols; this->symbols = symbols; auto mach_o_object = open_mach_o_cached(object_path); if(!mach_o_object) { return this->symbols.unwrap(); } const auto& symbol_table = mach_o_object.unwrap_value()->symbol_table(); if(!symbol_table) { return this->symbols.unwrap(); } for(const auto& symbol : symbol_table.unwrap_value()) { symbols[symbol.name] = symbol.address; } this->symbols = std::move(symbols); } return symbols.unwrap(); } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING frame_with_inlines resolve_frame( const object_frame& frame_info, const std::string& symbol_name, std::size_t offset ) { const auto& symbol_table = get_symbols(); auto it = symbol_table.find(symbol_name); if(it != symbol_table.end()) { auto frame = frame_info; // substitute a translated address object for the target file in frame.object_address = it->second + offset; auto res = get_resolver()->resolve_frame(frame); // replace the translated address with the object address in the binary res.frame.object_address = frame_info.object_address; return res; } else { return { { frame_info.raw_address, frame_info.object_address, nullable::null(), nullable::null(), frame_info.object_path, symbol_name, false }, {} }; } } }; struct debug_map_symbol_info { uint64_t source_address; uint64_t size; std::string name; nullable target_address; // T(-1) is used as a sentinel std::size_t object_index; // index into target_objects }; class debug_map_resolver : public symbol_resolver { std::vector target_objects; std::vector symbols; public: debug_map_resolver(const std::string& source_object_path) { // load mach-o // TODO: Cache somehow? auto mach_o_object = open_mach_o_cached(source_object_path); if(!mach_o_object) { return; } mach_o& source_mach = *mach_o_object.unwrap_value(); auto source_debug_map = source_mach.get_debug_map(); if(!source_debug_map) { return; } // get symbol entries from debug map, as well as the various object files used to make this binary for(auto& entry : source_debug_map.unwrap_value()) { // object it came from target_objects.push_back({entry.first}); // push the symbols auto& map_entry_symbols = entry.second; symbols.reserve(symbols.size() + map_entry_symbols.size()); for(auto& symbol : map_entry_symbols) { symbols.push_back({ symbol.source_address, symbol.size, std::move(symbol.name), nullable::null(), target_objects.size() - 1 }); } } // sort for binary lookup later // TODO: Redundant? std::sort( symbols.begin(), symbols.end(), [] ( const debug_map_symbol_info& a, const debug_map_symbol_info& b ) { return a.source_address < b.source_address; } ); } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING frame_with_inlines resolve_frame(const object_frame& frame_info) override { // resolve object frame: // find the symbol in this executable corresponding to the object address // resolve the symbol in the object it came from, based on the symbol name auto closest_symbol_it = first_less_than_or_equal( symbols.begin(), symbols.end(), frame_info.object_address, [] ( uint64_t pc, const debug_map_symbol_info& symbol ) { return pc < symbol.source_address; } ); if(closest_symbol_it != symbols.end()) { if(frame_info.object_address <= closest_symbol_it->source_address + closest_symbol_it->size) { return target_objects[closest_symbol_it->object_index].resolve_frame( { frame_info.raw_address, // the resolver doesn't care about the object address here, only the offset from the start // of the symbol and it'll lookup the symbol's base-address frame_info.object_address, frame_info.object_path }, closest_symbol_it->name, frame_info.object_address - closest_symbol_it->source_address ); } } // There was either no closest symbol or the closest symbol didn't end up containing the address we're // looking for, so just return a blank frame return { { frame_info.raw_address, frame_info.object_address, nullable::null(), nullable::null(), frame_info.object_path, "", false }, {} }; }; }; std::unique_ptr make_debug_map_resolver(const std::string& object_path) { return detail::make_unique(object_path); } #endif } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/dwarf/dwarf.hpp000066400000000000000000000500271504061443700205230ustar00rootroot00000000000000#ifndef DWARF_HPP #define DWARF_HPP #include #include "utils/error.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" #include #include #ifdef CPPTRACE_USE_NESTED_LIBDWARF_HEADER_PATH #include #include #else #include #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdwarf { static_assert(std::is_pointer::value, "Dwarf_Die not a pointer"); static_assert(std::is_pointer::value, "Dwarf_Debug not a pointer"); using rangelist_entries = std::vector>; [[noreturn]] inline void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) { Dwarf_Unsigned ev = dwarf_errno(error); // dwarf_dealloc_error deallocates the message, attaching to msg is convenient auto msg = raii_wrap(dwarf_errmsg(error), [dbg, error] (char*) { dwarf_dealloc_error(dbg, error); }); throw internal_error("dwarf error {} {}", ev, msg.get()); } struct die_object { Dwarf_Debug dbg = nullptr; Dwarf_Die die = nullptr; // Error handling helper // For some reason R (*f)(Args..., void*)-style deduction isn't possible, seems like a bug in all compilers // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56190 template< typename... Args, typename... Args2, typename std::enable_if< std::is_same< decltype( (void)std::declval()(std::forward(std::declval())..., nullptr) ), void >::value, int >::type = 0 > int wrap(int (*f)(Args...), Args2&&... args) const { Dwarf_Error error = nullptr; int ret = f(std::forward(args)..., &error); if(ret == DW_DLV_ERROR) { handle_dwarf_error(dbg, error); } return ret; } die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) { ASSERT(dbg != nullptr); } ~die_object() { release(); } void release() { if(die) { dwarf_dealloc_die(exchange(die, nullptr)); } } die_object(const die_object&) = delete; die_object& operator=(const die_object&) = delete; // dbg doesn't strictly have to be st to null but it helps ensure attempts to use the die_object after this to // segfault. A valid use otherwise would be moved_from.get_sibling() which would get the next CU. die_object(die_object&& other) noexcept : dbg(exchange(other.dbg, nullptr)), die(exchange(other.die, nullptr)) {} die_object& operator=(die_object&& other) noexcept { release(); dbg = exchange(other.dbg, nullptr); die = exchange(other.die, nullptr); return *this; } die_object clone() const { Dwarf_Off global_offset = get_global_offset(); Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); Dwarf_Die die_copy = nullptr; VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &die_copy) == DW_DLV_OK); return {dbg, die_copy}; } die_object get_child() const { Dwarf_Die child = nullptr; int ret = wrap(dwarf_child, die, &child); if(ret == DW_DLV_OK) { return die_object(dbg, child); } else if(ret == DW_DLV_NO_ENTRY) { return die_object(dbg, nullptr); } else { PANIC(); } } die_object get_sibling() const { Dwarf_Die sibling = nullptr; int ret = wrap(dwarf_siblingof_b, dbg, die, true, &sibling); if(ret == DW_DLV_OK) { return die_object(dbg, sibling); } else if(ret == DW_DLV_NO_ENTRY) { return die_object(dbg, nullptr); } else { PANIC(); } } operator bool() const { return die != nullptr; } Dwarf_Die get() const { return die; } std::string get_name() const { char empty[] = ""; char* name = empty; // Note: It's important to not free the string from this function. int ret = wrap(dwarf_diename, die, &name); std::string str; if(ret != DW_DLV_NO_ENTRY) { str = name; } return name; } optional get_string_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) { auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); char* raw_str; // Note: It's important to not free the string from this function. // https://github.com/davea42/libdwarf-code/issues/279 VERIFY(wrap(dwarf_formstring, attr, &raw_str) == DW_DLV_OK); std::string str = raw_str; return str; } else { return nullopt; } } optional get_unsigned_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) { auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); // Dwarf_Half form = 0; // VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); Dwarf_Unsigned val; VERIFY(wrap(dwarf_formudata, attr, &val) == DW_DLV_OK); return val; } else { return nullopt; } } bool has_attr(Dwarf_Half attr_num) const { Dwarf_Bool present = false; VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK); return present; } Dwarf_Half get_tag() const { Dwarf_Half tag = 0; VERIFY(wrap(dwarf_tag, die, &tag) == DW_DLV_OK); return tag; } const char* get_tag_name() const { const char* tag_name; if(dwarf_get_TAG_name(get_tag(), &tag_name) == DW_DLV_OK) { return tag_name; } else { return ""; } } Dwarf_Off get_global_offset() const { Dwarf_Off off; VERIFY(wrap(dwarf_dieoffset, die, &off) == DW_DLV_OK); return off; } die_object resolve_reference_attribute(Dwarf_Half attr_num) const { Dwarf_Attribute attr; VERIFY(dwarf_attr(die, attr_num, &attr, nullptr) == DW_DLV_OK); auto wrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); Dwarf_Half form = 0; VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); switch(form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: { Dwarf_Off off = 0; Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); VERIFY(wrap(dwarf_formref, attr, &off, &is_info) == DW_DLV_OK); Dwarf_Off global_offset = 0; VERIFY(wrap(dwarf_convert_to_global_offset, attr, off, &global_offset) == DW_DLV_OK); Dwarf_Die target = nullptr; VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &target) == DW_DLV_OK); return die_object(dbg, target); } case DW_FORM_ref_addr: { Dwarf_Off off; VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK); int is_info = dwarf_get_die_infotypes_flag(die); Dwarf_Die target = nullptr; VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info, &target) == DW_DLV_OK); return die_object(dbg, target); } case DW_FORM_ref_sig8: { Dwarf_Sig8 signature; VERIFY(wrap(dwarf_formsig8, attr, &signature) == DW_DLV_OK); Dwarf_Die target = nullptr; Dwarf_Bool targ_is_info = false; VERIFY(wrap(dwarf_find_die_given_sig8, dbg, &signature, &target, &targ_is_info) == DW_DLV_OK); return die_object(dbg, target); } default: PANIC(microfmt::format("unknown form for attribute {} {}\n", attr_num, form)); } } Dwarf_Unsigned get_ranges_base_address(const die_object& cu_die) const { // After libdwarf v0.11.0 this can use dwarf_get_ranges_baseaddress, however, in the interest of not // requiring v0.11.0 just yet the logic is implemented here too. // The base address is: // - If the die has a rangelist, use the low_pc for that die // - Otherwise use the low_pc from the CU if present // - Otherwise 0 if(has_attr(DW_AT_ranges)) { if(has_attr(DW_AT_low_pc)) { Dwarf_Addr lowpc; if(wrap(dwarf_lowpc, die, &lowpc) == DW_DLV_OK) { return lowpc; } } } if(cu_die.has_attr(DW_AT_low_pc)) { Dwarf_Addr lowpc; if(wrap(dwarf_lowpc, cu_die.get(), &lowpc) == DW_DLV_OK) { return lowpc; } } return 0; } Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) const { Dwarf_Unsigned off = 0; Dwarf_Half form = 0; VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); if (form == DW_FORM_rnglistx) { VERIFY(wrap(dwarf_formudata, attr, &off) == DW_DLV_OK); } else { VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK); } return off; } template // callback should return true to keep going void dwarf5_ranges(F callback) const { Dwarf_Attribute attr = nullptr; if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) { return; } auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); Dwarf_Unsigned offset = get_ranges_offset(attr); Dwarf_Half form = 0; VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK); // get .debug_rnglists info Dwarf_Rnglists_Head head = nullptr; Dwarf_Unsigned rnglists_entries = 0; Dwarf_Unsigned dw_global_offset_of_rle_set = 0; int res = wrap( dwarf_rnglists_get_rle_head, attr, form, offset, &head, &rnglists_entries, &dw_global_offset_of_rle_set ); auto headwrapper = raii_wrap(head, [] (Dwarf_Rnglists_Head head) { dwarf_dealloc_rnglists_head(head); }); if(res == DW_DLV_NO_ENTRY) { return; } VERIFY(res == DW_DLV_OK); for(std::size_t i = 0 ; i < rnglists_entries; i++) { unsigned entrylen = 0; unsigned rle_value_out = 0; Dwarf_Unsigned raw1 = 0; Dwarf_Unsigned raw2 = 0; Dwarf_Bool unavailable = 0; Dwarf_Unsigned cooked1 = 0; Dwarf_Unsigned cooked2 = 0; res = wrap( dwarf_get_rnglists_entry_fields_a, head, i, &entrylen, &rle_value_out, &raw1, &raw2, &unavailable, &cooked1, &cooked2 ); if(res == DW_DLV_NO_ENTRY) { continue; } VERIFY(res == DW_DLV_OK); if(unavailable) { continue; } switch(rle_value_out) { // Following the same scheme from libdwarf-addr2line case DW_RLE_end_of_list: case DW_RLE_base_address: case DW_RLE_base_addressx: // Already handled break; case DW_RLE_offset_pair: case DW_RLE_startx_endx: case DW_RLE_start_end: case DW_RLE_startx_length: case DW_RLE_start_length: if(!callback(cooked1, cooked2)) { return; } break; default: PANIC("Something is wrong"); break; } } } template // callback should return true to keep going void dwarf4_ranges(Dwarf_Addr baseaddr, F callback) const { Dwarf_Attribute attr = nullptr; if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) { return; } auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); }); Dwarf_Unsigned offset; if(wrap(dwarf_global_formref, attr, &offset) != DW_DLV_OK) { return; } Dwarf_Addr baseaddr_original = baseaddr; Dwarf_Ranges* ranges = nullptr; Dwarf_Signed count = 0; VERIFY( wrap( dwarf_get_ranges_b, dbg, offset, die, nullptr, &ranges, &count, nullptr ) == DW_DLV_OK ); auto rangeswrapper = raii_wrap( ranges, [this, count] (Dwarf_Ranges* ranges) { dwarf_dealloc_ranges(dbg, ranges, count); } ); for(int i = 0; i < count; i++) { if(ranges[i].dwr_type == DW_RANGES_ENTRY) { if(!callback(baseaddr + ranges[i].dwr_addr1, baseaddr + ranges[i].dwr_addr2)) { return; } } else if(ranges[i].dwr_type == DW_RANGES_ADDRESS_SELECTION) { baseaddr = ranges[i].dwr_addr2; } else { ASSERT(ranges[i].dwr_type == DW_RANGES_END); baseaddr = baseaddr_original; } } } template // callback should return true to keep going void dwarf_ranges(const die_object& cu_die, int version, F callback) const { Dwarf_Addr lowpc; if(wrap(dwarf_lowpc, die, &lowpc) == DW_DLV_OK) { Dwarf_Addr highpc = 0; enum Dwarf_Form_Class return_class; if(wrap(dwarf_highpc_b, die, &highpc, nullptr, &return_class) == DW_DLV_OK) { if(return_class == DW_FORM_CLASS_CONSTANT) { highpc += lowpc; } if(!callback(lowpc, highpc)) { return; } } } if(version >= 5) { dwarf5_ranges(callback); } else { dwarf4_ranges(get_ranges_base_address(cu_die), callback); } } rangelist_entries get_rangelist_entries(const die_object& cu_die, int version) const { rangelist_entries vec; dwarf_ranges(cu_die, version, [&vec] (Dwarf_Addr low, Dwarf_Addr high) { // Simple coalescing optimization: // Sometimes the range list entries are really continuous: [100, 200), [200, 300) // Other times there's just one byte of separation [300, 399), [400, 500) // Those are the main two cases I've observed. // This will not catch all cases, presumably, as the range lists aren't sorted. But compilers/linkers // seem to like to emit the ranges in sorted order. if(!vec.empty() && low - vec.back().second <= 1) { vec.back().second = high; } else { vec.push_back({low, high}); } return true; }); return vec; } Dwarf_Bool pc_in_die(const die_object& cu_die, int version, Dwarf_Addr pc) const { bool found = false; dwarf_ranges(cu_die, version, [&found, pc] (Dwarf_Addr low, Dwarf_Addr high) { if(pc >= low && pc < high) { found = true; return false; } return true; }); return found; } void print() const { std::fprintf( stderr, "%08llx %s %s\n", to_ull(get_global_offset()), get_tag_name(), get_name().c_str() ); } }; // walk die list, callback is called on each die and should return true to // continue traversal // returns true if traversal should continue inline bool walk_die_list( const die_object& die, const std::function& fn ) { // TODO: Refactor so there is only one fn call bool continue_traversal = true; if(fn(die)) { die_object current = die.get_sibling(); while(current) { if(fn(current)) { current = current.get_sibling(); } else { continue_traversal = false; break; } } } return continue_traversal; } // walk die list, recursing into children, callback is called on each die // and should return true to continue traversal // returns true if traversal should continue inline bool walk_die_list_recursive( const die_object& die, const std::function& fn ) { return walk_die_list( die, [&fn](const die_object& die) { auto child = die.get_child(); if(child) { if(!walk_die_list_recursive(child, fn)) { return false; } } return fn(die); } ); } class maybe_owned_die_object { // Hacky... I wish std::variant existed. optional owned_die; optional ref_die; maybe_owned_die_object(die_object&& die) : owned_die(std::move(die)) {} maybe_owned_die_object(const die_object& die) : ref_die(&die) {} public: static maybe_owned_die_object owned(die_object&& die) { return maybe_owned_die_object{std::move(die)}; } static maybe_owned_die_object ref(const die_object& die) { return maybe_owned_die_object{die}; } const die_object& get() { ASSERT(owned_die || ref_die, "Mal-formed maybe_owned_die_object"); if(owned_die) { return owned_die.unwrap(); } else { return *ref_die.unwrap(); } } }; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/dwarf/dwarf_options.cpp000066400000000000000000000020421504061443700222630ustar00rootroot00000000000000#include "symbols/dwarf/dwarf_options.hpp" #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::atomic> dwarf_resolver_line_table_cache_size{nullable::null()}; std::atomic dwarf_resolver_disable_aranges{false}; optional get_dwarf_resolver_line_table_cache_size() { auto max_entries = dwarf_resolver_line_table_cache_size.load(); return max_entries.has_value() ? optional(max_entries.value()) : nullopt; } bool get_dwarf_resolver_disable_aranges() { return dwarf_resolver_disable_aranges.load(); } } CPPTRACE_END_NAMESPACE CPPTRACE_BEGIN_NAMESPACE namespace experimental { void set_dwarf_resolver_line_table_cache_size(nullable max_entries) { detail::dwarf_resolver_line_table_cache_size.store(max_entries); } void set_dwarf_resolver_disable_aranges(bool disable) { detail::dwarf_resolver_disable_aranges.store(disable); } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/symbols/dwarf/dwarf_options.hpp000066400000000000000000000004521504061443700222730ustar00rootroot00000000000000#ifndef DWARF_OPTIONS_HPP #define DWARF_OPTIONS_HPP #include "utils/optional.hpp" #include CPPTRACE_BEGIN_NAMESPACE namespace detail { optional get_dwarf_resolver_line_table_cache_size(); bool get_dwarf_resolver_disable_aranges(); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/dwarf/dwarf_resolver.cpp000066400000000000000000001343021504061443700224360ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF #include "symbols/dwarf/resolver.hpp" #include #include "symbols/dwarf/dwarf.hpp" // has dwarf #includes #include "symbols/dwarf/dwarf_utils.hpp" #include "symbols/dwarf/dwarf_options.hpp" #include "symbols/symbols.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/utils.hpp" #include "utils/lru_cache.hpp" #include "platform/path.hpp" #include "platform/program_name.hpp" // For CPPTRACE_MAX_PATH #include "logging.hpp" #if IS_APPLE #include "binary/mach-o.hpp" #endif #include #include #include #include #include #include #include #include #include #include // It's been tricky to piece together how to handle all this dwarf stuff. Some resources I've used are // https://www.prevanders.net/libdwarf.pdf // https://github.com/davea42/libdwarf-addr2line // https://github.com/ruby/ruby/blob/master/addr2line.c CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdwarf { // printbugging as we go constexpr bool dump_dwarf = false; constexpr bool trace_dwarf = false; class dwarf_resolver; // used to describe data from an upstream binary to a resolver for the .dwo struct skeleton_info { die_object cu_die; Dwarf_Half dwversion; dwarf_resolver& resolver; }; class dwarf_resolver : public symbol_resolver { std::string object_path; // dwarf_finish needs to be called after all other dwarf stuff is cleaned up, e.g. `srcfiles` and aranges etc // raii_wrapping ensures this is the last thing done after the destructor logic and all other data members are // cleaned up raii_wrapper dbg{nullptr, [](Dwarf_Debug dbg) { dwarf_finish(dbg); }}; bool ok = false; // .debug_aranges cache Dwarf_Arange* aranges = nullptr; Dwarf_Signed arange_count = 0; // Map from CU -> Line context lru_cache line_tables{get_dwarf_resolver_line_table_cache_size()}; // Map from CU -> Sorted subprograms vector using subprogram_map = range_map; std::unordered_map subprograms_cache; // Vector of ranges and their corresponding CU offsets struct compile_unit { die_object die; Dwarf_Half dwversion; }; range_map cu_cache; bool generated_cu_cache = false; // Map from CU -> {srcfiles, count} std::unordered_map srcfiles_cache; // Map from CU -> split full cu resolver std::unordered_map> split_full_cu_resolvers; // info for resolving a dwo object optional skeleton; private: // Error handling helper // For some reason R (*f)(Args..., void*)-style deduction isn't possible, seems like a bug in all compilers // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56190 template< typename... Args, typename... Args2, typename std::enable_if< std::is_same< decltype( (void)std::declval()(std::forward(std::declval())..., nullptr) ), void >::value, int >::type = 0 > int wrap(int (*f)(Args...), Args2&&... args) const { Dwarf_Error error = nullptr; int ret = f(std::forward(args)..., &error); if(ret == DW_DLV_ERROR) { handle_dwarf_error(dbg, error); } return ret; } public: CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING explicit dwarf_resolver(cstring_view object_path_, optional split_ = nullopt) : object_path(object_path_), skeleton(std::move(split_)) { // use a buffer when invoking dwarf_init_path, which allows it to automatically find debuglink or dSYM // sources bool use_buffer = true; // for universal / fat mach-o files unsigned universal_number = 0; #if IS_APPLE if(directory_exists(object_path + ".dSYM")) { // Possibly depends on the build system but a obj.cpp.o.dSYM/Contents/Resources/DWARF/obj.cpp.o can be // created alongside .o files. These are text files containing directives, as opposed to something we // can actually use std::string dsym_resource = object_path + ".dSYM/Contents/Resources/DWARF/" + basename(object_path); if(file_is_mach_o(dsym_resource)) { object_path = std::move(dsym_resource); } use_buffer = false; // we resolved dSYM above as appropriate } auto result = macho_is_fat(object_path); if(result.is_error()) { result.drop_error(); } else if(result.unwrap_value()) { auto mach_o_object = open_mach_o_cached(object_path); if(!mach_o_object) { ok = false; return; } universal_number = mach_o_object.unwrap_value()->get_fat_index(); } #endif // Giving libdwarf a buffer for a true output path is needed for its automatic resolution of debuglink and // dSYM files. We don't utilize the dSYM logic here, we just care about debuglink. std::unique_ptr buffer; if(use_buffer) { buffer = std::unique_ptr(new char[CPPTRACE_MAX_PATH]); } dwarf_set_de_alloc_flag(0); Dwarf_Error error = nullptr; auto ret = dwarf_init_path_a( object_path.c_str(), buffer.get(), CPPTRACE_MAX_PATH, DW_GROUPNUMBER_ANY, universal_number, nullptr, nullptr, &dbg.get(), &error ); if(ret == DW_DLV_OK) { ok = true; } else if(ret == DW_DLV_NO_ENTRY) { // fail, no debug info ok = false; } else if(ret == DW_DLV_ERROR) { // fail, parsing error ok = false; Dwarf_Unsigned ev = dwarf_errno(error); auto msg = raii_wrap( dwarf_errmsg(error), [this, error] (char*) { dwarf_dealloc_error(dbg.get(), error); } ); log::error("dwarf error: dwarf_init_path_a failed with error {} {}", ev, msg.get()); } else { ok = false; PANIC("Unknown return code from dwarf_init_path"); } if(skeleton) { VERIFY(wrap(dwarf_set_tied_dbg, dbg, skeleton.unwrap().resolver.dbg) == DW_DLV_OK); } if(ok && !get_dwarf_resolver_disable_aranges()) { // Check for .debug_aranges for fast lookup wrap(dwarf_get_aranges, dbg, &aranges, &arange_count); } } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING ~dwarf_resolver() override { if(aranges) { for(int i = 0; i < arange_count; i++) { dwarf_dealloc(dbg, aranges[i], DW_DLA_ARANGE); aranges[i] = nullptr; } dwarf_dealloc(dbg, aranges, DW_DLA_LIST); } } dwarf_resolver(const dwarf_resolver&) = delete; dwarf_resolver& operator=(const dwarf_resolver&) = delete; dwarf_resolver(dwarf_resolver&&) = delete; dwarf_resolver& operator=(dwarf_resolver&&) = delete; private: // walk all CU's in a dbg, callback is called on each die and should return true to // continue traversal void walk_compilation_units(const std::function& fn) { // libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull Dwarf_Unsigned next_cu_header; Dwarf_Half header_cu_type; while(true) { int ret = wrap( dwarf_next_cu_header_d, dbg, true, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &next_cu_header, &header_cu_type ); if(ret == DW_DLV_NO_ENTRY) { if(dump_dwarf) { std::fprintf(stderr, "End walk_dbg\n"); } return; } if(ret != DW_DLV_OK) { PANIC("Unexpected return code from dwarf_next_cu_header_d"); return; } // 0 passed as the die to the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d // to fetch the cu die die_object cu_die(dbg, nullptr); cu_die = cu_die.get_sibling(); if(!cu_die) { break; } if(!walk_die_list(cu_die, fn)) { break; } } if(dump_dwarf) { std::fprintf(stderr, "End walk_compilation_units\n"); } } void lazy_generate_cu_cache() { if(!generated_cu_cache) { walk_compilation_units([this] (const die_object& cu_die) { Dwarf_Half offset_size = 0; Dwarf_Half dwversion = 0; VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK); if(skeleton) { // NOTE: If we have a corresponding skeleton, we assume we have one CU matching the skeleton CU // Precedence for this assumption is https://dwarfstd.org/doc/DWARF5.pdf#subsection.3.1.3 // TODO: Also assuming same dwversion const auto& skeleton_cu = skeleton.unwrap().cu_die; auto ranges_vec = skeleton_cu.get_rangelist_entries(skeleton_cu, dwversion); if(!ranges_vec.empty()) { auto cu_die_handle = cu_cache.add_item({cu_die.clone(), dwversion}); for(auto range : ranges_vec) { cu_cache.insert(cu_die_handle, range.first, range.second); } } return false; } else { auto ranges_vec = cu_die.get_rangelist_entries(cu_die, dwversion); if(!ranges_vec.empty()) { auto cu_die_handle = cu_cache.add_item({cu_die.clone(), dwversion}); for(auto range : ranges_vec) { cu_cache.insert(cu_die_handle, range.first, range.second); } } return true; } }); cu_cache.finalize(); generated_cu_cache = true; } } std::string subprogram_symbol( const die_object& die, Dwarf_Half dwversion ) { ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine); optional name; if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { name = std::move(linkage_name); } else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) { name = std::move(linkage_name); } else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) { name = std::move(linkage_name); } if(name.has_value()) { return std::move(name).unwrap(); } else { if(die.has_attr(DW_AT_specification)) { die_object spec = die.resolve_reference_attribute(DW_AT_specification); return subprogram_symbol(spec, dwversion); } else if(die.has_attr(DW_AT_abstract_origin)) { die_object spec = die.resolve_reference_attribute(DW_AT_abstract_origin); return subprogram_symbol(spec, dwversion); } } return ""; } // despite (some) dwarf using 1-indexing, file_i should be the 0-based index std::string resolve_filename(const die_object& cu_die, Dwarf_Unsigned file_i) { // for split-dwarf line resolution happens in the skeleton if(skeleton) { return skeleton.unwrap().resolver.resolve_filename(skeleton.unwrap().cu_die, file_i); } std::string filename; if(get_cache_mode() == cache_mode::prioritize_memory) { char** dw_srcfiles; Dwarf_Signed dw_filecount; VERIFY(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount) == DW_DLV_OK); srcfiles srcfiles(cu_die.dbg, dw_srcfiles, dw_filecount); if(Dwarf_Signed(file_i) < dw_filecount) { // dwarf is using 1-indexing filename = srcfiles.get(file_i); } } else { auto off = cu_die.get_global_offset(); auto it = srcfiles_cache.find(off); if(it == srcfiles_cache.end()) { char** dw_srcfiles; Dwarf_Signed dw_filecount; VERIFY(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount) == DW_DLV_OK); it = srcfiles_cache.emplace_hint(it, off, srcfiles{cu_die.dbg, dw_srcfiles, dw_filecount}); } if(file_i < it->second.count()) { // dwarf is using 1-indexing filename = it->second.get(file_i); } } return filename; } void get_inlines_info( const die_object& cu_die, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, std::vector& inlines ) { ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine); // get_inlines_info is recursive and recurses into dies with pc ranges matching the pc we're looking for, // however, because I wouldn't want anything stack overflowing I'm breaking the recursion out into a loop // while looping when we find the target die we need to be able to store a die somewhere that doesn't die // at the end of the list traversal, we'll use this as a holder for it die_object current_obj_holder(dbg, nullptr); optional> current_die = die; while(current_die.has_value()) { auto child = current_die.unwrap().get().get_child(); if(!child) { break; } optional> target_die; walk_die_list( child, [this, &cu_die, pc, dwversion, &inlines, &target_die, ¤t_obj_holder] (const die_object& die) { if(die.get_tag() == DW_TAG_inlined_subroutine && die.pc_in_die(cu_die, dwversion, pc)) { const auto name = subprogram_symbol(die, dwversion); auto file_i = die.get_unsigned_attribute(DW_AT_call_file); // TODO: Refactor.... Probably put logic in resolve_filename. if(file_i) { // for dwarf 2, 3, 4, and experimental line table version 0xfe06 1-indexing is used // for dwarf 5 0-indexing is used optional line_table_opt; if(skeleton) { line_table_opt = skeleton.unwrap().resolver.get_line_table( skeleton.unwrap().cu_die ); } else { line_table_opt = get_line_table(cu_die); } if(line_table_opt) { auto& line_table = line_table_opt.unwrap(); if(line_table.version != 5) { if(file_i.unwrap() == 0) { file_i.reset(); // 0 means no name to be found } else { // decrement to 0-based index file_i.unwrap()--; } } } else { // silently continue } } std::string file = file_i ? resolve_filename(cu_die, file_i.unwrap()) : ""; const auto line = die.get_unsigned_attribute(DW_AT_call_line); const auto col = die.get_unsigned_attribute(DW_AT_call_column); inlines.push_back(stacktrace_frame{ 0, 0, // TODO: Could put an object address here... {static_cast(line.value_or(0))}, {static_cast(col.value_or(0))}, file, name, true }); current_obj_holder = die.clone(); target_die = current_obj_holder; return false; } else if(die.get_tag() == DW_TAG_lexical_block && die.pc_in_die(cu_die, dwversion, pc)) { current_obj_holder = die.clone(); target_die = current_obj_holder; return false; } else { return true; } } ); // recursing into the found target as-if by get_inlines_info(cu_die, die, pc, dwversion, inlines); current_die = target_die; } } std::string retrieve_symbol_for_subprogram( const die_object& cu_die, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, std::vector& inlines ) { ASSERT(die.get_tag() == DW_TAG_subprogram); const auto name = subprogram_symbol(die, dwversion); if(should_resolve_inlined_calls()) { get_inlines_info(cu_die, die, pc, dwversion, inlines); } return name; } // returns true if this call found the symbol CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING bool retrieve_symbol_walk( const die_object& cu_die, const die_object& die, Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame, std::vector& inlines ) { bool found = false; walk_die_list( die, [this, &cu_die, pc, dwversion, &frame, &inlines, &found] (const die_object& die) { if(dump_dwarf) { std::fprintf( stderr, "-------------> %08llx %s %s\n", to_ull(die.get_global_offset()), die.get_tag_name(), die.get_name().c_str() ); } if(!(die.get_tag() == DW_TAG_namespace || die.pc_in_die(cu_die, dwversion, pc))) { if(dump_dwarf) { std::fprintf(stderr, "pc not in die\n"); } } else { if(trace_dwarf) { std::fprintf( stderr, "%s %08llx %s\n", die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die", to_ull(die.get_global_offset()), die.get_tag_name() ); } if(die.get_tag() == DW_TAG_subprogram) { frame.symbol = retrieve_symbol_for_subprogram(cu_die, die, pc, dwversion, inlines); found = true; return false; } auto child = die.get_child(); if(child) { if(retrieve_symbol_walk(cu_die, child, pc, dwversion, frame, inlines)) { found = true; return false; } } else { if(dump_dwarf) { std::fprintf(stderr, "(no child)\n"); } } } return true; } ); if(dump_dwarf) { std::fprintf(stderr, "End walk_die_list\n"); } return found; } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void preprocess_subprograms( const die_object& cu_die, const die_object& die, Dwarf_Half dwversion, subprogram_map& subprogram_cache ) { walk_die_list( die, [this, &cu_die, dwversion, &subprogram_cache] (const die_object& die) { switch(die.get_tag()) { case DW_TAG_subprogram: { auto ranges_vec = die.get_rangelist_entries(cu_die, dwversion); // TODO: Feels super inefficient and some day should maybe use an interval tree. if(!ranges_vec.empty()) { auto die_handle = subprogram_cache.add_item(die.clone()); for(auto range : ranges_vec) { subprogram_cache.insert(die_handle, range.first, range.second); } } // Walk children to get things like lambdas // TODO: Somehow find a way to get better names here? For gcc it's just "operator()" // On clang it's better auto child = die.get_child(); if(child) { preprocess_subprograms(cu_die, child, dwversion, subprogram_cache); } } break; case DW_TAG_namespace: case DW_TAG_structure_type: case DW_TAG_class_type: case DW_TAG_module: case DW_TAG_imported_module: case DW_TAG_compile_unit: { auto child = die.get_child(); if(child) { preprocess_subprograms(cu_die, child, dwversion, subprogram_cache); } } break; default: break; } return true; } ); if(dump_dwarf) { std::fprintf(stderr, "End walk_die_list\n"); } } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void retrieve_symbol( const die_object& cu_die, Dwarf_Addr pc, Dwarf_Half dwversion, stacktrace_frame& frame, std::vector& inlines ) { if(get_cache_mode() == cache_mode::prioritize_memory) { retrieve_symbol_walk(cu_die, cu_die, pc, dwversion, frame, inlines); } else { auto off = cu_die.get_global_offset(); auto it = subprograms_cache.find(off); if(it == subprograms_cache.end()) { // TODO: Refactor. Do the sort in the preprocess function and return the vec directly. subprogram_map subprogram_cache; preprocess_subprograms(cu_die, cu_die, dwversion, subprogram_cache); subprogram_cache.finalize(); subprograms_cache.emplace(off, std::move(subprogram_cache)); it = subprograms_cache.find(off); } const auto& subprogram_cache = it->second; auto maybe_die = subprogram_cache.lookup(pc); // If the vector has been empty this can happen if(maybe_die.has_value() && maybe_die.unwrap().pc_in_die(cu_die, dwversion, pc)) { frame.symbol = retrieve_symbol_for_subprogram(cu_die, maybe_die.unwrap(), pc, dwversion, inlines); } } } // returns a reference to a CU's line table, may be invalidated if the line_tables map is modified CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING optional get_line_table(const die_object& cu_die) { auto off = cu_die.get_global_offset(); auto res = line_tables.maybe_get(off); if(res) { return res; } else { Dwarf_Unsigned version; Dwarf_Small table_count; Dwarf_Line_Context line_context; int ret = wrap( dwarf_srclines_b, cu_die.get(), &version, &table_count, &line_context ); static_assert(std::is_unsigned::value, "Expected unsigned Dwarf_Small"); VERIFY(/*table_count >= 0 &&*/ table_count <= 2, "Unknown dwarf line table count"); if(ret == DW_DLV_NO_ENTRY) { // TODO: Failing silently for now return nullopt; } VERIFY(ret == DW_DLV_OK); std::vector line_entries; if(get_cache_mode() == cache_mode::prioritize_speed) { // build lookup table Dwarf_Line* line_buffer = nullptr; Dwarf_Signed line_count = 0; Dwarf_Line* linebuf_actuals = nullptr; Dwarf_Signed linecount_actuals = 0; VERIFY( wrap( dwarf_srclines_two_level_from_linecontext, line_context, &line_buffer, &line_count, &linebuf_actuals, &linecount_actuals ) == DW_DLV_OK ); // TODO: Make any attempt to note PC ranges? Handle line end sequence? line_entries.reserve(line_count); for(int i = 0; i < line_count; i++) { Dwarf_Line line = line_buffer[i]; Dwarf_Addr low_addr = 0; VERIFY(wrap(dwarf_lineaddr, line, &low_addr) == DW_DLV_OK); // scan ahead for the last line entry matching this pc int j; for(j = i + 1; j < line_count; j++) { Dwarf_Addr addr = 0; VERIFY(wrap(dwarf_lineaddr, line_buffer[j], &addr) == DW_DLV_OK); if(addr != low_addr) { break; } } line = line_buffer[j - 1]; line_entries.push_back({ low_addr, line }); i = j - 1; } // sort lines std::sort(line_entries.begin(), line_entries.end(), [] (const line_entry& a, const line_entry& b) { return a.low < b.low; }); } return line_tables.insert(off, line_table_info{version, line_context, std::move(line_entries)}); } } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void retrieve_line_info( const die_object& cu_die, Dwarf_Addr pc, stacktrace_frame& frame ) { // For debug fission the skeleton debug info will have the line table if(skeleton) { return skeleton.unwrap().resolver.retrieve_line_info(skeleton.unwrap().cu_die, pc, frame); } auto table_info_opt = get_line_table(cu_die); if(!table_info_opt) { return; // failing silently for now } auto& table_info = table_info_opt.unwrap(); if(get_cache_mode() == cache_mode::prioritize_speed) { // Lookup in the table auto& line_entries = table_info.line_entries; auto table_it = first_less_than_or_equal( line_entries.begin(), line_entries.end(), pc, [] (Dwarf_Addr pc, const line_entry& entry) { return pc < entry.low; } ); // If the vector has been empty this can happen if(table_it != line_entries.end()) { Dwarf_Line line = table_it->line; // line number if(!table_it->line_number) { Dwarf_Unsigned line_number = 0; VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK); table_it->line_number = static_cast(line_number); } frame.line = table_it->line_number.unwrap(); // column number if(!table_it->column_number) { Dwarf_Unsigned column_number = 0; VERIFY(wrap(dwarf_lineoff_b, line, &column_number) == DW_DLV_OK); table_it->column_number = static_cast(column_number); } frame.column = table_it->column_number.unwrap(); // filename if(!table_it->path) { char* filename = nullptr; VERIFY(wrap(dwarf_linesrc, line, &filename) == DW_DLV_OK); auto wrapper = raii_wrap( filename, [this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); } ); table_it->path = filename; } frame.filename = table_it->path.unwrap(); } } else { Dwarf_Line_Context line_context = table_info.line_context; // walk for it Dwarf_Line* line_buffer = nullptr; Dwarf_Signed line_count = 0; Dwarf_Line* linebuf_actuals = nullptr; Dwarf_Signed linecount_actuals = 0; VERIFY( wrap( dwarf_srclines_two_level_from_linecontext, line_context, &line_buffer, &line_count, &linebuf_actuals, &linecount_actuals ) == DW_DLV_OK ); Dwarf_Addr last_lineaddr = 0; Dwarf_Line last_line = nullptr; for(int i = 0; i < line_count; i++) { Dwarf_Line line = line_buffer[i]; Dwarf_Addr lineaddr = 0; VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); Dwarf_Line found_line = nullptr; if(pc == lineaddr) { // Multiple PCs may correspond to a line, find the last one found_line = line; for(int j = i + 1; j < line_count; j++) { Dwarf_Line line = line_buffer[j]; Dwarf_Addr lineaddr = 0; VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK); if(pc == lineaddr) { found_line = line; } } } else if(last_line && pc > last_lineaddr && pc < lineaddr) { // Guess that the last line had it found_line = last_line; } if(found_line) { Dwarf_Unsigned line_number = 0; VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK); frame.line = static_cast(line_number); char* filename = nullptr; VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK); auto wrapper = raii_wrap( filename, [this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); } ); frame.filename = filename; } else { Dwarf_Bool is_line_end; VERIFY(wrap(dwarf_lineendsequence, line, &is_line_end) == DW_DLV_OK); if(is_line_end) { last_lineaddr = 0; last_line = nullptr; } else { last_lineaddr = lineaddr; last_line = line; } } } } } struct cu_info { maybe_owned_die_object cu_die; Dwarf_Half dwversion; }; // CU resolution has three paths: // - If aranges are present, the pc is looked up in aranges (falls through to next cases if not in aranges) // - If cache mode is prioritize memory, the CUs are walked for a match // - Otherwise a CU cache is built up and CUs are looked up in the map CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING optional lookup_cu(Dwarf_Addr pc) { // Check for .debug_aranges for fast lookup if(aranges && !skeleton) { // don't bother under split dwarf // Try to find pc in aranges Dwarf_Arange arange; if(wrap(dwarf_get_arange, aranges, arange_count, pc, &arange) == DW_DLV_OK) { // Address in table, load CU die Dwarf_Off cu_die_offset; VERIFY(wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset) == DW_DLV_OK); Dwarf_Die raw_die; // Setting is_info = true for now, assuming in .debug_info rather than .debug_types VERIFY(wrap(dwarf_offdie_b, dbg, cu_die_offset, true, &raw_die) == DW_DLV_OK); die_object cu_die(dbg, raw_die); Dwarf_Half offset_size = 0; Dwarf_Half dwversion = 0; VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK); if(trace_dwarf) { std::fprintf(stderr, "Found CU in aranges\n"); cu_die.print(); } return cu_info{maybe_owned_die_object::owned(std::move(cu_die)), dwversion}; } } // otherwise, or if not in aranges // one reason to fallback here is if the compilation has dwarf generated from different compilers and only // some of them generate aranges (e.g. static linking with cpptrace after specifying clang++ as the c++ // compiler while the C compiler defaults to an older gcc) if(get_cache_mode() == cache_mode::prioritize_memory) { // walk for the cu and go from there optional info; walk_compilation_units([this, pc, &info] (const die_object& cu_die) { Dwarf_Half offset_size = 0; Dwarf_Half dwversion = 0; dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); //auto p = cu_die.get_pc_range(dwversion); //cu_die.print(); //fprintf(stderr, " %llx, %llx\n", p.first, p.second); if(trace_dwarf) { std::fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str()); } // NOTE: If we have a corresponding skeleton, we assume we have one CU matching the skeleton CU if( ( skeleton && skeleton.unwrap().cu_die.pc_in_die( skeleton.unwrap().cu_die, skeleton.unwrap().dwversion, pc ) ) || cu_die.pc_in_die(cu_die, dwversion, pc) ) { if(trace_dwarf) { std::fprintf( stderr, "pc in die %08llx %s (now searching for %08llx)\n", to_ull(cu_die.get_global_offset()), cu_die.get_tag_name(), to_ull(pc) ); } info = cu_info{maybe_owned_die_object::owned(cu_die.clone()), dwversion}; return false; } return true; }); return info; } else { lazy_generate_cu_cache(); // look up the cu auto res = cu_cache.lookup(pc); // res can be nullopt if the cu_cache vector is empty // It can also happen for something like _start, where there is a cached CU for the object but // _start is outside of the CU's PC range if(res) { const auto& die = res.unwrap().die; const auto dwversion = res.unwrap().dwversion; // TODO: Cache the range list? // NOTE: If we have a corresponding skeleton, we assume we have one CU matching the skeleton CU if( ( skeleton && skeleton.unwrap().cu_die.pc_in_die( skeleton.unwrap().cu_die, skeleton.unwrap().dwversion, pc ) ) || die.pc_in_die(die, dwversion, pc) ) { return cu_info{maybe_owned_die_object::ref(die), dwversion}; } } return nullopt; } } optional get_dwo_name(const die_object& cu_die) { if(auto dwo_name = cu_die.get_string_attribute(DW_AT_GNU_dwo_name)) { return dwo_name; } else if(auto dwo_name = cu_die.get_string_attribute(DW_AT_dwo_name)) { return dwo_name; } else { return nullopt; } } void perform_dwarf_fission_resolution( const die_object& cu_die, const optional& dwo_name, const object_frame& object_frame_info, stacktrace_frame& frame, std::vector& inlines ) { // Split dwarf / debug fission / dwo is handled here // Location of the split full CU is a combination of DW_AT_dwo_name/DW_AT_GNU_dwo_name and DW_AT_comp_dir // https://gcc.gnu.org/wiki/DebugFission if(dwo_name) { // TODO: DWO ID? auto comp_dir = cu_die.get_string_attribute(DW_AT_comp_dir); Dwarf_Half offset_size = 0; Dwarf_Half dwversion = 0; dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); std::string path; if(is_absolute(dwo_name.unwrap())) { path = dwo_name.unwrap(); } else if(comp_dir) { path = comp_dir.unwrap() + PATH_SEP + dwo_name.unwrap(); } else { // maybe default to dwo_name but for now not doing anything return; } // todo: slight inefficiency in this copy-back strategy due to other frame members frame_with_inlines res; if(get_cache_mode() == cache_mode::prioritize_memory) { dwarf_resolver resolver( path, skeleton_info{cu_die.clone(), dwversion, *this} ); res = resolver.resolve_frame(object_frame_info); } else { auto off = cu_die.get_global_offset(); auto it = split_full_cu_resolvers.find(off); if(it == split_full_cu_resolvers.end()) { it = split_full_cu_resolvers.emplace( off, detail::make_unique(path, skeleton_info{cu_die.clone(), dwversion, *this}) ).first; } res = it->second->resolve_frame(object_frame_info); } frame = std::move(res.frame); inlines = std::move(res.inlines); } } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void resolve_frame_core( const object_frame& object_frame_info, stacktrace_frame& frame, std::vector& inlines ) { auto pc = object_frame_info.object_address; if(dump_dwarf) { std::fprintf(stderr, "%s\n", object_path.c_str()); std::fprintf(stderr, "%llx\n", to_ull(pc)); } optional cu = lookup_cu(pc); if(cu) { const auto& cu_die = cu.unwrap().cu_die.get(); // gnu non-standard debug-fission may create non-skeleton CU DIEs and just add dwo attributes // clang emits dwo names in the split CUs, so guard against going down the dwarf fission path (which // doesn't infinitely recurse because it's not emitted as an absolute path and there's no comp dir but // it's good to guard against the infinite recursion anyway) auto dwo_name = get_dwo_name(cu_die); if(cu_die.get_tag() == DW_TAG_skeleton_unit || (dwo_name && !skeleton)) { perform_dwarf_fission_resolution(cu_die, dwo_name, object_frame_info, frame, inlines); } else { retrieve_line_info(cu_die, pc, frame); retrieve_symbol(cu_die, pc, cu.unwrap().dwversion, frame, inlines); } } } public: CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING frame_with_inlines resolve_frame(const object_frame& frame_info) override { if(!ok) { return { { frame_info.raw_address, frame_info.object_address, nullable::null(), nullable::null(), frame_info.object_path, "", false }, {} }; } stacktrace_frame frame = null_frame(); frame.filename = frame_info.object_path; frame.raw_address = frame_info.raw_address; frame.object_address = frame_info.object_address; if(trace_dwarf) { std::fprintf( stderr, "Starting resolution for %s %08llx\n", object_path.c_str(), to_ull(frame_info.object_address) ); } std::vector inlines; resolve_frame_core( frame_info, frame, inlines ); return {std::move(frame), std::move(inlines)}; } }; std::unique_ptr make_dwarf_resolver(cstring_view object_path) { return detail::make_unique(object_path); } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/dwarf/dwarf_utils.hpp000066400000000000000000000123761504061443700217500ustar00rootroot00000000000000#ifndef DWARF_UTILS_HPP #define DWARF_UTILS_HPP #include #include "symbols/dwarf/dwarf.hpp" // has dwarf #includes #include "utils/error.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdwarf { class srcfiles { Dwarf_Debug dbg = nullptr; char** dw_srcfiles = nullptr; Dwarf_Unsigned dw_filecount = 0; public: srcfiles(Dwarf_Debug dbg, char** dw_srcfiles, Dwarf_Signed filecount) : dbg(dbg), dw_srcfiles(dw_srcfiles), dw_filecount(static_cast(filecount)) { if(filecount < 0) { throw internal_error("Unexpected dw_filecount {}", filecount); } } ~srcfiles() { release(); } void release() { if(dw_srcfiles) { for(unsigned i = 0; i < dw_filecount; i++) { dwarf_dealloc(dbg, dw_srcfiles[i], DW_DLA_STRING); dw_srcfiles[i] = nullptr; } dwarf_dealloc(dbg, dw_srcfiles, DW_DLA_LIST); dw_srcfiles = nullptr; } } srcfiles(const srcfiles&) = delete; srcfiles(srcfiles&& other) { *this = std::move(other); } srcfiles& operator=(const srcfiles&) = delete; srcfiles& operator=(srcfiles&& other) { release(); dbg = exchange(other.dbg, nullptr); dw_srcfiles = exchange(other.dw_srcfiles, nullptr); dw_filecount = exchange(other.dw_filecount, 0); return *this; } // note: dwarf uses 1-indexing const char* get(Dwarf_Unsigned file_i) const { if(file_i >= dw_filecount) { throw internal_error( "Error while accessing the srcfiles list, requested index {} is out of bounds (count = {})", file_i, dw_filecount ); } return dw_srcfiles[file_i]; } Dwarf_Unsigned count() const { return dw_filecount; } }; // container of items which are keyed by ranges template class range_map { public: struct handle { std::uint32_t index; }; private: struct PACKED range_entry { handle item; K low; K high; }; std::vector items; std::vector range_entries; public: handle add_item(V&& item) { items.push_back(std::move(item)); VERIFY(items.size() < std::numeric_limits::max()); return handle{static_cast(items.size() - 1)}; } void insert(handle handle, K low, K high) { range_entries.push_back({handle, low, high}); } void finalize() { std::sort(range_entries.begin(), range_entries.end(), [] (const range_entry& a, const range_entry& b) { return a.low < b.low; }); } std::size_t ranges_count() const { return range_entries.size(); } optional lookup(K key) const { auto vec_it = first_less_than_or_equal( range_entries.begin(), range_entries.end(), key, [] (K key, const range_entry& entry) { return key < entry.low; } ); if(vec_it == range_entries.end()) { return nullopt; } return items.at(vec_it->item.index); } }; struct line_entry { Dwarf_Addr low; // Dwarf_Addr high; // int i; Dwarf_Line line; optional path; optional line_number; optional column_number; line_entry(Dwarf_Addr low, Dwarf_Line line) : low(low), line(line) {} }; struct line_table_info { Dwarf_Unsigned version = 0; Dwarf_Line_Context line_context = nullptr; // sorted by low_addr // TODO: Make this optional at some point, it may not be generated if cache mode switches during program exec... std::vector line_entries; line_table_info( Dwarf_Unsigned version, Dwarf_Line_Context line_context, std::vector&& line_entries ) : version(version), line_context(line_context), line_entries(std::move(line_entries)) {} ~line_table_info() { release(); } void release() { dwarf_srclines_dealloc_b(line_context); line_context = nullptr; } line_table_info(const line_table_info&) = delete; line_table_info(line_table_info&& other) { *this = std::move(other); } line_table_info& operator=(const line_table_info&) = delete; line_table_info& operator=(line_table_info&& other) { release(); version = other.version; line_context = exchange(other.line_context, nullptr); line_entries = std::move(other.line_entries); return *this; } }; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/dwarf/resolver.hpp000066400000000000000000000030331504061443700212540ustar00rootroot00000000000000#ifndef SYMBOL_RESOLVER_HPP #define SYMBOL_RESOLVER_HPP #include #include "symbols/symbols.hpp" #include "platform/platform.hpp" #include "utils/string_view.hpp" #include #if false #define CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING CPPTRACE_FORCE_NO_INLINE #else #define CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdwarf { class symbol_resolver { public: virtual ~symbol_resolver() = default; CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING virtual frame_with_inlines resolve_frame(const object_frame& frame_info) = 0; }; class null_resolver : public symbol_resolver { public: explicit null_resolver() = default; null_resolver(cstring_view) {} CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING frame_with_inlines resolve_frame(const object_frame& frame_info) override { return { { frame_info.raw_address, frame_info.object_address, nullable::null(), nullable::null(), frame_info.object_path, "", false }, {} }; }; }; std::unique_ptr make_dwarf_resolver(cstring_view object_path); #if IS_APPLE std::unique_ptr make_debug_map_resolver(const std::string& object_path); #endif } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols.hpp000066400000000000000000000046671504061443700200160ustar00rootroot00000000000000#ifndef SYMBOLS_HPP #define SYMBOLS_HPP #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { using collated_vec = std::vector< std::pair, std::reference_wrapper> >; struct frame_with_inlines { stacktrace_frame frame; std::vector inlines; }; using collated_vec_with_inlines = std::vector< std::pair, std::reference_wrapper> >; // These two helpers create a map from a target object to a vector of frames to resolve std::unordered_map collate_frames( const std::vector& frames, std::vector& trace ); std::unordered_map collate_frames( const std::vector& frames, std::vector& trace ); #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE namespace libbacktrace { std::vector resolve_frames(const std::vector& frames); } #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF namespace libdwarf { std::vector resolve_frames(const std::vector& frames); } #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL namespace libdl { std::vector resolve_frames(const std::vector& frames); } #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE namespace addr2line { std::vector resolve_frames(const std::vector& frames); } #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP namespace dbghelp { std::vector resolve_frames(const std::vector& frames); } #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING namespace nothing { std::vector resolve_frames(const std::vector& frames); std::vector resolve_frames(const std::vector& frames); } #endif std::vector resolve_frames(const std::vector& frames); std::vector resolve_frames(const std::vector& frames); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols_core.cpp000066400000000000000000000135101504061443700210040ustar00rootroot00000000000000#include #include #include "cpptrace/forward.hpp" #include "symbols/symbols.hpp" #include #include #include "utils/error.hpp" #include "binary/object.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { template std::unordered_map collate_frames( const std::vector& frames, std::vector& trace ) { std::unordered_map entries; for(std::size_t i = 0; i < frames.size(); i++) { const auto& entry = frames[i]; // The path may be empty. This can happens if libdl fails to find the shared object for a frame, e.g. I've // observed this on macos when looking up the shared object containing `start`. // It can also happen for JIT frames. As such, we don't exclude them from the output. entries[entry.object_path].emplace_back( entry, trace[i] ); } return entries; } std::unordered_map collate_frames( const std::vector& frames, std::vector& trace ) { return collate_frames(frames, trace); } std::unordered_map collate_frames( const std::vector& frames, std::vector& trace ) { return collate_frames(frames, trace); } /* * * * All the code here is awful and I'm not proud of it. * * * */ // Resolver must not support walking inlines void fill_blanks( std::vector& vec, std::vector (*resolver)(const std::vector&) ) { std::vector addresses; for(const auto& frame : vec) { if(frame.symbol.empty() || frame.filename.empty()) { addresses.push_back(frame.raw_address); } } std::vector new_frames = resolver(addresses); std::size_t i = 0; for(auto& frame : vec) { if(frame.symbol.empty() || frame.filename.empty()) { // three cases to handle, either partially overwrite or fully overwrite if(frame.symbol.empty() && frame.filename.empty()) { frame = new_frames[i]; } else if(frame.symbol.empty() && !frame.filename.empty()) { frame.symbol = new_frames[i].symbol; } else { ASSERT(!frame.symbol.empty() && frame.filename.empty()); frame.filename = new_frames[i].filename; frame.line = new_frames[i].line; frame.column = new_frames[i].column; } i++; } } } // TODO: Symbol resolution code should probably handle when object addresses are 0 std::vector resolve_frames(const std::vector& frames) { #if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) std::vector trace = libdwarf::resolve_frames(frames); fill_blanks(trace, dbghelp::resolve_frames); return trace; #else #if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDL) \ || defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \ || defined(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) // actually need to go backwards to a void* std::vector raw_frames(frames.size()); for(std::size_t i = 0; i < frames.size(); i++) { raw_frames[i] = frames[i].raw_address; } #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL return libdl::resolve_frames(raw_frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF return libdwarf::resolve_frames(frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP return dbghelp::resolve_frames(raw_frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE return addr2line::resolve_frames(frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE return libbacktrace::resolve_frames(raw_frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING return nothing::resolve_frames(frames); #endif #endif } std::vector resolve_frames(const std::vector& frames) { #if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) \ || defined(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) auto dlframes = get_frames_object_info(frames); #endif #if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) std::vector trace = libdwarf::resolve_frames(dlframes); fill_blanks(trace, dbghelp::resolve_frames); return trace; #else #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL return libdl::resolve_frames(frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF return libdwarf::resolve_frames(dlframes); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP return dbghelp::resolve_frames(frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE return addr2line::resolve_frames(dlframes); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE return libbacktrace::resolve_frames(frames); #endif #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING return nothing::resolve_frames(frames); #endif #endif } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/symbols/symbols_with_addr2line.cpp000066400000000000000000000310461504061443700227570ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE #include #include "symbols/symbols.hpp" #include "utils/common.hpp" #include "utils/microfmt.hpp" #include "utils/utils.hpp" #include #include #include #include #include #include #include #include #if IS_LINUX || IS_APPLE #include #include #include #endif #include "binary/object.hpp" #include "options.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace addr2line { #if IS_LINUX || IS_APPLE bool has_addr2line() { static std::mutex mutex; static bool has_addr2line = false; static bool checked = false; std::lock_guard lock(mutex); if(!checked) { checked = true; // Detects if addr2line exists by trying to invoke addr2line --help constexpr int magic = 42; const pid_t pid = fork(); if(pid == -1) { return false; } if(pid == 0) { // child close(STDOUT_FILENO); close(STDERR_FILENO); // atos --help writes to stderr #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH #if !IS_APPLE execlp("addr2line", "addr2line", "--help", nullptr); #else execlp("atos", "atos", "--help", nullptr); #endif #else #ifndef CPPTRACE_ADDR2LINE_PATH #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #endif execl(CPPTRACE_ADDR2LINE_PATH, CPPTRACE_ADDR2LINE_PATH, "--help", nullptr); #endif _exit(magic); } int status; waitpid(pid, &status, 0); has_addr2line = WEXITSTATUS(status) == 0; } return has_addr2line; } struct pipe_ends { int read; int write; }; struct pipe_t { union { pipe_ends end; int data[2]; }; }; static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing"); std::string resolve_addresses(const std::string& addresses, const std::string& executable) { pipe_t output_pipe; pipe_t input_pipe; if(pipe(output_pipe.data) != 0 || pipe(input_pipe.data) != 0) { throw internal_error("call to pipe failed: {}", errno); } const pid_t pid = fork(); if(pid == -1) { return ""; } // error? TODO: Diagnostic if(pid == 0) { // child dup2(output_pipe.end.write, STDOUT_FILENO); dup2(input_pipe.end.read, STDIN_FILENO); close(output_pipe.end.read); close(output_pipe.end.write); close(input_pipe.end.read); close(input_pipe.end.write); close(STDERR_FILENO); // TODO: Might be worth conditionally enabling or piping #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH #if !IS_APPLE execlp("addr2line", "addr2line", "-e", executable.c_str(), "-f", "-C", "-p", nullptr); #else execlp("atos", "atos", "-o", executable.c_str(), "-fullPath", nullptr); #endif #else #ifndef CPPTRACE_ADDR2LINE_PATH #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #endif #if !IS_APPLE execl( CPPTRACE_ADDR2LINE_PATH, CPPTRACE_ADDR2LINE_PATH, "-e", executable.c_str(), "-f", "-C", "-p", nullptr ); #else execl( CPPTRACE_ADDR2LINE_PATH, CPPTRACE_ADDR2LINE_PATH, "-o", executable.c_str(), "-fullPath", nullptr ); #endif #endif _exit(1); // TODO: Diagnostic? } if(write(input_pipe.end.write, addresses.data(), addresses.size()) == -1) { throw internal_error("call to write failed: {}", errno); } close(input_pipe.end.read); close(input_pipe.end.write); close(output_pipe.end.write); std::string output; constexpr int buffer_size = 4096; char buffer[buffer_size]; std::size_t count = 0; while((count = read(output_pipe.end.read, buffer, buffer_size)) > 0) { output.insert(output.end(), buffer, buffer + count); } // TODO: check status from addr2line? waitpid(pid, nullptr, 0); return output; } #elif IS_WINDOWS bool has_addr2line() { static std::mutex mutex; static bool has_addr2line = false; static bool checked = false; std::lock_guard lock(mutex); if(!checked) { // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. checked = true; #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH std::FILE* p = popen("addr2line --version", "r"); #else #ifndef CPPTRACE_ADDR2LINE_PATH #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #endif std::FILE* p = popen(CPPTRACE_ADDR2LINE_PATH " --version", "r"); #endif if(p) { has_addr2line = pclose(p) == 0; } } return has_addr2line; } std::string resolve_addresses(const std::string& addresses, const std::string& executable) { // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. ///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str()); #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH std::FILE* p = popen(("addr2line -e \"" + executable + "\" -fCp " + addresses).c_str(), "r"); #else #ifndef CPPTRACE_ADDR2LINE_PATH #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #endif std::FILE* p = popen( (CPPTRACE_ADDR2LINE_PATH " -e \"" + executable + "\" -fCp " + addresses).c_str(), "r" ); #endif std::string output; constexpr int buffer_size = 4096; char buffer[buffer_size]; std::size_t count = 0; while((count = std::fread(buffer, 1, buffer_size, p)) > 0) { output.insert(output.end(), buffer, buffer + count); } pclose(p); ///fprintf(stderr, "%s\n", output.c_str()); return output; } #endif void update_trace(const std::string& line, std::size_t entry_index, const collated_vec& entries_vec) { #if !IS_APPLE // Result will be of the form " at path:line" // The path may be ?? if addr2line cannot resolve, line may be ? // Edge cases: // ?? ??:0 // symbol :? const std::size_t at_location = line.find(" at "); std::size_t symbol_end; std::size_t filename_start; if(at_location != std::string::npos) { symbol_end = at_location; filename_start = at_location + 4; } else { VERIFY(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output"); symbol_end = 2; filename_start = 3; } auto symbol = line.substr(0, symbol_end); auto colon = line.rfind(':'); VERIFY(colon != std::string::npos); VERIFY(colon >= filename_start); // :? to deal with "symbol :?" edge case auto filename = line.substr(filename_start, colon - filename_start); auto line_number = line.substr(colon + 1); if(line_number != "?") { entries_vec[entry_index].second.get().line = std::stoi(line_number); } if(!filename.empty() && filename != "??") { entries_vec[entry_index].second.get().filename = filename; } if(!symbol.empty()) { entries_vec[entry_index].second.get().symbol = symbol; } #else // Result will be of the form " (in ) (file:line)" // The symbol may just be the given address if atos can't resolve it // Examples: // trace() (in demo) (demo.cpp:8) // 0x100003b70 (in demo) // 0xffffffffffffffff // foo (in bar) + 14 // I'm making some assumptions here. Support may need to be improved later. This is tricky output to // parse. const std::size_t in_location = line.find(" (in "); if(in_location == std::string::npos) { // presumably the 0xffffffffffffffff case return; } const std::size_t symbol_end = in_location; entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end); const std::size_t object_end = line.find(")", in_location); VERIFY( object_end != std::string::npos, "Unexpected edge case while processing addr2line/atos output" ); const std::size_t filename_start = line.find(") (", object_end); if(filename_start == std::string::npos) { // presumably something like 0x100003b70 (in demo) or foo (in bar) + 14 return; } const std::size_t filename_end = line.find(":", filename_start); VERIFY( filename_end != std::string::npos, "Unexpected edge case while processing addr2line/atos output" ); entries_vec[entry_index].second.get().filename = line.substr( filename_start + 3, filename_end - filename_start - 3 ); const std::size_t line_start = filename_end + 1; const std::size_t line_end = line.find(")", filename_end); VERIFY( line_end == line.size() - 1, "Unexpected edge case while processing addr2line/atos output" ); entries_vec[entry_index].second.get().line = std::stoi(line.substr(line_start, line_end - line_start)); #endif } std::vector resolve_frames(const std::vector& frames) { // TODO: Refactor better std::vector trace(frames.size(), null_frame()); for(std::size_t i = 0; i < frames.size(); i++) { trace[i].raw_address = frames[i].raw_address; trace[i].object_address = frames[i].object_address; // Set what is known for now, and resolutions from addr2line should overwrite trace[i].filename = frames[i].object_path; } if(has_addr2line()) { const auto entries = collate_frames(frames, trace); for(const auto& entry : entries) { try { const auto& object_name = entry.first; if(object_name.empty()) { continue; } const auto& entries_vec = entry.second; // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're // no addresses why would get_addr2line_targets do anything? The reason is because if things in // get_addr2line_targets fail it will silently skip. This is partly an optimization but also an // assertion below will fail if addr2line is given an empty input. if(entries_vec.empty()) { continue; } std::string address_input; for(const auto& pair : entries_vec) { address_input += microfmt::format( "{:h}{}", pair.first.get().object_address, #if !IS_WINDOWS '\n' #else ' ' #endif ); } auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); VERIFY(output.size() == entries_vec.size()); for(std::size_t i = 0; i < output.size(); i++) { update_trace(output[i], i, entries_vec); } } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); } } } return trace; } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols_with_dbghelp.cpp000066400000000000000000000514751504061443700225300ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP #include #include #include "symbols/symbols.hpp" #include "platform/dbghelp_utils.hpp" #include "binary/object.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/utils.hpp" #include "options.hpp" #include "logging.hpp" #include #include #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace dbghelp { // SymFromAddr only returns the function's name. In order to get information about parameters, // important for C++ stack traces where functions may be overloaded, we have to manually use // Windows DIA to walk debug info structures. Resources: // https://web.archive.org/web/20201027025750/http://www.debuginfo.com/articles/dbghelptypeinfo.html // https://web.archive.org/web/20201203160805/http://www.debuginfo.com/articles/dbghelptypeinfofigures.html // https://github.com/DynamoRIO/dynamorio/blob/master/ext/drsyms/drsyms_windows.c#L1370-L1439 // TODO: Currently unable to detect rvalue references // TODO: Currently unable to detect const enum class SymTagEnum { SymTagNull, SymTagExe, SymTagCompiland, SymTagCompilandDetails, SymTagCompilandEnv, SymTagFunction, SymTagBlock, SymTagData, SymTagAnnotation, SymTagLabel, SymTagPublicSymbol, SymTagUDT, SymTagEnum, SymTagFunctionType, SymTagPointerType, SymTagArrayType, SymTagBaseType, SymTagTypedef, SymTagBaseClass, SymTagFriend, SymTagFunctionArgType, SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagUsingNamespace, SymTagVTableShape, SymTagVTable, SymTagCustom, SymTagThunk, SymTagCustomType, SymTagManagedType, SymTagDimension, SymTagCallSite, SymTagInlineSite, SymTagBaseInterface, SymTagVectorType, SymTagMatrixType, SymTagHLSLType, SymTagCaller, SymTagCallee, SymTagExport, SymTagHeapAllocationSite, SymTagCoffGroup, SymTagMax }; enum class IMAGEHLP_SYMBOL_TYPE_INFO { TI_GET_SYMTAG, TI_GET_SYMNAME, TI_GET_LENGTH, TI_GET_TYPE, TI_GET_TYPEID, TI_GET_BASETYPE, TI_GET_ARRAYINDEXTYPEID, TI_FINDCHILDREN, TI_GET_DATAKIND, TI_GET_ADDRESSOFFSET, TI_GET_OFFSET, TI_GET_VALUE, TI_GET_COUNT, TI_GET_CHILDRENCOUNT, TI_GET_BITPOSITION, TI_GET_VIRTUALBASECLASS, TI_GET_VIRTUALTABLESHAPEID, TI_GET_VIRTUALBASEPOINTEROFFSET, TI_GET_CLASSPARENTID, TI_GET_NESTED, TI_GET_SYMINDEX, TI_GET_LEXICALPARENT, TI_GET_ADDRESS, TI_GET_THISADJUST, TI_GET_UDTKIND, TI_IS_EQUIV_TO, TI_GET_CALLING_CONVENTION, TI_IS_CLOSE_EQUIV_TO, TI_GTIEX_REQS_VALID, TI_GET_VIRTUALBASEOFFSET, TI_GET_VIRTUALBASEDISPINDEX, TI_GET_IS_REFERENCE, TI_GET_INDIRECTVIRTUALBASECLASS, TI_GET_VIRTUALBASETABLETYPE, TI_GET_OBJECTPOINTERTYPE, IMAGEHLP_SYMBOL_TYPE_INFO_MAX }; enum class BasicType { btNoType = 0, btVoid = 1, btChar = 2, btWChar = 3, btInt = 6, btUInt = 7, btFloat = 8, btBCD = 9, btBool = 10, btLong = 13, btULong = 14, btCurrency = 25, btDate = 26, btVariant = 27, btComplex = 28, btBit = 29, btBSTR = 30, btHresult = 31 }; // SymGetTypeInfo utility template T get_info(ULONG type_index, HANDLE proc, ULONG64 modbase) { T info; if( !SymGetTypeInfo( proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), &info ) ) { if(FAILABLE) { return (T)-1; } else { throw internal_error( "SymGetTypeInfo failed: {}", std::system_error(GetLastError(), std::system_category()).what() ); } } return info; } template std::string get_info_wchar(ULONG type_index, HANDLE proc, ULONG64 modbase) { WCHAR* info; if( !SymGetTypeInfo(proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), &info) ) { throw internal_error( "SymGetTypeInfo failed: {}", std::system_error(GetLastError(), std::system_category()).what() ); } // special case to properly free a buffer and convert string to narrow chars, only used for // TI_GET_SYMNAME static_assert( SymType == IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_SYMNAME, "get_info_wchar called with unexpected IMAGEHLP_SYMBOL_TYPE_INFO" ); std::wstring wstr(info); std::string str; str.reserve(wstr.size()); for(const auto c : wstr) { str.push_back(static_cast(c)); } LocalFree(info); return str; } // Translate basic types to string static std::string get_basic_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { auto basic_type = get_info( type_index, proc, modbase ); //auto length = get_info(type_index, proc, modbase); switch(basic_type) { case BasicType::btNoType: return ""; case BasicType::btVoid: return "void"; case BasicType::btChar: return "char"; case BasicType::btWChar: return "wchar_t"; case BasicType::btInt: return "int"; case BasicType::btUInt: return "unsigned int"; case BasicType::btFloat: return "float"; case BasicType::btBool: return "bool"; case BasicType::btLong: return "long"; case BasicType::btULong: return "unsigned long"; default: return ""; } } static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase); struct class_name_result { bool has_class_name; std::string name; }; // Helper for member pointers static class_name_result lookup_class_name(ULONG type_index, HANDLE proc, ULONG64 modbase) { DWORD class_parent_id = get_info( type_index, proc, modbase ); if(class_parent_id == (DWORD)-1) { return {false, ""}; } else { return {true, resolve_type(class_parent_id, proc, modbase)}; } } struct type_result { std::string base; std::string extent; }; // Resolve more complex types // returns [base, extent] static type_result lookup_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { auto tag = get_info(type_index, proc, modbase); switch(tag) { case SymTagEnum::SymTagBaseType: return {get_basic_type(type_index, proc, modbase), ""}; case SymTagEnum::SymTagPointerType: { DWORD underlying_type_id = get_info( type_index, proc, modbase ); bool is_ref = get_info( type_index, proc, modbase ); std::string pp = is_ref ? "&" : "*"; // pointer punctuator auto class_name_res = lookup_class_name(type_index, proc, modbase); if(class_name_res.has_class_name) { pp = class_name_res.name + "::" + pp; } const auto type = lookup_type(underlying_type_id, proc, modbase); if(type.extent.empty()) { return {type.base + (pp.size() > 1 ? " " : "") + pp, ""}; } else { return {type.base + "(" + pp, ")" + type.extent}; } } case SymTagEnum::SymTagArrayType: { DWORD underlying_type_id = get_info( type_index, proc, modbase ); DWORD length = get_info( type_index, proc, modbase ); const auto type = lookup_type(underlying_type_id, proc, modbase); return {type.base, "[" + std::to_string(length) + "]" + type.extent}; } case SymTagEnum::SymTagFunctionType: { DWORD return_type_id = get_info( type_index, proc, modbase ); DWORD n_children = get_info( type_index, proc, modbase ); DWORD class_parent_id = get_info( type_index, proc, modbase ); int n_ignore = class_parent_id != (DWORD)-1; // ignore this param // this must be ignored before TI_FINDCHILDREN_PARAMS::Count is set, else error n_children -= n_ignore; // return type const auto return_type = lookup_type(return_type_id, proc, modbase); if(n_children == 0) { return {return_type.base, "()" + return_type.extent}; } else { // alignment should be fine std::size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) + (n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]); TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz]; auto guard = scope_exit([&] { delete[] (char*) children; }); children->Start = 0; children->Count = n_children; if( !SymGetTypeInfo( proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>( IMAGEHLP_SYMBOL_TYPE_INFO::TI_FINDCHILDREN ), children ) ) { throw internal_error( "SymGetTypeInfo failed: {}", std::system_error(GetLastError(), std::system_category()).what() ); } // get children type std::string extent = "("; if(children->Start != 0) { throw internal_error("Error: children->Start == 0"); } for(std::size_t i = 0; i < n_children; i++) { extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase); } extent += ")"; return {return_type.base, extent + return_type.extent}; } } case SymTagEnum::SymTagFunctionArgType: { DWORD underlying_type_id = get_info(type_index, proc, modbase); return {resolve_type(underlying_type_id, proc, modbase), ""}; } case SymTagEnum::SymTagTypedef: case SymTagEnum::SymTagEnum: case SymTagEnum::SymTagUDT: case SymTagEnum::SymTagBaseClass: return { get_info_wchar(type_index, proc, modbase), "" }; default: return { "::type>(tag)) + ">", "" }; }; } static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase) { const auto type = lookup_type(type_index, proc, modbase); return type.base + type.extent; } struct function_info { HANDLE proc; ULONG64 modbase; int counter; int n_children; int n_ignore; std::string str; }; // Enumerates function parameters static BOOL __stdcall enumerator_callback( PSYMBOL_INFO symbol_info, ULONG, PVOID data ) { function_info* ctx = (function_info*)data; if(ctx->counter++ >= ctx->n_children) { return false; } if(ctx->n_ignore-- > 0) { return true; // just skip } ctx->str += resolve_type(symbol_info->TypeIndex, ctx->proc, ctx->modbase); if(ctx->counter < ctx->n_children) { ctx->str += ", "; } return true; } // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions stacktrace_frame resolve_frame(HANDLE proc, frame_ptr addr) { // The get_frame_object_info() ends up being inexpensive, at on my machine // debug release // uncached trace resolution (29 frames) 1.9-2.1 ms 1.4-1.8 ms // cached trace resolution (29 frames) 1.1-1.2 ms 0.2-0.4 ms // get_frame_object_info() 0.001-0.002 ms 0.0003-0.0006 ms // At some point it might make sense to make an option to control this. auto object_frame = get_frame_object_info(addr); // Dbghelp is is single-threaded, so acquire a lock. auto lock = get_dbghelp_lock(); alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; union { DWORD64 a; DWORD b; } displacement; IMAGEHLP_LINE line; bool got_line = SymGetLineFromAddr(proc, addr, &displacement.b, &line); if(SymFromAddr(proc, addr, &displacement.a, symbol)) { if(got_line) { IMAGEHLP_STACK_FRAME frame; frame.InstructionOffset = symbol->Address; // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext // "If you call SymSetContext to set the context to its current value, the // function fails but GetLastError returns ERROR_SUCCESS." // This is the stupidest fucking api I've ever worked with. if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) { log::error("Stack trace: Internal error while calling SymSetContext"); return { addr, object_frame.object_address, { static_cast(line.LineNumber) }, nullable::null(), line.FileName, symbol->Name, false }; } DWORD n_children = get_info( symbol->TypeIndex, proc, symbol->ModBase ); DWORD class_parent_id = get_info( symbol->TypeIndex, proc, symbol->ModBase ); function_info fi { proc, symbol->ModBase, 0, int(n_children), class_parent_id != (DWORD)-1, "" }; SymEnumSymbols(proc, 0, nullptr, enumerator_callback, &fi); std::string signature = symbol->Name + std::string("(") + fi.str + ")"; // There's a phenomina with DIA not inserting commas after template parameters. Fix them here. static std::regex comma_re(R"(,(?=\S))"); signature = std::regex_replace(signature, comma_re, ", "); return { addr, object_frame.object_address, { static_cast(line.LineNumber) }, nullable::null(), line.FileName, signature, false, }; } else { return { addr, object_frame.object_address, nullable::null(), nullable::null(), "", symbol->Name, false }; } } else { return { addr, object_frame.object_address, nullable::null(), nullable::null(), "", "", false }; } } std::vector resolve_frames(const std::vector& frames) { // Dbghelp is is single-threaded, so acquire a lock. auto lock = get_dbghelp_lock(); std::vector trace; trace.reserve(frames.size()); // TODO: When does this need to be called? Can it be moved to the symbolizer? SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); auto syminit_info = ensure_syminit(); for(const auto frame : frames) { try { trace.push_back(resolve_frame(syminit_info.get_process_handle() , frame)); } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); auto entry = null_frame(); entry.raw_address = frame; trace.push_back(entry); } } return trace; } } } CPPTRACE_END_NAMESPACE CPPTRACE_BEGIN_NAMESPACE /* When a module was loaded at runtime with LoadLibrary after SymInitialize was already called, it is necessary to manually load the symbols from that module with SymLoadModuleEx. See "Symbol Handler Initialization" in Microsoft documentation at https://learn.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization */ void load_symbols_for_file(const std::string& filename) { HMODULE hModule = GetModuleHandleA(filename.c_str()); if (hModule == NULL) { throw detail::internal_error( "Unable to get module handle for file '{}' : {}", filename, std::system_error(GetLastError(), std::system_category()).what() ); } // SymLoadModuleEx needs the module's base address and size, so get these with GetModuleInformation. MODULEINFO module_info; if ( !GetModuleInformation( GetCurrentProcess(), hModule, &module_info, sizeof(module_info) ) ) { throw detail::internal_error( "Unable to get module information for file '{}' : {}", filename, std::system_error(GetLastError(), std::system_category()).what() ); } auto lock = detail::get_dbghelp_lock(); HANDLE syminit_handle = detail::ensure_syminit().get_process_handle(); if ( !SymLoadModuleEx( syminit_handle, NULL, filename.c_str(), NULL, (DWORD64)module_info.lpBaseOfDll, // The documentation says this is optional, but if omitted (0), symbol loading fails module_info.SizeOfImage, NULL, 0 ) ) { throw detail::internal_error( "Unable to load symbols for file '{}' : {}", filename, std::system_error(GetLastError(), std::system_category()).what() ); } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols_with_dl.cpp000066400000000000000000000030101504061443700215000ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL #include #include "symbols/symbols.hpp" #include "binary/module_base.hpp" #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdl { stacktrace_frame resolve_frame(const frame_ptr addr) { Dl_info info; if(dladdr(reinterpret_cast(addr), &info)) { // thread-safe auto base = get_module_image_base(info.dli_fname); return { addr, base.has_value() ? addr - reinterpret_cast(info.dli_fbase) + base.unwrap_value() : 0, nullable::null(), nullable::null(), info.dli_fname ? info.dli_fname : "", info.dli_sname ? info.dli_sname : "", false }; } else { return { addr, 0, nullable::null(), nullable::null(), "", "", false }; } } std::vector resolve_frames(const std::vector& frames) { std::vector trace; trace.reserve(frames.size()); for(const auto frame : frames) { trace.push_back(resolve_frame(frame)); } return trace; } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols_with_libbacktrace.cpp000066400000000000000000000066451504061443700235300ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE #include #include "symbols/symbols.hpp" #include "platform/program_name.hpp" #include "utils/error.hpp" #include "utils/common.hpp" #include "options.hpp" #include #include #include #include #include #include #ifdef CPPTRACE_BACKTRACE_PATH #include CPPTRACE_BACKTRACE_PATH #else #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libbacktrace { int full_callback(void* data, std::uintptr_t address, const char* file, int line, const char* symbol) { stacktrace_frame& frame = *static_cast(data); frame.raw_address = address; frame.line = line; frame.filename = file ? file : ""; frame.symbol = symbol ? symbol : ""; return 0; } void syminfo_callback(void* data, std::uintptr_t address, const char* symbol, std::uintptr_t, std::uintptr_t) { stacktrace_frame& frame = *static_cast(data); frame.raw_address = address; frame.line = 0; frame.filename = ""; frame.symbol = symbol ? symbol : ""; } void error_callback(void*, const char* msg, int errnum) { if(msg == std::string("no debug info in ELF executable")) { // https://github.com/jeremy-rifkin/cpptrace/issues/114 // https://github.com/ianlancetaylor/libbacktrace/blob/ae1e707dbacd4a5cc82fcf2d3816f410e9c5fec4/elf.c#L592 // not a critical error, just return return; } throw internal_error("Libbacktrace error: {}, code {}", msg, errnum); } backtrace_state* get_backtrace_state() { static std::mutex mutex; const std::lock_guard lock(mutex); // backtrace_create_state must be called only one time per program static backtrace_state* state = nullptr; static bool called = false; if(!called) { state = backtrace_create_state(program_name(), true, error_callback, nullptr); called = true; } return state; } // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions stacktrace_frame resolve_frame(const frame_ptr addr) { try { stacktrace_frame frame = null_frame(); frame.raw_address = addr; backtrace_pcinfo( get_backtrace_state(), addr, full_callback, error_callback, &frame ); if(frame.symbol.empty()) { // fallback, try to at least recover the symbol name with backtrace_syminfo backtrace_syminfo( get_backtrace_state(), addr, syminfo_callback, error_callback, &frame ); } return frame; } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); return null_frame(); } } std::vector resolve_frames(const std::vector& frames) { std::vector trace; trace.reserve(frames.size()); for(const auto frame : frames) { trace.push_back(resolve_frame(frame)); } return trace; } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols_with_libdwarf.cpp000066400000000000000000000163661504061443700227150ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF #include "symbols/symbols.hpp" #include #include "dwarf/resolver.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include "binary/elf.hpp" #include "binary/mach-o.hpp" #include "jit/jit_objects.hpp" #include #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace libdwarf { std::unique_ptr get_resolver_for_object(const std::string& object_path) { #if IS_APPLE // Check if dSYM exist, if not fallback to debug map if(!directory_exists(object_path + ".dSYM")) { return make_debug_map_resolver(object_path); } #endif return make_dwarf_resolver(object_path); } // not thread-safe, replies on caller to lock maybe_owned get_resolver(const std::string& object_name) { // cache resolvers since objects are likely to be traced more than once static std::unordered_map> resolver_map; auto it = resolver_map.find(object_name); if(it != resolver_map.end()) { return it->second.get(); } else { std::unique_ptr resolver_object = get_resolver_for_object(object_name); if(get_cache_mode() == cache_mode::prioritize_speed) { // .emplace needed, for some reason .insert tries to copy <= gcc 7.2 return resolver_map.emplace(object_name, std::move(resolver_object)).first->second.get(); } else { // gcc 4 has trouble with automatic moves of locals here https://godbolt.org/z/9oWdWjbf8 return maybe_owned{std::move(resolver_object)}; } } } // flatten trace with inlines std::vector flatten_inlines(std::vector& trace) { std::vector final_trace; for(auto& entry : trace) { // most recent call first if(!entry.inlines.empty()) { // insert in reverse order final_trace.insert( final_trace.end(), std::make_move_iterator(entry.inlines.rbegin()), std::make_move_iterator(entry.inlines.rend()) ); } final_trace.push_back(std::move(entry.frame)); if(!entry.inlines.empty()) { // rotate line info due to quirk of how dwarf stores this stuff // inclusive range auto begin = final_trace.end() - (1 + entry.inlines.size()); auto end = final_trace.end() - 1; auto carry_line = end->line; auto carry_column = end->column; std::string carry_filename = std::move(end->filename); for(auto it = end; it != begin; it--) { it->line = (it - 1)->line; it->column = (it - 1)->column; it->filename = std::move((it - 1)->filename); } begin->line = carry_line; begin->column = carry_column; begin->filename = std::move(carry_filename); } } return final_trace; } #if IS_LINUX || IS_APPLE CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void try_resolve_jit_frame(const cpptrace::object_frame& dlframe, frame_with_inlines& frame) { auto object_res = lookup_jit_object(dlframe.raw_address); // TODO: At some point, dwarf resolution if(object_res) { frame.frame.symbol = object_res.unwrap().object .lookup_symbol(dlframe.raw_address - object_res.unwrap().base).value_or(""); } } #endif CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING void try_resolve_frame( symbol_resolver* resolver, const cpptrace::object_frame& dlframe, frame_with_inlines& frame ) { try { frame = resolver->resolve_frame(dlframe); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); frame.frame.raw_address = dlframe.raw_address; frame.frame.object_address = dlframe.object_address; frame.frame.filename = dlframe.object_path; } } CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING std::vector resolve_frames(const std::vector& frames) { std::vector trace(frames.size(), {null_frame(), {}}); // Locking around all libdwarf interaction per https://github.com/davea42/libdwarf-code/discussions/184 // And also locking for interactions with get_resolver static std::mutex mutex; const std::lock_guard lock(mutex); for(const auto& group : collate_frames(frames, trace)) { try { const auto& object_name = group.first; if(object_name.empty()) { #if IS_LINUX || IS_APPLE for(const auto& entry : group.second) { try_resolve_jit_frame(entry.first.get(), entry.second.get()); } #endif continue; } // TODO PERF: Potentially a duplicate open and parse with module base stuff (and debug map resolver) #if IS_LINUX auto object = open_elf_cached(object_name); #elif IS_APPLE auto object = open_mach_o_cached(object_name); #endif auto resolver = get_resolver(object_name); for(const auto& entry : group.second) { const auto& dlframe = entry.first.get(); auto& frame = entry.second.get(); try_resolve_frame(resolver.get(), dlframe, frame); #if IS_LINUX || IS_APPLE // fallback to symbol tables if(frame.frame.symbol.empty() && object.has_value()) { frame.frame.symbol = object .unwrap_value() ->lookup_symbol(dlframe.object_address).value_or(""); } #endif } } catch(...) { // NOSONAR detail::log_and_maybe_propagate_exception(std::current_exception()); } } // fill in basic info for any frames where there were resolution issues for(std::size_t i = 0; i < frames.size(); i++) { const auto& dlframe = frames[i]; auto& frame = trace[i]; if(frame.frame == null_frame()) { frame = { { dlframe.raw_address, dlframe.object_address, nullable::null(), nullable::null(), dlframe.object_path, "", false }, {} }; } } // flatten and finish return flatten_inlines(trace); } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/symbols/symbols_with_nothing.cpp000066400000000000000000000011221504061443700225510ustar00rootroot00000000000000#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING #include #include "symbols/symbols.hpp" #include "utils/common.hpp" #include CPPTRACE_BEGIN_NAMESPACE namespace detail { namespace nothing { std::vector resolve_frames(const std::vector& frames) { return std::vector(frames.size(), null_frame()); } std::vector resolve_frames(const std::vector& frames) { return std::vector(frames.size(), null_frame()); } } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/000077500000000000000000000000001504061443700154145ustar00rootroot00000000000000cpptrace-1.0.4/src/unwind/unwind.hpp000066400000000000000000000017571504061443700174430ustar00rootroot00000000000000#ifndef UNWIND_HPP #define UNWIND_HPP #include #include #include #ifdef CPPTRACE_UNWIND_WITH_DBGHELP #define WIN32_LEAN_AND_MEAN #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #ifdef CPPTRACE_HARD_MAX_FRAMES constexpr std::size_t hard_max_frames = CPPTRACE_HARD_MAX_FRAMES; #else constexpr std::size_t hard_max_frames = 400; #endif #ifdef CPPTRACE_UNWIND_WITH_DBGHELP CPPTRACE_FORCE_NO_INLINE std::vector capture_frames( std::size_t skip, std::size_t max_depth, EXCEPTION_POINTERS* exception_pointers = nullptr ); #else CPPTRACE_FORCE_NO_INLINE std::vector capture_frames(std::size_t skip, std::size_t max_depth); #endif CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth); bool has_safe_unwind(); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/unwind_with_dbghelp.cpp000066400000000000000000000145041504061443700221500ustar00rootroot00000000000000#ifdef CPPTRACE_UNWIND_WITH_DBGHELP #include #include "unwind/unwind.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include "platform/dbghelp_utils.hpp" #include #include #include #include // Fucking windows headers #ifdef min #undef min #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { #if IS_MSVC #pragma warning(push) #pragma warning(disable: 4740) // warning C4740: flow in or out of inline asm code suppresses global optimization #endif CPPTRACE_FORCE_NO_INLINE std::vector capture_frames( std::size_t skip, std::size_t max_depth, EXCEPTION_POINTERS* exception_pointers ) { // https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/ // Get current thread context // GetThreadContext cannot be used on the current thread. // RtlCaptureContext doesn't work on i386 CONTEXT context; ZeroMemory(&context, sizeof(CONTEXT)); if(exception_pointers) { context = *exception_pointers->ContextRecord; } else { skip++; // we're unwinding from the capture_frames frame, skip it #if defined(_M_IX86) || defined(__i386__) context.ContextFlags = CONTEXT_CONTROL; #if IS_MSVC __asm { label: mov [context.Ebp], ebp; mov [context.Esp], esp; mov eax, [label]; mov [context.Eip], eax; } #else asm( "label:\n\t" "mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t" "mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t" "mov{l $label, %%eax | eax, OFFSET label};\n\t" "mov{l %%eax, %[cEip] | %[cEip], eax};\n\t" : [cEbp] "=r" (context.Ebp), [cEsp] "=r" (context.Esp), [cEip] "=r" (context.Eip) ); #endif #else RtlCaptureContext(&context); #endif } // Setup current frame STACKFRAME64 frame; ZeroMemory(&frame, sizeof(STACKFRAME64)); DWORD machine_type; #if defined(_M_IX86) || defined(__i386__) machine_type = IMAGE_FILE_MACHINE_I386; frame.AddrPC.Offset = context.Eip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Ebp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Esp; frame.AddrStack.Mode = AddrModeFlat; #elif defined(_M_X64) || defined(__x86_64__) machine_type = IMAGE_FILE_MACHINE_AMD64; frame.AddrPC.Offset = context.Rip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Rsp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Rsp; frame.AddrStack.Mode = AddrModeFlat; #elif defined(_M_IA64) machine_type = IMAGE_FILE_MACHINE_IA64; frame.AddrPC.Offset = context.StIIP; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.IntSp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrBStore.Offset= context.RsBSP; frame.AddrBStore.Mode = AddrModeFlat; frame.AddrStack.Offset = context.IntSp; frame.AddrStack.Mode = AddrModeFlat; #elif defined(_M_ARM) || defined(__arm__) machine_type = IMAGE_FILE_MACHINE_ARM; frame.AddrPC.Offset = context.Pc; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.R11; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Sp; frame.AddrStack.Mode = AddrModeFlat; #elif defined(_M_ARM64) || defined(__aarch64__) machine_type = IMAGE_FILE_MACHINE_ARM64; frame.AddrPC.Offset = context.Pc; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Fp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Sp; frame.AddrStack.Mode = AddrModeFlat; #else #error "Cpptrace: StackWalk64 not supported for this platform yet" #endif std::vector trace; // Dbghelp is is single-threaded, so acquire a lock. auto lock = get_dbghelp_lock(); // For some reason SymInitialize must be called before StackWalk64 // Note that the code assumes that // SymInitialize( GetCurrentProcess(), NULL, TRUE ) has // already been called. // auto syminit_info = ensure_syminit(); HANDLE thread = GetCurrentThread(); while(trace.size() < max_depth) { if( !StackWalk64( machine_type, syminit_info.get_process_handle(), thread, &frame, machine_type == IMAGE_FILE_MACHINE_I386 ? NULL : &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ) ) { // Either failed or finished walking break; } if(frame.AddrPC.Offset != 0) { // Valid frame if(skip) { skip--; } else { // On x86/x64/arm, as far as I can tell, the frame return address is always one after the call // So we just decrement to get the pc back inside the `call` / `bl` // This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo. trace.push_back(to_frame_ptr(frame.AddrPC.Offset) - 1); } } else { // base break; } } return trace; } CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) { // Can't safe trace with dbghelp return 0; } #if IS_MSVC #pragma warning(pop) #endif bool has_safe_unwind() { return false; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/unwind_with_execinfo.cpp000066400000000000000000000026461504061443700223470ustar00rootroot00000000000000#ifdef CPPTRACE_UNWIND_WITH_EXECINFO #include "unwind/unwind.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { CPPTRACE_FORCE_NO_INLINE std::vector capture_frames(std::size_t skip, std::size_t max_depth) { skip++; std::vector addrs(skip + std::min(hard_max_frames, max_depth), nullptr); // thread safe const int n_frames = backtrace(addrs.data(), static_cast(addrs.size())); // I hate the copy here but it's the only way that isn't UB std::vector frames(n_frames - skip, 0); for(int i = skip; i < n_frames; i++) { // On x86/x64/arm, as far as I can tell, the frame return address is always one after the call // So we just decrement to get the pc back inside the `call` / `bl` // This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo. frames[i - skip] = reinterpret_cast(addrs[i]) - 1; } return frames; } CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) { // Can't safe trace with execinfo return 0; } bool has_safe_unwind() { return false; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/unwind_with_libunwind.cpp000066400000000000000000000056231504061443700225400ustar00rootroot00000000000000#ifdef CPPTRACE_UNWIND_WITH_LIBUNWIND #include "unwind/unwind.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/utils.hpp" #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { CPPTRACE_FORCE_NO_INLINE std::vector capture_frames(std::size_t skip, std::size_t max_depth) { skip++; std::vector frames; unw_context_t context; unw_cursor_t cursor; unw_getcontext(&context); unw_init_local(&cursor, &context); do { unw_word_t pc; unw_word_t sp; unw_get_reg(&cursor, UNW_REG_IP, &pc); unw_get_reg(&cursor, UNW_REG_SP, &sp); if(skip) { skip--; } else { // pc is the instruction after the `call`, adjust back to the previous instruction frames.push_back(to_frame_ptr(pc) - 1); } } while(unw_step(&cursor) > 0 && frames.size() < max_depth); return frames; } CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) { // some code duplication, but whatever skip++; unw_context_t context; unw_cursor_t cursor; // thread and signal-safe https://www.nongnu.org/libunwind/man/unw_getcontext(3).html unw_getcontext(&context); // thread and signal-safe https://www.nongnu.org/libunwind/man/unw_init_local(3).html unw_init_local(&cursor, &context); size_t i = 0; while(i < size && i < max_depth) { unw_word_t pc; unw_word_t sp; // thread and signal-safe https://www.nongnu.org/libunwind/man/unw_get_reg(3).html unw_get_reg(&cursor, UNW_REG_IP, &pc); unw_get_reg(&cursor, UNW_REG_SP, &sp); if(skip) { skip--; } else { // thread and signal-safe if(unw_is_signal_frame(&cursor)) { // pc is the instruction that caused the signal // just a cast, thread and signal safe buffer[i] = to_frame_ptr(pc); } else { // pc is the instruction after the `call`, adjust back to the previous instruction // just a cast, thread and signal safe buffer[i] = to_frame_ptr(pc) - 1; } i++; } // thread and signal-safe as long as the cursor is in the local address space, which it is // https://www.nongnu.org/libunwind/man/unw_step(3).html if(unw_step(&cursor) <= 0) { break; } } return i; } bool has_safe_unwind() { return true; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/unwind_with_nothing.cpp000066400000000000000000000007361504061443700222130ustar00rootroot00000000000000#ifdef CPPTRACE_UNWIND_WITH_NOTHING #include "unwind/unwind.hpp" #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { std::vector capture_frames(std::size_t, std::size_t) { return {}; } CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) { return 0; } bool has_safe_unwind() { return false; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/unwind_with_unwind.cpp000066400000000000000000000040271504061443700220460ustar00rootroot00000000000000#ifdef CPPTRACE_UNWIND_WITH_UNWIND #include "unwind/unwind.hpp" #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/utils.hpp" #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { struct unwind_state { std::size_t skip; std::size_t max_depth; std::vector& vec; }; _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) { unwind_state& state = *static_cast(arg); if(state.skip) { state.skip--; if(_Unwind_GetIP(context) == frame_ptr(0)) { return _URC_END_OF_STACK; } else { return _URC_NO_REASON; } } ASSERT( state.vec.size() < state.max_depth, "Somehow cpptrace::detail::unwind_callback is being called beyond the max_depth" ); int is_before_instruction = 0; frame_ptr ip = _Unwind_GetIPInfo(context, &is_before_instruction); if(!is_before_instruction && ip != frame_ptr(0)) { ip--; } if (ip == frame_ptr(0)) { return _URC_END_OF_STACK; } else { state.vec.push_back(ip); if(state.vec.size() >= state.max_depth) { return _URC_END_OF_STACK; } else { return _URC_NO_REASON; } } } CPPTRACE_FORCE_NO_INLINE std::vector capture_frames(std::size_t skip, std::size_t max_depth) { std::vector frames; unwind_state state{skip + 1, max_depth, frames}; _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe return frames; } CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) { // Can't safe trace with _Unwind return 0; } bool has_safe_unwind() { return false; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/unwind/unwind_with_winapi.cpp000066400000000000000000000031441504061443700220300ustar00rootroot00000000000000#ifdef CPPTRACE_UNWIND_WITH_WINAPI #include #include "unwind/unwind.hpp" #include "utils/common.hpp" #include "utils/utils.hpp" #include #include #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include // Fucking windows headers #ifdef min #undef min #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { CPPTRACE_FORCE_NO_INLINE std::vector capture_frames(std::size_t skip, std::size_t max_depth) { std::vector addrs(skip + std::min(hard_max_frames, max_depth), nullptr); std::size_t n_frames = CaptureStackBackTrace( static_cast(skip + 1), static_cast(addrs.size()), addrs.data(), NULL ); // I hate the copy here but it's the only way that isn't UB std::vector frames(n_frames, 0); for(std::size_t i = 0; i < n_frames; i++) { // On x86/x64/arm, as far as I can tell, the frame return address is always one after the call // So we just decrement to get the pc back inside the `call` / `bl` // This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo. frames[i] = reinterpret_cast(addrs[i]) - 1; } return frames; } CPPTRACE_FORCE_NO_INLINE std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) { // Can't safe trace with winapi return 0; } bool has_safe_unwind() { return false; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils.cpp000066400000000000000000000062431504061443700157610ustar00rootroot00000000000000#include #include #include #include #include "demangle/demangle.hpp" #include "snippets/snippet.hpp" #include "utils/utils.hpp" #include "platform/exception_type.hpp" #include "options.hpp" #if !IS_WINDOWS #include #endif CPPTRACE_BEGIN_NAMESPACE std::string demangle(const std::string& name) { return detail::demangle(name, false); } std::string get_snippet(const std::string& path, std::size_t line, std::size_t context_size, bool color) { return detail::get_snippet(path, line, nullable::null(), context_size, color); } std::string get_snippet( const std::string& path, std::size_t line, nullable column, std::size_t context_size, bool color ) { return detail::get_snippet(path, line, column, context_size, color); } bool isatty(int fd) { return detail::isatty(fd); } #if IS_WINDOWS extern const int stdin_fileno = detail::fileno(stdin); extern const int stdout_fileno = detail::fileno(stdout); extern const int stderr_fileno = detail::fileno(stderr); #else extern const int stdin_fileno = STDIN_FILENO; extern const int stdout_fileno = STDOUT_FILENO; extern const int stderr_fileno = STDERR_FILENO; #endif CPPTRACE_FORCE_NO_INLINE void print_terminate_trace() { try { // try/catch can never be hit but it's needed to prevent TCO get_default_formatter().print(std::cerr, generate_trace(1)); } catch(...) { detail::log_and_maybe_propagate_exception(std::current_exception()); } } [[noreturn]] void MSVC_CDECL terminate_handler() { // TODO: Support std::nested_exception? try { auto ptr = std::current_exception(); if(ptr == nullptr) { fputs("terminate called without an active exception", stderr); print_terminate_trace(); } else { std::rethrow_exception(ptr); } } catch(cpptrace::exception& e) { microfmt::print( stderr, "Terminate called after throwing an instance of {}: {}\n", demangle(typeid(e).name()), e.message() ); e.trace().print(std::cerr, isatty(stderr_fileno)); } catch(std::exception& e) { microfmt::print( stderr, "Terminate called after throwing an instance of {}: {}\n", demangle(typeid(e).name()), e.what() ); print_terminate_trace(); } catch(...) { microfmt::print( stderr, "Terminate called after throwing an instance of {}\n", detail::exception_type_name() ); print_terminate_trace(); } std::flush(std::cerr); abort(); } void register_terminate_handler() { std::set_terminate(terminate_handler); } #if defined(_WIN32) && !defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) void load_symbols_for_file(const std::string&) { // nop } #endif CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/000077500000000000000000000000001504061443700152505ustar00rootroot00000000000000cpptrace-1.0.4/src/utils/common.hpp000066400000000000000000000024321504061443700172520ustar00rootroot00000000000000#ifndef COMMON_HPP #define COMMON_HPP #include #include "platform/platform.hpp" #include #define ESC "\033[" #define RESET ESC "0m" #define RED ESC "31m" #define GREEN ESC "32m" #define YELLOW ESC "33m" #define BLUE ESC "34m" #define MAGENTA ESC "35m" #define CYAN ESC "36m" #if IS_GCC || IS_CLANG #define NODISCARD __attribute__((warn_unused_result)) // #elif IS_MSVC && _MSC_VER >= 1700 // #define NODISCARD _Check_return_ #else #define NODISCARD #endif // workaround a bizarre gcc bug https://godbolt.org/z/s78vnf7jv // https://github.com/jeremy-rifkin/cpptrace/issues/220 #if defined(__GNUC__) && (__GNUC__ < 7) #undef NODISCARD #define NODISCARD #endif #if IS_MSVC #define MSVC_CDECL __cdecl #else #define MSVC_CDECL #endif // support is pretty good https://godbolt.org/z/djTqv7WMY, checked in cmake during config #ifdef HAS_ATTRIBUTE_PACKED #define PACKED __attribute__((packed)) #else #define PACKED #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { inline stacktrace_frame null_frame() { return { 0, 0, nullable::null(), nullable::null(), "", "", false }; } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/error.cpp000066400000000000000000000052531504061443700171120ustar00rootroot00000000000000#include "utils/error.hpp" #include #include #include "platform/exception_type.hpp" #include "logging.hpp" #include "options.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { internal_error::internal_error(std::string message) : msg("Cpptrace internal error: " + std::move(message)) {} constexpr const char* assert_actions[] = {"assertion", "verification", "panic"}; constexpr const char* assert_names[] = {"ASSERT", "VERIFY", "PANIC"}; void assert_fail( assert_type type, const char* expression, const char* signature, source_location location, const char* message ) { const char* action = assert_actions[static_cast::type>(type)]; const char* name = assert_names[static_cast::type>(type)]; if(message == nullptr) { throw internal_error( "Cpptrace {} failed at {}:{}: {}\n" " {}({});\n", action, location.file, location.line, signature, name, expression ); } else { throw internal_error( "Cpptrace {} failed at {}:{}: {}: {}\n" " {}({});\n", action, location.file, location.line, signature, message, name, expression ); } } void panic( const char* signature, source_location location, string_view message ) { if(message == "") { throw internal_error( "Cpptrace panic {}:{}: {}\n", location.file, location.line, signature ); } else { throw internal_error( "Cpptrace panic {}:{}: {}: {}\n", location.file, location.line, signature, message ); } } void log_and_maybe_propagate_exception(std::exception_ptr ptr) { try { if(ptr) { std::rethrow_exception(ptr); } } catch(const internal_error& e) { log::error("Unhandled cpptrace internal error: {}", e.what()); } catch(const std::exception& e) { log::error( "Unhandled cpptrace internal error of type {}: {}", cpptrace::demangle(typeid(e).name()), e.what() ); } catch(...) { log::error("Unhandled cpptrace internal error of type {}", detail::exception_type_name()); } if(!should_absorb_trace_exceptions()) { if(ptr) { std::rethrow_exception(ptr); } } } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/error.hpp000066400000000000000000000067251504061443700171240ustar00rootroot00000000000000#ifndef ERROR_HPP #define ERROR_HPP #include #include #include #include "platform/platform.hpp" #include "utils/microfmt.hpp" #include "utils/string_view.hpp" #include #if IS_MSVC #define CPPTRACE_PFUNC __FUNCSIG__ #else #define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__ #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { class internal_error : public std::exception { std::string msg; public: internal_error(std::string message); template internal_error(const char* format, Args&&... args) : internal_error(microfmt::format(format, args...)) {} const char* what() const noexcept override { return msg.c_str(); } }; // Lightweight std::source_location. struct source_location { const char* const file; const int line; constexpr source_location( const char* _file, int _line ) : file(_file), line(_line) {} }; #define CPPTRACE_CURRENT_LOCATION ::cpptrace::detail::source_location(__FILE__, __LINE__) enum class assert_type { assert, verify, panic, }; [[noreturn]] CPPTRACE_EXPORT void assert_fail( assert_type type, const char* expression, const char* signature, source_location location, const char* message ); [[noreturn]] void panic( const char* signature, source_location location, string_view message = "" ); template void nullfn(Args&&...); #define PHONY_USE(...) (static_cast(0)) // Work around a compiler warning template std::string as_string(T&& value) { return std::string(std::forward(value)); } inline std::string as_string() { return ""; } // Check condition in both debug and release. std::runtime_error on failure. #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION, ::cpptrace::detail::as_string(__VA_ARGS__))) inline void assert_impl( bool condition, const char* message, assert_type type, const char* args, const char* signature, source_location location ) { if(!condition) { assert_fail(type, args, signature, location, message); } } inline void assert_impl( bool condition, assert_type type, const char* args, const char* signature, source_location location ) { assert_impl( condition, nullptr, type, args, signature, location ); } // Check condition in both debug and release. std::runtime_error on failure. #define VERIFY(...) ( \ assert_impl(__VA_ARGS__, ::cpptrace::detail::assert_type::verify, #__VA_ARGS__, CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION) \ ) #ifndef NDEBUG // Check condition in both debug. std::runtime_error on failure. #define ASSERT(...) ( \ assert_impl(__VA_ARGS__, ::cpptrace::detail::assert_type::assert, #__VA_ARGS__, CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION) \ ) #else // Check condition in both debug. std::runtime_error on failure. #define ASSERT(...) PHONY_USE(__VA_ARGS__) #endif void log_and_maybe_propagate_exception(std::exception_ptr); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/io/000077500000000000000000000000001504061443700156575ustar00rootroot00000000000000cpptrace-1.0.4/src/utils/io/base_file.hpp000066400000000000000000000026261504061443700203070ustar00rootroot00000000000000#ifndef BASE_FILE_HPP #define BASE_FILE_HPP #include "utils/span.hpp" #include "utils/utils.hpp" #include CPPTRACE_BEGIN_NAMESPACE namespace detail { class base_file { public: virtual ~base_file() = default; virtual string_view path() const = 0; virtual Result read_bytes(bspan buffer, off_t offset) const = 0; template< typename T, typename std::enable_if< std::is_standard_layout::value && is_trivially_copyable::value && !is_span::value, int >::type = 0 > Result read(off_t offset) { T object{}; auto res = read_bytes(make_bspan(object), offset); if(!res) { return res.unwrap_error(); } return object; } template< typename T, typename std::enable_if< std::is_standard_layout::value && is_trivially_copyable::value, int >::type = 0 > Result read_span(span items, off_t offset) { return read_bytes( make_span(reinterpret_cast(items.data()), reinterpret_cast(items.data() + items.size())), offset ); } }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/io/file.cpp000066400000000000000000000020001504061443700172720ustar00rootroot00000000000000#define _CRT_SECURE_NO_WARNINGS #include "utils/io/file.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { string_view file::path() const { return object_path; } Result file::open(cstring_view object_path) { auto file_obj = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter); if(file_obj == nullptr) { return internal_error("Unable to read object file {}", object_path); } return file(std::move(file_obj), object_path); } Result file::read_bytes(bspan buffer, off_t offset) const { if(std::fseek(file_obj, offset, SEEK_SET) != 0) { return internal_error("fseek error in {} at offset {}", path(), offset); } if(std::fread(buffer.data(), buffer.size(), 1, file_obj) != 1) { return internal_error("fread error in {} at offset {} for {} bytes", path(), offset, buffer.size()); } return monostate{}; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/io/file.hpp000066400000000000000000000013651504061443700173140ustar00rootroot00000000000000#ifndef FILE_HPP #define FILE_HPP #include "utils/string_view.hpp" #include "utils/span.hpp" #include "utils/io/base_file.hpp" #include "utils/utils.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { class file : public base_file { file_wrapper file_obj; std::string object_path; file(file_wrapper file_obj, string_view path) : file_obj(std::move(file_obj)), object_path(path) {} public: file(file&&) = default; ~file() override = default; string_view path() const override; static Result open(cstring_view object_path); virtual Result read_bytes(bspan buffer, off_t offset) const override; }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/io/memory_file_view.cpp000066400000000000000000000014341504061443700217260ustar00rootroot00000000000000#include "utils/io/memory_file_view.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { string_view memory_file_view::path() const { return object_path; } Result memory_file_view::read_bytes(bspan buffer, off_t offset) const { if(offset < 0) { return internal_error("Illegal read in memory file {}: offset {}", path(), offset); } if(offset + buffer.size() > data.size()) { return internal_error( "Illegal read in memory file {}: offset = {}, size = {}, file size = {}", path(), offset, buffer.size(), data.size() ); } std::memcpy(buffer.data(), data.data() + offset, buffer.size()); return monostate{}; } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/io/memory_file_view.hpp000066400000000000000000000012031504061443700217250ustar00rootroot00000000000000#ifndef MEMORY_FILE_VIEW_HPP #define MEMORY_FILE_VIEW_HPP #include "utils/error.hpp" #include "utils/span.hpp" #include "utils/io/base_file.hpp" #include "utils/utils.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { class memory_file_view : public base_file { cbspan data; std::string object_path = ""; public: memory_file_view(cbspan data) : data(data) {} ~memory_file_view() override = default; string_view path() const override; virtual Result read_bytes(bspan buffer, off_t offset) const override; }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/lru_cache.hpp000066400000000000000000000055071504061443700177150ustar00rootroot00000000000000#ifndef LRU_CACHE_HPP #define LRU_CACHE_HPP #include "utils/error.hpp" #include "utils/optional.hpp" #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { template class lru_cache { struct kvp { K key; V value; }; using list_type = std::list; using list_iterator = typename list_type::iterator; mutable list_type lru; std::unordered_map map; optional max_size; public: lru_cache() = default; lru_cache(optional max_size) : max_size(max_size) { VERIFY(!max_size || max_size.unwrap() > 0); } void set_max_size(optional size) { VERIFY(!size || size.unwrap() > 0); max_size = size; maybe_trim(); } void maybe_touch(const K& key) { auto it = map.find(key); if(it == map.end()) { return; } auto list_it = it->second; touch(list_it); } optional maybe_get(const K& key) { auto it = map.find(key); if(it == map.end()) { return nullopt; } else { touch(it->second); return it->second->value; } } optional maybe_get(const K& key) const { auto it = map.find(key); if(it == map.end()) { return nullopt; } else { touch(it->second); return it->second->value; } } void set(const K& key, V value) { auto it = map.find(key); if(it == map.end()) { insert(key, std::move(value)); } else { touch(it->second); it->second->value = std::move(value); } } optional insert(const K& key, V value) { auto pair = map.insert({key, lru.end()}); if(!pair.second) { // didn't insert return nullopt; } auto map_it = pair.first; lru.push_front({key, std::move(value)}); map_it->second = lru.begin(); maybe_trim(); return lru.front().value; } std::size_t size() const { return lru.size(); } private: void touch(list_iterator list_it) const { lru.splice(lru.begin(), lru, list_it); } void maybe_trim() { while(max_size && lru.size() > max_size.unwrap()) { const auto& to_remove = lru.back(); map.erase(to_remove.key); lru.pop_back(); } } }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/microfmt.cpp000066400000000000000000000003211504061443700175700ustar00rootroot00000000000000#include "utils/microfmt.hpp" #include CPPTRACE_BEGIN_NAMESPACE namespace microfmt { namespace detail { std::ostream& get_cout() { return std::cout; } } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/microfmt.hpp000066400000000000000000000317131504061443700176060ustar00rootroot00000000000000#ifndef MICROFMT_HPP #define MICROFMT_HPP #include #include #include #include #include #include #include #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) #include #endif #ifdef _MSC_VER #include #endif #include "utils/string_view.hpp" // https://github.com/jeremy-rifkin/microfmt // Format: {[align][width][:[fill][base]]} # width: number or {} CPPTRACE_BEGIN_NAMESPACE namespace microfmt { namespace detail { inline std::uint64_t clz(std::uint64_t value) { #ifdef _MSC_VER unsigned long out = 0; #ifdef _WIN64 _BitScanReverse64(&out, value); #else if(_BitScanReverse(&out, std::uint32_t(value >> 32))) { return 63 - int(out + 32); } _BitScanReverse(&out, std::uint32_t(value)); #endif return 63 - out; #else return __builtin_clzll(value); #endif } template U to(V v) { return static_cast(v); // A way to cast to U without "warning: useless cast to type" } enum class alignment { left, right }; struct format_options { alignment align = alignment::left; char fill = ' '; size_t width = 0; char base = 'd'; }; template void do_write(OutputIt out, InputIt begin, InputIt end, const format_options& options) { auto size = end - begin; if(static_cast(size) >= options.width) { std::copy(begin, end, out); } else { if(options.align == alignment::left) { std::copy(begin, end, out); std::fill_n(out, options.width - size, options.fill); } else { std::fill_n(out, options.width - size, options.fill); std::copy(begin, end, out); } } } template std::string to_string(std::uint64_t value, const char* digits = "0123456789abcdef") { if(value == 0) { return "0"; } else { // digits = floor(1 + log_base(x)) // log_base(x) = log_2(x) / log_2(base) // log_2(x) == 63 - clz(x) // 1 + (63 - clz(value)) / (63 - clz(1 << shift)) // 63 - clz(1 << shift) is the same as shift auto n_digits = to(1 + (63 - clz(value)) / shift); std::string number; number.resize(n_digits); std::size_t i = n_digits - 1; while(value > 0) { number[i--] = digits[value & mask]; value >>= shift; } return number; } } inline std::string to_string(std::uint64_t value, const format_options& options) { switch(options.base) { case 'H': return to_string<4, 0xf>(value, "0123456789ABCDEF"); case 'h': return to_string<4, 0xf>(value); case 'o': return to_string<3, 0x7>(value); case 'b': return to_string<1, 0x1>(value); default: return std::to_string(value); // failure: decimal } } struct string_view { const char* data; std::size_t size; }; class format_value { enum class value_type { char_value, int64_value, uint64_value, string_value, string_view_value, c_string_value, }; union { char char_value; std::int64_t int64_value; std::uint64_t uint64_value; const std::string* string_value; string_view string_view_value; const char* c_string_value; }; value_type value; public: format_value(char c) : char_value(c), value(value_type::char_value) {} format_value(short int_val) : int64_value(int_val), value(value_type::int64_value) {} format_value(int int_val) : int64_value(int_val), value(value_type::int64_value) {} format_value(long int_val) : int64_value(int_val), value(value_type::int64_value) {} format_value(long long int_val) : int64_value(int_val), value(value_type::int64_value) {} format_value(unsigned char int_val) : uint64_value(int_val), value(value_type::uint64_value) {} format_value(unsigned short int_val) : uint64_value(int_val), value(value_type::uint64_value) {} format_value(unsigned int int_val) : uint64_value(int_val), value(value_type::uint64_value) {} format_value(unsigned long int_val) : uint64_value(int_val), value(value_type::uint64_value) {} format_value(unsigned long long int_val) : uint64_value(int_val), value(value_type::uint64_value) {} format_value(const std::string& string) : string_value(&string), value(value_type::string_value) {} #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) format_value(std::string_view sv) : string_view_value{sv.data(), sv.size()}, value(value_type::string_view_value) {} #endif format_value(cpptrace::detail::string_view sv) : string_view_value{sv.data(), sv.size()}, value(value_type::string_view_value) {} format_value(const char* c_string) : c_string_value(c_string), value(value_type::c_string_value) {} int unwrap_int() const { switch(value) { case value_type::int64_value: return static_cast(int64_value); case value_type::uint64_value: return static_cast(uint64_value); default: return 0; // failure: just 0 } } public: template void write(OutputIt out, const format_options& options) const { switch(value) { case value_type::char_value: do_write(out, &char_value, &char_value + 1, options); break; case value_type::int64_value: { std::string str; std::int64_t val = int64_value; if(val < 0) { str += '-'; val *= -1; } str += to_string(static_cast(val), options); do_write(out, str.begin(), str.end(), options); } break; case value_type::uint64_value: { std::string str = to_string(uint64_value, options); do_write(out, str.begin(), str.end(), options); } break; case value_type::string_value: do_write(out, string_value->begin(), string_value->end(), options); break; case value_type::string_view_value: do_write(out, string_view_value.data, string_view_value.data + string_view_value.size, options); break; case value_type::c_string_value: do_write(out, c_string_value, c_string_value + std::strlen(c_string_value), options); break; } // failure: nop } }; // note: previously used std::array and there was a bug with std::array affecting old msvc // https://godbolt.org/z/88T8hrzzq mre: https://godbolt.org/z/drd8echbP template void format(OutputIt out, InputIt fmt_begin, InputIt fmt_end, const std::initializer_list& args) { std::size_t arg_i = 0; auto it = fmt_begin; auto peek = [&] (std::size_t dist) -> char { // 0 on failure return fmt_end - it > signed(dist) ? *(it + dist) : 0; }; auto read_number = [&] () -> int { // -1 on failure auto scan = it; int num = 0; while(scan != fmt_end && isdigit(*scan)) { num *= 10; num += *scan - '0'; scan++; } if(scan != it) { it = scan; return num; } else { return -1; } }; for(; it != fmt_end; it++) { if((*it == '{' || *it == '}') && peek(1) == *it) { // parse {{ and }} escapes it++; } else if(*it == '{' && it + 1 != fmt_end) { auto saved_it = it; auto handle_formatter = [&] () { it++; format_options options; // try to parse alignment if(*it == '<' || *it == '>') { options.align = *it++ == '<' ? alignment::left : alignment::right; } // try to parse width auto width = read_number(); // handles fmt_end check if(width != -1) { options.width = width; } else if(it != fmt_end && *it == '{') { // try to parse variable width if(peek(1) != '}') { return false; } it += 2; options.width = arg_i < args.size() ? args.begin()[arg_i++].unwrap_int() : 0; } // try to parse fill/base if(it != fmt_end && *it == ':') { it++; if(fmt_end - it > 1 && *it != '}' && peek(1) != '}') { // two chars before the }, fill+base options.fill = *it++; options.base = *it++; } else if(it != fmt_end && *it != '}') { // one char before the }, just base if(*it == 'd' || *it == 'h' || *it == 'H' || *it == 'o' || *it == 'b') { options.base = *it++; } else { options.fill = *it++; } } } if(it == fmt_end || *it != '}') { return false; } if(arg_i < args.size()) { args.begin()[arg_i++].write(out, options); } return true; }; if(handle_formatter()) { continue; // If reached here, successfully parsed and wrote a formatter. Don't write *it. } it = saved_it; // go back } *out++ = *it; } } #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) template void format(OutputIt out, std::string_view fmt, const std::initializer_list& args) { return format(out, fmt.begin(), fmt.end(), args); } #endif template void format(OutputIt out, const char* fmt, const std::initializer_list& args) { return format(out, fmt, fmt + std::strlen(fmt), args); } std::ostream& get_cout(); } template std::string format(const S& fmt, Args&&... args) { std::string str; detail::format(std::back_inserter(str), fmt, {detail::format_value(args)...}); return str; } template void print(const S& fmt, Args&&... args) { detail::format(std::ostream_iterator(detail::get_cout()), fmt, {args...}); } template void print(std::ostream& ostream, const S& fmt, Args&&... args) { detail::format(std::ostream_iterator(ostream), fmt, {args...}); } template void print(std::FILE* stream, const S& fmt, Args&&... args) { auto str = format(fmt, args...); fwrite(str.data(), 1, str.size(), stream); } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/optional.hpp000066400000000000000000000117151504061443700176130ustar00rootroot00000000000000#ifndef OPTIONAL_HPP #define OPTIONAL_HPP #include #include #include #include #include "utils/common.hpp" #include "utils/error.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { struct nullopt_t {}; static constexpr nullopt_t nullopt; template< typename T, typename std::enable_if< !std::is_same::type, void>::value && !std::is_rvalue_reference::value, int >::type = 0 > using well_behaved = typename std::conditional< std::is_reference::value, std::reference_wrapper::type>, T >::type; template< typename T, typename std::enable_if::type, void>::value, int>::type = 0 > class optional { using value_type = well_behaved; union { char x{}; value_type uvalue; }; bool holds_value = false; public: optional() noexcept {} optional(nullopt_t) noexcept {} ~optional() { reset(); } optional(const optional& other) : holds_value(other.holds_value) { if(holds_value) { new (static_cast(std::addressof(uvalue))) value_type(other.uvalue); } } optional(optional&& other) noexcept(std::is_nothrow_move_constructible::value) : holds_value(other.holds_value) { if(holds_value) { new (static_cast(std::addressof(uvalue))) value_type(std::move(other.uvalue)); } } optional& operator=(const optional& other) { optional copy(other); swap(copy); return *this; } optional& operator=(optional&& other) noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) { if (this != &other) { reset(); if (other.holds_value) { new (static_cast(std::addressof(uvalue))) value_type(std::move(other.uvalue)); holds_value = true; } } return *this; } template< typename U = T, typename std::enable_if::type, optional>::value, int>::type = 0 > optional(U&& value) : holds_value(true) { new (static_cast(std::addressof(uvalue))) value_type(std::forward(value)); } template< typename U = T, typename std::enable_if::type, optional>::value, int>::type = 0 > optional& operator=(U&& value) { optional o(std::forward(value)); swap(o); return *this; } optional& operator=(nullopt_t) noexcept { reset(); return *this; } void swap(optional& other) noexcept { if(holds_value && other.holds_value) { std::swap(uvalue, other.uvalue); } else if(holds_value && !other.holds_value) { new (&other.uvalue) value_type(std::move(uvalue)); uvalue.~value_type(); } else if(!holds_value && other.holds_value) { new (static_cast(std::addressof(uvalue))) value_type(std::move(other.uvalue)); other.uvalue.~value_type(); } std::swap(holds_value, other.holds_value); } bool has_value() const { return holds_value; } explicit operator bool() const { return holds_value; } void reset() { if(holds_value) { uvalue.~value_type(); } holds_value = false; } NODISCARD T& unwrap() & { ASSERT(holds_value, "Optional does not contain a value"); return uvalue; } NODISCARD const T& unwrap() const & { ASSERT(holds_value, "Optional does not contain a value"); return uvalue; } NODISCARD T&& unwrap() && { ASSERT(holds_value, "Optional does not contain a value"); return std::move(uvalue); } NODISCARD const T&& unwrap() const && { ASSERT(holds_value, "Optional does not contain a value"); return std::move(uvalue); } template NODISCARD T value_or(U&& default_value) const & { return holds_value ? static_cast(uvalue) : static_cast(std::forward(default_value)); } template NODISCARD T value_or(U&& default_value) && { return holds_value ? static_cast(std::move(uvalue)) : static_cast(std::forward(default_value)); } }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/replace_all.cpp000066400000000000000000000043651504061443700202270ustar00rootroot00000000000000#include "utils/replace_all.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { void replace_all(std::string& str, string_view substr, string_view replacement) { std::string::size_type pos = 0; while((pos = str.find(substr.data(), pos, substr.size())) != std::string::npos) { str.replace(pos, substr.size(), replacement.data(), replacement.size()); pos += replacement.size(); } } void replace_all(std::string& str, const std::regex& re, string_view replacement) { std::smatch match; std::size_t i = 0; while(std::regex_search(str.cbegin() + i, str.cend(), match, re)) { str.replace(i + match.position(), match.length(), replacement.data(), replacement.size()); i += match.position() + replacement.size(); } } void replace_all_dynamic(std::string& str, string_view substr, string_view replacement) { std::string::size_type pos = 0; while((pos = str.find(substr.data(), pos, substr.size())) != std::string::npos) { str.replace(pos, substr.size(), replacement.data(), replacement.size()); // advancing by one rather than replacement.length() in case replacement leads to // another replacement opportunity, e.g. folding > > > to >> > then >>> pos++; } } void replace_all_template(std::string& str, const std::pair& rule) { const auto& re = rule.first; const auto& replacement = rule.second; std::smatch match; std::size_t cursor = 0; while(std::regex_search(str.cbegin() + cursor, str.cend(), match, re)) { // find matching > const std::size_t match_begin = cursor + match.position(); std::size_t end = match_begin + match.length(); for(int c = 1; end < str.size() && c > 0; end++) { if(str[end] == '<') { c++; } else if(str[end] == '>') { c--; } } // make the replacement str.replace(match_begin, end - match_begin, replacement.data(), replacement.size()); cursor = match_begin + replacement.size(); } } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/replace_all.hpp000066400000000000000000000015221504061443700202240ustar00rootroot00000000000000#ifndef REPLACE_ALL #define REPLACE_ALL #include #include #include "utils/string_view.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { // replace all instances of substr with the replacement void replace_all(std::string& str, string_view substr, string_view replacement); // replace all regex matches with the replacement void replace_all(std::string& str, const std::regex& re, string_view replacement); // replace all instances of substr with the replacement, including new instances introduced by the replacement void replace_all_dynamic(std::string& str, string_view substr, string_view replacement); // replace all matches of a regex including template parameters void replace_all_template(std::string& str, const std::pair& rule); } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/result.hpp000066400000000000000000000133341504061443700173030ustar00rootroot00000000000000#ifndef RESULT_HPP #define RESULT_HPP #include #include #include #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/optional.hpp" #include "options.hpp" #include "logging.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { template::value, int>::type = 0> class Result { using value_type = well_behaved; union { value_type value_; E error_; }; enum class member { value, error }; member active; void destroy() { if(active == member::value) { value_.~value_type(); } else { error_.~E(); } } public: Result(value_type&& value) : value_(std::move(value)), active(member::value) {} Result(E&& error) : error_(std::move(error)), active(member::error) { log::debug("Error result constructed: {}", unwrap_error().what()); } Result(const value_type& value) : value_(value_type(value)), active(member::value) {} Result(const E& error) : error_(E(error)), active(member::error) { log::debug("Error result constructed: {}", unwrap_error().what()); } template< typename U = T, typename std::enable_if< !std::is_same::type, Result>::value && std::is_constructible::value && !std::is_constructible::value, int >::type = 0 > Result(U&& u) : Result(value_type(std::forward(u))) {} Result(Result&& other) : active(other.active) { if(other.active == member::value) { new (&value_) value_type(std::move(other.value_)); } else { new (&error_) E(std::move(other.error_)); } } ~Result() { destroy(); } Result& operator=(const Result& other) { if (this != &other) { destroy(); if(other.active == member::value) { new (&value_) value_type(other.value_); } else { new (&error_) E(other.error_); } active = other.active; } return *this; } Result& operator=(Result&& other) noexcept( std::is_nothrow_move_constructible::value && std::is_nothrow_move_constructible::value ) { if (this != &other) { destroy(); if(other.active == member::value) { new (&value_) value_type(std::move(other.value_)); } else { new (&error_) E(std::move(other.error_)); } active = other.active; } return *this; } bool has_value() const { return active == member::value; } bool is_error() const { return active == member::error; } explicit operator bool() const { return has_value(); } NODISCARD optional value() const & { return has_value() ? optional(value_) : nullopt; } NODISCARD optional error() const & { return is_error() ? optional(error_) : nullopt; } NODISCARD optional value() && { return has_value() ? optional(std::move(value_)) : nullopt; } NODISCARD optional error() && { return is_error() ? optional(std::move(error_)) : nullopt; } NODISCARD T& unwrap_value() & { ASSERT(has_value(), "Result does not contain a value"); return value_; } NODISCARD const T& unwrap_value() const & { ASSERT(has_value(), "Result does not contain a value"); return value_; } NODISCARD T unwrap_value() && { ASSERT(has_value(), "Result does not contain a value"); return std::move(value_); } NODISCARD E& unwrap_error() & { ASSERT(is_error(), "Result does not contain an error"); return error_; } NODISCARD const E& unwrap_error() const & { ASSERT(is_error(), "Result does not contain an error"); return error_; } NODISCARD E unwrap_error() && { ASSERT(is_error(), "Result does not contain an error"); return std::move(error_); } template NODISCARD T value_or(U&& default_value) const & { return has_value() ? static_cast(value_) : static_cast(std::forward(default_value)); } template NODISCARD T value_or(U&& default_value) && { return has_value() ? static_cast(std::move(value_)) : static_cast(std::forward(default_value)); } template NODISCARD auto transform(F&& f) & -> Result())), E> { if(has_value()) { return f(unwrap_value()); } else { return unwrap_error(); } } template NODISCARD auto transform(F&& f) && -> Result())), E> { if(has_value()) { return f(std::move(unwrap_value())); } else { return unwrap_error(); } } void drop_error() const { if(is_error()) { log::error(unwrap_error().what()); } } }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/span.hpp000066400000000000000000000053121504061443700167230ustar00rootroot00000000000000#ifndef SPAN_HPP #define SPAN_HPP #include "utils/utils.hpp" #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { // basic span implementation // I haven't implemented most members because I don't need them, more will be added as needed template class span { T* ptr; std::size_t count; public: using element_type = T; using value_type = typename std::remove_cv::type; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using iterator = T*; using const_iterator = const T*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using i_am_span = void; span() : ptr(nullptr), count(0) {} span(T* ptr, std::size_t count) : ptr(ptr), count(count) {} template span(It begin, It end) : ptr(std::addressof(*begin)), count(end - begin) {} T* data() const noexcept { return ptr; } std::size_t size() const noexcept { return count; } bool empty() const noexcept { return count == 0; } iterator begin() noexcept { return ptr; } iterator end() noexcept { return ptr + count; } const_iterator begin() const noexcept { return ptr; } const_iterator end() const noexcept { return ptr + count; } }; using bspan = span; using cbspan = span; template struct is_span : std::false_type {}; template struct is_span> : std::true_type {}; template auto make_span(It begin, It end) -> span::type> { return {begin, end}; } template auto make_span(It begin, std::size_t count) -> span::type> { return {begin, count}; } template< typename T, typename std::enable_if< std::is_standard_layout::value && is_trivially_copyable::value && !is_span::value, int >::type = 0 > span make_bspan(T& object) { return span(reinterpret_cast(std::addressof(object)), sizeof(object)); } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/string_view.cpp000066400000000000000000000031671504061443700203230ustar00rootroot00000000000000#include "utils/string_view.hpp" #include "utils/error.hpp" #include "utils/microfmt.hpp" #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { constexpr std::size_t string_view::npos; char string_view::operator[](size_t i) const { ASSERT(i < size()); return ptr[i]; } char string_view::at(size_t i) const { if(i >= size()) { throw std::runtime_error(microfmt::format("Out of bounds access {} >= {}", i, size())); } return ptr[i]; } bool string_view::starts_with(string_view str) const { return substr(0, str.size()) == str; } bool string_view::ends_with(string_view str) const { if(size() < str.size()) { return false; } return substr(size() - str.size(), str.size()) == str; } std::size_t string_view::find_last_of(string_view chars) const { if(empty() || chars.empty()) { return npos; } std::size_t pos = size(); while(pos-- > 0) { if(std::find(chars.begin(), chars.end(), ptr[pos]) != chars.end()) { return pos; } } return npos; } bool operator==(string_view a, string_view b) { return a.size() == b.size() && std::memcmp(a.data(), b.data(), a.size()) == 0; } constexpr std::size_t cstring_view::npos; cstring_view cstring_view::substr(std::size_t pos) const { ASSERT(pos <= count); return {ptr + pos, count - pos}; } void cstring_view::check_null() const { ASSERT(ptr[count] == 0); } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/string_view.hpp000066400000000000000000000127601504061443700203270ustar00rootroot00000000000000#ifndef STRING_VIEW_HPP #define STRING_VIEW_HPP #include #include #include #include #include CPPTRACE_BEGIN_NAMESPACE namespace detail { // Simple string view implementations // I haven't implemented all members because I don't need most of them currently, more may be added as needed // members exported for tests class string_view { const char* ptr; std::size_t count; public: using traits_type = std::char_traits; using value_type = char; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = char*; using const_pointer = const char*; using reference = char&; using const_reference = const char&; using iterator = char*; using const_iterator = const char*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; CPPTRACE_EXPORT static constexpr std::size_t npos = std::string::npos; string_view() : ptr(nullptr), count(0) {} string_view(const char* str) : ptr(str), count(std::strlen(str)) {} string_view(const std::string& str) : ptr(str.c_str()), count(str.size()) {} string_view(const char* ptr, std::size_t count) : ptr(ptr), count(count) {} string_view(const char* begin, const char* end) : ptr(begin), count(end - begin) {} explicit operator std::string() { return std::string(ptr, ptr + count); } const char* data() const noexcept { return ptr; } std::size_t size() const noexcept { return count; } bool empty() const noexcept { return count == 0; } CPPTRACE_EXPORT char operator[](size_t i) const; CPPTRACE_EXPORT char at(size_t i) const; char front() { return operator[](0); } char back() { return operator[](size() - 1); } string_view substr(size_t pos = 0, size_t n = npos) const { return {ptr + pos, (std::min)(size() - pos, n)}; } void advance(size_t n) { *this = substr(n); } CPPTRACE_EXPORT bool starts_with(string_view chars) const; CPPTRACE_EXPORT bool ends_with(string_view chars) const; CPPTRACE_EXPORT std::size_t find_last_of(string_view chars) const; const_iterator begin() const noexcept { return ptr; } const_iterator end() const noexcept { return ptr + count; } }; CPPTRACE_EXPORT bool operator==(string_view, string_view); inline bool operator!=(string_view a, string_view b) { return !(a == b); } inline void operator+=(std::string& str, string_view sv) { str.append(sv.data(), sv.size()); } class cstring_view { const char* ptr; std::size_t count; public: using traits_type = std::char_traits; using value_type = char; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = char*; using const_pointer = const char*; using reference = char&; using const_reference = const char&; using iterator = char*; using const_iterator = const char*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; CPPTRACE_EXPORT static constexpr std::size_t npos = string_view::npos; cstring_view() : ptr(nullptr), count(0) {} cstring_view(const char* str) : ptr(str), count(std::strlen(str)) {} cstring_view(const std::string& str) : ptr(str.c_str()), count(str.size()) {} cstring_view(const char* ptr, std::size_t count) : ptr(ptr), count(count) { check_null(); } explicit operator std::string() { return std::string(ptr, ptr + count); } operator string_view() const noexcept { return string_view(ptr, count); } const char* data() const noexcept { return ptr; } const char* c_str() const noexcept { return ptr; } std::size_t size() const noexcept { return count; } bool empty() const noexcept { return count == 0; } char operator[](size_t i) const { return operator string_view().operator[](i); } char at(size_t i) const { return operator string_view().at(i); } char front() { return operator string_view().front(); } char back() { return operator string_view().back(); } bool starts_with(string_view chars) const { return operator string_view().starts_with(chars); } bool ends_with(string_view chars) const { return operator string_view().ends_with(chars); } std::size_t find_last_of(string_view chars) const { return operator string_view().find_last_of(chars); } CPPTRACE_EXPORT cstring_view substr(std::size_t pos) const; const_iterator begin() const noexcept { return ptr; } const_iterator end() const noexcept { return ptr + count; } private: CPPTRACE_EXPORT void check_null() const; }; } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/src/utils/utils.cpp000066400000000000000000000033401504061443700171140ustar00rootroot00000000000000#include "utils/utils.hpp" #include "utils/string_view.hpp" #if IS_WINDOWS #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #else #include #include #endif CPPTRACE_BEGIN_NAMESPACE namespace detail { bool isatty(int fd) { #if IS_WINDOWS return _isatty(fd); #else return ::isatty(fd); #endif } int fileno(std::FILE* stream) { #if IS_WINDOWS return _fileno(stream); #else return ::fileno(stream); #endif } void enable_virtual_terminal_processing_if_needed() noexcept { // enable colors / ansi processing if necessary #if IS_WINDOWS // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4; #endif HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; if(hOut == INVALID_HANDLE_VALUE) return; if(!GetConsoleMode(hOut, &dwMode)) return; if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return; #endif } bool directory_exists(cstring_view path) { #if IS_WINDOWS DWORD dwAttrib = GetFileAttributesA(path.c_str()); return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); #else struct stat sb; return stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode); #endif } } CPPTRACE_END_NAMESPACE cpptrace-1.0.4/src/utils/utils.hpp000066400000000000000000000261561504061443700171330ustar00rootroot00000000000000#ifndef UTILS_HPP #define UTILS_HPP #include #include #include #include #include #include #include #include #include #include #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/optional.hpp" #include "utils/result.hpp" CPPTRACE_BEGIN_NAMESPACE namespace detail { bool isatty(int fd); int fileno(std::FILE* stream); void enable_virtual_terminal_processing_if_needed() noexcept; inline std::vector split(const std::string& str, const std::string& delims) { std::vector vec; std::size_t old_pos = 0; std::size_t pos = 0; while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) { vec.emplace_back(str.substr(old_pos, pos - old_pos)); old_pos = pos + 1; } vec.emplace_back(str.substr(old_pos)); return vec; } template std::string join(const C& container, const std::string& delim) { auto iter = std::begin(container); auto end = std::end(container); std::string str; if(std::distance(iter, end) > 0) { str += *iter; while(++iter != end) { str += delim; str += *iter; } } return str; } // closest value in a sorted range such that *it <= value template ForwardIt first_less_than_or_equal(ForwardIt begin, ForwardIt end, const T& value) { auto it = std::upper_bound(begin, end, value); // it is first > value, we want first <= value if(it != begin) { return --it; } return end; } // closest value in a sorted range such that *it <= value template ForwardIt first_less_than_or_equal(ForwardIt begin, ForwardIt end, const T& value, Compare compare) { auto it = std::upper_bound(begin, end, value, compare); // it is first > value, we want first <= value if(it != begin) { return --it; } return end; } constexpr const char* const whitespace = " \t\n\r\f\v"; inline std::string trim(const std::string& str) { if(str.empty()) { return ""; } const std::size_t left = str.find_first_not_of(whitespace); const std::size_t right = str.find_last_not_of(whitespace) + 1; return str.substr(left, right - left); } inline bool starts_with(const std::string& str, const std::string& prefix) { return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; } inline bool is_little_endian() { std::uint16_t num = 0x1; const auto* ptr = (std::uint8_t*)# return ptr[0] == 1; } // Modified from // https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c template struct byte_swapper; template struct byte_swapper { T operator()(T val) { return val; } }; template struct byte_swapper { T operator()(T val) { return (((val >> 8) & 0xff) | ((val & 0xff) << 8)); } }; template struct byte_swapper { T operator()(T val) { return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); } }; template struct byte_swapper { T operator()(T val) { return (((val & 0xff00000000000000ULL) >> 56) | ((val & 0x00ff000000000000ULL) >> 40) | ((val & 0x0000ff0000000000ULL) >> 24) | ((val & 0x000000ff00000000ULL) >> 8 ) | ((val & 0x00000000ff000000ULL) << 8 ) | ((val & 0x0000000000ff0000ULL) << 24) | ((val & 0x000000000000ff00ULL) << 40) | ((val & 0x00000000000000ffULL) << 56)); } }; template::value, int>::type = 0> T byteswap(T value) { return byte_swapper{}(value); } template< typename T, typename std::enable_if::value && !std::is_signed::value, int>::type = 0 > constexpr bool is_positive_power_of_two(T value) { return (value != 0) && (value & (value - 1)) == 0; } template< typename T, typename std::enable_if::value && std::is_signed::value, int>::type = 0 > bool is_positive_power_of_two(T value) { if(value < 0) { return false; } return is_positive_power_of_two(static_cast::type>(value)); } constexpr unsigned n_digits(unsigned value) noexcept { return value < 10 ? 1 : 1 + n_digits(value / 10); } #if defined(__GNUC__) && (__GNUC__ < 5) && !defined(__clang__) template using is_trivially_copyable = std::is_trivial; #else template using is_trivially_copyable = std::is_trivially_copyable; #endif // TODO: Re-evaluate use of off_t template< typename T, typename std::enable_if< std::is_standard_layout::value && is_trivially_copyable::value, int >::type = 0 > Result load_bytes(std::FILE* object_file, off_t offset) { T object; if(std::fseek(object_file, offset, SEEK_SET) != 0) { return internal_error("fseek error"); } if(std::fread(&object, sizeof(T), 1, object_file) != 1) { return internal_error("fread error"); } return object; } // shamelessly stolen from stackoverflow bool directory_exists(cstring_view path); inline std::string basename(cstring_view path, bool maybe_windows = false) { // Assumes no trailing /'s auto pos = path.find_last_of(maybe_windows ? "/\\" : "/"); if(pos == cstring_view::npos) { return std::string(path); } else { return std::string(path.substr(pos + 1)); } } // A way to cast to unsigned long long without "warning: useless cast to type" template unsigned long long to_ull(T t) { return static_cast(t); } template frame_ptr to_frame_ptr(T t) { return static_cast(t); } // A way to cast to U without "warning: useless cast to type" template U to(V v) { return static_cast(v); } template T exchange(T& obj, U&& value) { T old = std::move(obj); obj = std::forward(value); return old; } template using void_t = void; struct monostate {}; // TODO: Rework some stuff here. Not sure deleters should be optional or moved. // Also allow file_wrapper file = std::fopen(object_path.c_str(), "rb"); template< typename T, typename D, // Note: Previously checked if D was invocable and returned void but this kept causing problems for MSVC // == 19.38-specific msvc bug https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565 // <= 19.23 msvc also appears to fail (but for a different reason https://godbolt.org/z/6Y5EvdWPK) // <= 19.39 msvc also has trouble with it for different reasons https://godbolt.org/z/aPPPT7z3z typename std::enable_if< std::is_standard_layout::value && is_trivially_copyable::value, int >::type = 0, typename std::enable_if< std::is_nothrow_move_constructible::value, int >::type = 0 > class raii_wrapper { T obj; optional deleter; public: raii_wrapper(T obj, D deleter) : obj(obj), deleter(deleter) {} raii_wrapper(raii_wrapper&& other) noexcept : obj(std::move(other.obj)), deleter(std::move(other.deleter)) { other.deleter = nullopt; } raii_wrapper(const raii_wrapper&) = delete; raii_wrapper& operator=(raii_wrapper&&) = delete; raii_wrapper& operator=(const raii_wrapper&) = delete; ~raii_wrapper() { if(deleter.has_value()) { deleter.unwrap()(obj); } } operator T&() { return obj; } operator const T&() const { return obj; } T& get() { return obj; } const T& get() const { return obj; } }; template raii_wrapper::type, D> raii_wrap(T obj, D deleter) { return raii_wrapper::type, D>(obj, deleter); } inline void file_deleter(std::FILE* ptr) { if(ptr) { fclose(ptr); } } using file_wrapper = raii_wrapper; template auto make_unique(Args&&... args) -> typename std::enable_if::value, std::unique_ptr>::type { return std::unique_ptr(new T(std::forward(args)...)); } template auto make_unique(T&& arg) -> typename std::enable_if::value, std::unique_ptr>::type { return std::unique_ptr(new T(std::forward(arg))); } template class maybe_owned { std::unique_ptr owned; T* ptr; public: maybe_owned(T* ptr) : ptr(ptr) {} maybe_owned(std::unique_ptr&& owned) : owned(std::move(owned)), ptr(this->owned.get()) {} T* operator->() { return ptr; } T& operator*() { return *ptr; } T* get() { return ptr; } }; template class scope_guard { F f; bool active; public: template< typename G, typename std::enable_if::type, scope_guard>::value, int>::type = 0 > scope_guard(G&& f) : f(std::forward(f)), active(true) {} ~scope_guard() { if(active) { f(); } } scope_guard(const scope_guard&) = delete; scope_guard(scope_guard&& other) : f(std::move(other.f)), active(exchange(other.active, false)) {} scope_guard& operator=(const scope_guard&) = delete; scope_guard& operator=(scope_guard&& other) { f = std::move(other.f); active = exchange(other.active, false); return *this; } }; template NODISCARD auto scope_exit(F&& f) -> scope_guard { return scope_guard(std::forward(f)); } } CPPTRACE_END_NAMESPACE #endif cpptrace-1.0.4/test/000077500000000000000000000000001504061443700143005ustar00rootroot00000000000000cpptrace-1.0.4/test/BUILD.bazel000066400000000000000000000031041504061443700161540ustar00rootroot00000000000000cc_test( name = "unittest", deps = [ "//:cpptrace", "@googletest//:gtest", "@googletest//:gtest_main" ], srcs = [ "unit/main.cpp", "unit/tracing/common.hpp", "unit/tracing/raw_trace.cpp", "unit/tracing/object_trace.cpp", "unit/tracing/stacktrace.cpp", "unit/tracing/from_current.cpp", "unit/tracing/traced_exception.cpp", "unit/internals/optional.cpp", "unit/internals/result.cpp", "unit/internals/string_utils.cpp", "unit/internals/general.cpp", "unit/lib/formatting.cpp", "unit/lib/nullable.cpp" ], local_defines = [ "CPPTRACE_NO_TEST_SNIPPETS" ], linkstatic = 1, ) # Disabled until https://github.com/bazelbuild/bazel/pull/19940 # cc_test( # name = "unittest_module", # deps = [ # "//:cpptrace_module", # "@googletest//:gtest", # "@googletest//:gtest_main" # ], # srcs = [ # "unit/main.cpp", # "unit/tracing/common.hpp", # "unit/tracing/raw_trace.cpp", # "unit/tracing/object_trace.cpp", # "unit/tracing/stacktrace.cpp", # "unit/tracing/from_current.cpp", # "unit/tracing/traced_exception.cpp", # "unit/internals/optional.cpp", # "unit/internals/result.cpp", # "unit/internals/string_utils.cpp", # "unit/internals/general.cpp", # "unit/lib/formatting.cpp", # "unit/lib/nullable.cpp" # ], # local_defines = [ # "CPPTRACE_NO_TEST_SNIPPETS" # ], # linkstatic = 1, # ) cpptrace-1.0.4/test/CMakeLists.txt000066400000000000000000000126511504061443700170450ustar00rootroot00000000000000include(CTest) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set( warning_options ${warning_options} $<$:-Wno-infinite-recursion> ) macro(add_test_dependencies exec_name) target_compile_features(${exec_name} PRIVATE cxx_std_11) target_link_libraries(${exec_name} PRIVATE ${target_name}) target_compile_options(${exec_name} PRIVATE ${warning_options}) target_compile_options(${exec_name} PRIVATE ${debug}) if(NOT CPPTRACE_BUILD_NO_SYMBOLS AND CPPTRACE_BUILD_TESTING_SPLIT_DWARF) target_compile_options(${exec_name} PRIVATE -gsplit-dwarf) endif() if(NOT CPPTRACE_BUILD_NO_SYMBOLS AND NOT (CPPTRACE_BUILD_TESTING_DWARF_VERSION STREQUAL "0")) target_compile_options(${exec_name} PRIVATE -gdwarf-${CPPTRACE_BUILD_TESTING_DWARF_VERSION}) endif() # # Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE AND NOT CPPTRACE_BUILD_NO_SYMBOLS) check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4) if(HAS_DWARF4) target_compile_options(${exec_name} PRIVATE "$<$:-gdwarf-4>") endif() endif() # TODO: add debug info for mingw clang? if(CPPTRACE_BUILD_TEST_RDYNAMIC) set_property(TARGET ${exec_name} PROPERTY ENABLE_EXPORTS ON) endif() endmacro() add_executable(integration integration.cpp) add_executable(demo demo.cpp) add_executable(c_demo ctrace_demo.c) add_executable(link_test link_test.cpp) add_test_dependencies(integration) add_test_dependencies(demo) add_test_dependencies(c_demo) add_test_dependencies(link_test) if(HAS_CXX20_MODULES) add_executable(integration_modules integration.cpp) add_test_dependencies(integration_modules) add_executable(link_test_modules link_test.cpp) add_test_dependencies(link_test_modules) endif() if(UNIX) add_executable(signal_demo signal_demo.cpp) target_compile_features(signal_demo PRIVATE cxx_std_11) target_link_libraries(signal_demo PRIVATE ${target_name}) target_compile_options(signal_demo PRIVATE ${debug}) if(NOT CPPTRACE_BUILD_NO_SYMBOLS AND CPPTRACE_BUILD_TESTING_SPLIT_DWARF) target_compile_options(signal_demo PRIVATE -gsplit-dwarf) endif() if(NOT CPPTRACE_BUILD_NO_SYMBOLS AND NOT (CPPTRACE_BUILD_TESTING_DWARF_VERSION STREQUAL "0")) target_compile_options(signal_demo PRIVATE -gdwarf-${CPPTRACE_BUILD_TESTING_DWARF_VERSION}) endif() add_executable(signal_tracer signal_tracer.cpp) target_compile_features(signal_tracer PRIVATE cxx_std_11) target_link_libraries(signal_tracer PRIVATE ${target_name}) target_compile_options(signal_tracer PRIVATE ${debug}) endif() function(test_cpptrace) cmake_parse_arguments( CPPTRACE "" # No-arg options "TEST_NAME" # Single-arg options "DEFINE" # Multi-arg options ${ARGN} # Parameters to parse ) add_executable( "${CPPTRACE_TEST_NAME}" unit/main.cpp unit/tracing/raw_trace.cpp unit/tracing/object_trace.cpp unit/tracing/stacktrace.cpp unit/tracing/from_current.cpp unit/tracing/from_current_try_catch.cpp unit/tracing/try_catch.cpp unit/tracing/traced_exception.cpp unit/tracing/rethrow.cpp unit/internals/optional.cpp unit/internals/lru_cache.cpp unit/internals/result.cpp unit/internals/string_utils.cpp unit/internals/general.cpp unit/internals/span.cpp unit/internals/string_view.cpp unit/lib/formatting.cpp unit/lib/nullable.cpp unit/lib/prune_symbol.cpp ) target_compile_features("${CPPTRACE_TEST_NAME}" PRIVATE cxx_std_11) target_link_libraries("${CPPTRACE_TEST_NAME}" PRIVATE ${target_name} GTest::gtest_main GTest::gmock_main) target_compile_definitions("${CPPTRACE_TEST_NAME}" PRIVATE ${CPPTRACE_DEFINE}) target_compile_options("${CPPTRACE_TEST_NAME}" PRIVATE ${warning_options} $<$>:-Wno-pedantic -Wno-attributes>) target_compile_options("${CPPTRACE_TEST_NAME}" PRIVATE ${debug}) if(NOT CPPTRACE_BUILD_NO_SYMBOLS AND CPPTRACE_BUILD_TESTING_SPLIT_DWARF) target_compile_options("${CPPTRACE_TEST_NAME}" PRIVATE -gsplit-dwarf) endif() if(NOT CPPTRACE_BUILD_NO_SYMBOLS AND NOT (CPPTRACE_BUILD_TESTING_DWARF_VERSION STREQUAL "0")) target_compile_options("${CPPTRACE_TEST_NAME}" PRIVATE -gdwarf-${CPPTRACE_BUILD_TESTING_DWARF_VERSION}) endif() if(CPPTRACE_SANITIZER_BUILD) target_compile_definitions("${CPPTRACE_TEST_NAME}" PRIVATE CPPTRACE_SANITIZER_BUILD) endif() if(CPPTRACE_BUILD_NO_SYMBOLS) target_compile_definitions("${CPPTRACE_TEST_NAME}" PRIVATE CPPTRACE_BUILD_NO_SYMBOLS) endif() target_include_directories("${CPPTRACE_TEST_NAME}" PRIVATE ../src) add_test(NAME ${CPPTRACE_TEST_NAME} COMMAND ${CPPTRACE_TEST_NAME}) endfunction() # primarily a workaround for github actions issue https://github.com/actions/runner-images/issues/8659 if(NOT CPPTRACE_SKIP_UNIT) if(CPPTRACE_USE_EXTERNAL_GTEST) find_package(GTest) else() include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY "https://github.com/google/googletest.git" GIT_TAG 58d77fa8070e8cec2dc1ed015d66b454c8d78850 # v1.12.1, last to support C++11 ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) endif() test_cpptrace(TEST_NAME unittest) if(HAS_CXX20_MODULES) test_cpptrace( TEST_NAME unittest_module DEFINE TEST_MODULE ) endif() endif() cpptrace-1.0.4/test/add_subdirectory-integration/000077500000000000000000000000001504061443700221475ustar00rootroot00000000000000cpptrace-1.0.4/test/add_subdirectory-integration/CMakeLists.txt000066400000000000000000000006561504061443700247160ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.8) project(demo_project VERSION 0.0.1 LANGUAGES CXX) add_executable(main main.cpp) add_subdirectory(cpptrace) target_link_libraries(main cpptrace::cpptrace) target_compile_features(main PRIVATE cxx_std_11) if(WIN32) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endif() cpptrace-1.0.4/test/add_subdirectory-integration/main.cpp000066400000000000000000000002271504061443700236000ustar00rootroot00000000000000#include void trace() { cpptrace::generate_trace().print(); } void foo(int) { trace(); } int main() { foo(0); } cpptrace-1.0.4/test/ctrace_demo.c000066400000000000000000000020451504061443700167120ustar00rootroot00000000000000#include #include #include #include void trace(void) { ctrace_raw_trace raw_trace = ctrace_generate_raw_trace(1, INT_MAX); ctrace_object_trace obj_trace = ctrace_resolve_raw_trace_to_object_trace(&raw_trace); ctrace_stacktrace trace = ctrace_resolve_object_trace(&obj_trace); ctrace_print_stacktrace(&trace, stdout, 1); ctrace_free_stacktrace(&trace); ctrace_free_object_trace(&obj_trace); ctrace_free_raw_trace(&raw_trace); assert(raw_trace.frames == NULL && obj_trace.count == 0); } void bar(int n) { if(n == 0) { trace(); } else { bar(n - 1); } } void foo(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { (void)a, (void)b, (void)c, (void)d, (void)e, (void)f, (void)g, (void)h, (void)i, (void)j; bar(1); } void function_two(int a, float b) { (void)a, (void)b; foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } void function_one(int a) { (void)a; function_two(0, 0); } int main(void) { function_one(0); } cpptrace-1.0.4/test/demo.cpp000066400000000000000000000017131504061443700157320ustar00rootroot00000000000000#include #include #include #include #include #include void fail() { std::cout << "Throwing an exception from:" << std::endl; cpptrace::generate_trace().print(); throw std::runtime_error("foobar"); } void foo(int n) { if(n == 0) { fail(); } else { foo(n - 1); } } template void foo(int, Args... args) { foo(args...); } void function_two(int, float) { foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } void function_one(int) { function_two(0, 0); } int main() { cpptrace::absorb_trace_exceptions(false); cpptrace::use_default_stderr_logger(); cpptrace::register_terminate_handler(); CPPTRACE_TRY { function_one(0); } CPPTRACE_CATCH(const std::exception& e) { std::cout << "Exception caught: " << e.what() << std::endl; cpptrace::from_current_exception().print(); } } cpptrace-1.0.4/test/expected/000077500000000000000000000000001504061443700161015ustar00rootroot00000000000000cpptrace-1.0.4/test/expected/linux.libdl.txt000066400000000000000000000046651504061443700211010ustar00rootroot00000000000000./integration||4294967295||trace() ./integration||4294967295||www(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator > const&, std::vector, std::allocator >*, std::allocator, std::allocator >*> >&&) ./integration||4294967295||jjj(void (* const*)(float)) ./integration||4294967295||iii(Foo::Bar) ./integration||4294967295||hhh(int (* (*) [10]) [20]) ./integration||4294967295||ggg(int const* const*) ./integration||4294967295||fff(int (S::*)(float) const volatile &&) ./integration||4294967295||eee(int (*(* const* volatile (*) [10])())(float)) ./integration||4294967295||ddd(int (* (*) [10])()) ./integration||4294967295||ccc(int (*) [5][6][7][8]) ./integration||4294967295||bbb(int (* const (&) [5])(float, int const&)) ./integration||4294967295||aaa(int (&) [5]) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||foo(int) ./integration||4294967295||void foo(int, int) ./integration||4294967295||void foo(int, int, int) ./integration||4294967295||void foo(int, int, int, int) ./integration||4294967295||void foo(int, int, int, int, int) ./integration||4294967295||void foo(int, int, int, int, int, int) ./integration||4294967295||void foo(int, int, int, int, int, int, int) ./integration||4294967295||void foo(int, int, int, int, int, int, int, int) ./integration||4294967295||void foo(int, int, int, int, int, int, int, int, int) ./integration||4294967295||void foo(int, int, int, int, int, int, int, int, int, int) ./integration||4294967295||function_two(int, float) ./integration||4294967295||function_one(int) ./integration||4294967295||main /lib/x86_64-linux-gnu/libc.so.6||4294967295|| /lib/x86_64-linux-gnu/libc.so.6||4294967295||__libc_start_main ./integration||4294967295||_startcpptrace-1.0.4/test/expected/linux.txt000066400000000000000000000046441504061443700200110ustar00rootroot00000000000000test/integration.cpp||23||trace() test/integration.cpp||33||www(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator > const&, std::vector, std::allocator >*, std::allocator, std::allocator >*> >&&) test/integration.cpp||37||jjj(void (* const*)(float)) test/integration.cpp||45||iii(Foo::Bar) test/integration.cpp||55||hhh(int (* (*) [10]) [20]) test/integration.cpp||59||ggg(int const* const*) test/integration.cpp||63||fff(int (S::*)(float) const volatile &&) test/integration.cpp||68||eee(int (*(* const* volatile (*) [10])())(float)) test/integration.cpp||72||ddd(int (* (*) [10])()) test/integration.cpp||76||ccc(int (*) [5][6][7][8]) test/integration.cpp||80||bbb(int (* const (&) [5])(float, int const&)) test/integration.cpp||85||aaa(int (&) [5]) test/integration.cpp||94||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||106||void foo(int, int) test/integration.cpp||106||void foo(int, int, int) test/integration.cpp||106||void foo(int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/integration.cpp||112||function_two(int, float) test/integration.cpp||118||function_one(int) test/integration.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||4294967295||cpptrace-1.0.4/test/expected/macos.clang.libdl.txt000066400000000000000000000047611504061443700221240ustar00rootroot00000000000000build/integration||4294967295||trace() build/integration||4294967295||www(std::__1::basic_string, std::__1::allocator>&&, std::__1::basic_string, std::__1::allocator> const&, std::__1::vector, std::__1::allocator>*, std::__1::allocator, std::__1::allocator>*>>&&) build/integration||4294967295||jjj(void (* const*)(float)) build/integration||4294967295||iii(Foo::Bar) build/integration||4294967295||hhh(int (* (*) [10]) [20]) build/integration||4294967295||ggg(int const* const*) build/integration||4294967295||fff(int (S::*)(float) const volatile &&) build/integration||4294967295||eee(int (* (* const* volatile (*) [10])())(float)) build/integration||4294967295||ddd(int (* (*) [10])()) build/integration||4294967295||ccc(int (*) [5][6][7][8]) build/integration||4294967295||bbb(int (* const (&) [5])(float, int const&)) build/integration||4294967295||aaa(int (&) [5]) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||void foo(int, int) build/integration||4294967295||void foo(int, int, int) build/integration||4294967295||void foo(int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int, int, int, int) build/integration||4294967295||function_two(int, float) build/integration||4294967295||function_one(int) build/integration||4294967295||main /usr/lib/dyld||4294967295||startcpptrace-1.0.4/test/expected/macos.clang.txt000066400000000000000000000047021504061443700210320ustar00rootroot00000000000000test/integration.cpp||23||trace() test/integration.cpp||33||www(std::__1::basic_string, std::__1::allocator>&&, std::__1::basic_string, std::__1::allocator> const&, std::__1::vector, std::__1::allocator>*, std::__1::allocator, std::__1::allocator>*>>&&) test/integration.cpp||37||jjj(void (* const*)(float)) test/integration.cpp||45||iii(Foo::Bar) test/integration.cpp||55||hhh(int (* (*) [10]) [20]) test/integration.cpp||59||ggg(int const* const*) test/integration.cpp||63||fff(int (S::*)(float) const volatile &&) test/integration.cpp||68||eee(int (* (* const* volatile (*) [10])())(float)) test/integration.cpp||72||ddd(int (* (*) [10])()) test/integration.cpp||76||ccc(int (*) [5][6][7][8]) test/integration.cpp||80||bbb(int (* const (&) [5])(float, int const&)) test/integration.cpp||85||aaa(int (&) [5]) test/integration.cpp||94||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||106||void foo(int, int) test/integration.cpp||106||void foo(int, int, int) test/integration.cpp||106||void foo(int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/integration.cpp||112||function_two(int, float) test/integration.cpp||118||function_one(int) test/integration.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||4294967295||cpptrace-1.0.4/test/expected/macos.gcc.addr2line.txt000066400000000000000000000046401504061443700223460ustar00rootroot00000000000000test/integration.cpp||23||trace() test/integration.cpp||33||www(std::__cxx11::basic_string, std::allocator>&&, std::__cxx11::basic_string, std::allocator> const&, std::vector, std::allocator>*, std::allocator, std::allocator>*>>&&) test/integration.cpp||37||jjj(void (* const*)(float)) test/integration.cpp||45||iii(Foo::Bar) test/integration.cpp||55||hhh(int (* (*) [10]) [20]) test/integration.cpp||59||ggg(int const* const*) test/integration.cpp||63||fff(int (S::*)(float) const volatile &&) test/integration.cpp||68||eee(int (* (* const* volatile (*) [10])())(float)) test/integration.cpp||72||ddd(int (* (*) [10])()) test/integration.cpp||76||ccc(int (*) [5][6][7][8]) test/integration.cpp||80||bbb(int (* const (&) [5])(float, int const&)) test/integration.cpp||85||aaa(int (&) [5]) test/integration.cpp||94||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||106||void foo(int, int) test/integration.cpp||106||void foo(int, int, int) test/integration.cpp||106||void foo(int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/integration.cpp||112||function_two(int, float) test/integration.cpp||118||function_one(int) test/integration.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||4294967295||cpptrace-1.0.4/test/expected/macos.gcc.libdl.txt000066400000000000000000000047231504061443700215720ustar00rootroot00000000000000build/integration||4294967295||trace() build/integration||4294967295||www(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator > const&, std::vector, std::allocator >*, std::allocator, std::allocator >*> >&&) build/integration||4294967295||jjj(void (* const*)(float)) build/integration||4294967295||iii(Foo::Bar) build/integration||4294967295||hhh(int (* (*) [10]) [20]) build/integration||4294967295||ggg(int const* const*) build/integration||4294967295||fff(int (S::*)(float) const volatile &&) build/integration||4294967295||eee(int (*(* const* volatile (*) [10])())(float)) build/integration||4294967295||ddd(int (* (*) [10])()) build/integration||4294967295||ccc(int (*) [5][6][7][8]) build/integration||4294967295||bbb(int (* const (&) [5])(float, int const&)) build/integration||4294967295||aaa(int (&) [5]) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||foo(int) build/integration||4294967295||void foo(int, int) build/integration||4294967295||void foo(int, int, int) build/integration||4294967295||void foo(int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int, int, int) build/integration||4294967295||void foo(int, int, int, int, int, int, int, int, int, int) build/integration||4294967295||function_two(int, float) build/integration||4294967295||function_one(int) build/integration||4294967295||main /usr/lib/dyld||4294967295||startcpptrace-1.0.4/test/expected/macos.gcc.txt000066400000000000000000000046441504061443700205070ustar00rootroot00000000000000test/integration.cpp||23||trace() test/integration.cpp||33||www(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator > const&, std::vector, std::allocator >*, std::allocator, std::allocator >*> >&&) test/integration.cpp||37||jjj(void (* const*)(float)) test/integration.cpp||45||iii(Foo::Bar) test/integration.cpp||55||hhh(int (* (*) [10]) [20]) test/integration.cpp||59||ggg(int const* const*) test/integration.cpp||63||fff(int (S::*)(float) const volatile &&) test/integration.cpp||68||eee(int (*(* const* volatile (*) [10])())(float)) test/integration.cpp||72||ddd(int (* (*) [10])()) test/integration.cpp||76||ccc(int (*) [5][6][7][8]) test/integration.cpp||80||bbb(int (* const (&) [5])(float, int const&)) test/integration.cpp||85||aaa(int (&) [5]) test/integration.cpp||94||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||106||void foo(int, int) test/integration.cpp||106||void foo(int, int, int) test/integration.cpp||106||void foo(int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/integration.cpp||112||function_two(int, float) test/integration.cpp||118||function_one(int) test/integration.cpp||125||main ./csu/../sysdeps/nptl/libc_start_call_main.h||58||__libc_start_call_main ./csu/../csu/libc-start.c||392||__libc_start_main_impl ./test||4294967295||cpptrace-1.0.4/test/expected/windows.gcc.txt000066400000000000000000000047771504061443700211060ustar00rootroot00000000000000test/integration.cpp||23||trace() test/integration.cpp||33||www(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator > const&, std::vector, std::allocator >*, std::allocator, std::allocator >*> >&&) test/integration.cpp||37||jjj(void (* const*)(float)) test/integration.cpp||45||iii(Foo::Bar) test/integration.cpp||55||hhh(int (* (*) [10]) [20]) test/integration.cpp||59||ggg(int const* const*) test/integration.cpp||63||fff(int (S::*)(float) const volatile &&) test/integration.cpp||68||eee(int (*(* const* volatile (*) [10])())(float)) test/integration.cpp||72||ddd(int (* (*) [10])()) test/integration.cpp||76||ccc(int (*) [5][6][7][8]) test/integration.cpp||80||bbb(int (* const (&) [5])(float, int const&)) test/integration.cpp||85||aaa(int (&) [5]) test/integration.cpp||94||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||98||foo(int) test/integration.cpp||106||void foo(int, int) test/integration.cpp||106||void foo(int, int, int) test/integration.cpp||106||void foo(int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int) test/integration.cpp||106||void foo(int, int, int, int, int, int, int, int, int, int) test/integration.cpp||112||function_two(int, float) test/integration.cpp||118||function_one(int) test/integration.cpp||125||main C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||272||__tmainCRTStartup C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c||193||mainCRTStartup ||4294967295||BaseThreadInitThunk ||4294967295||RtlUserThreadStartcpptrace-1.0.4/test/expected/windows.txt000066400000000000000000000050451504061443700203400ustar00rootroot00000000000000test\integration.cpp||23||trace() test\integration.cpp||33||www(std::basic_string, std::allocator >*, std::basic_string, std::allocator >&, std::vector, std::allocator > *, std::allocator, std::allocator > *> >*) test\integration.cpp||37||jjj(void(*(*))(float)) test\integration.cpp||45||iii(Foo::Bar) test\integration.cpp||55||hhh(int(*(*)[10])[20]) test\integration.cpp||59||ggg(int**) test\integration.cpp||63||fff(int(S::*)(float)) test\integration.cpp||68||eee(int(*(*(*(*)[10]))())(float)) test\integration.cpp||72||ddd(int(*(*)[10])()) test\integration.cpp||76||ccc(int(*)[5][6][7][8]) test\integration.cpp||80||bbb(int(*(&)[5])(float, int&)) test\integration.cpp||85||aaa(int(&)[5]) test\integration.cpp||94||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||98||foo(int) test\integration.cpp||106||foo(int, int) test\integration.cpp||106||foo(int, int, int) test\integration.cpp||106||foo(int, int, int, int) test\integration.cpp||106||foo(int, int, int, int, int) test\integration.cpp||106||foo(int, int, int, int, int, int) test\integration.cpp||106||foo(int, int, int, int, int, int, int) test\integration.cpp||106||foo(int, int, int, int, int, int, int, int) test\integration.cpp||106||foo(int, int, int, int, int, int, int, int, int) test\integration.cpp||106||foo(int, int, int, int, int, int, int, int, int, int) test\integration.cpp||112||function_two(int, float) test\integration.cpp||118||function_one(int) test\integration.cpp||125||main() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||79||invoke_main() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||288||__scrt_common_main_seh() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl||331||__scrt_common_main() D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp||17||mainCRTStartup(void*) ||4294967295||BaseThreadInitThunk ||4294967295||RtlUserThreadStartcpptrace-1.0.4/test/fetchcontent-integration/000077500000000000000000000000001504061443700213055ustar00rootroot00000000000000cpptrace-1.0.4/test/fetchcontent-integration/CMakeLists.txt000066400000000000000000000012071504061443700240450ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.8) project(demo_project VERSION 0.0.1 LANGUAGES CXX) add_executable(main main.cpp) set(CPPTRACE_TAG "" CACHE STRING "cpptrace git tag") include(FetchContent) FetchContent_Declare( cpptrace GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git GIT_TAG ${CPPTRACE_TAG} ) FetchContent_MakeAvailable(cpptrace) target_link_libraries(main cpptrace::cpptrace) target_compile_features(main PRIVATE cxx_std_11) if(WIN32) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endif() cpptrace-1.0.4/test/fetchcontent-integration/main.cpp000066400000000000000000000002271504061443700227360ustar00rootroot00000000000000#include void trace() { cpptrace::generate_trace().print(); } void foo(int) { trace(); } int main() { foo(0); } cpptrace-1.0.4/test/findpackage-integration/000077500000000000000000000000001504061443700210555ustar00rootroot00000000000000cpptrace-1.0.4/test/findpackage-integration/CMakeLists.txt000066400000000000000000000006631504061443700236220ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.8) project(demo_project VERSION 0.0.1 LANGUAGES CXX) add_executable(main main.cpp) find_package(cpptrace REQUIRED) target_link_libraries(main cpptrace::cpptrace) target_compile_features(main PRIVATE cxx_std_11) if(WIN32) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endif() cpptrace-1.0.4/test/findpackage-integration/main.cpp000066400000000000000000000002271504061443700225060ustar00rootroot00000000000000#include void trace() { cpptrace::generate_trace().print(); } void foo(int) { trace(); } int main() { foo(0); } cpptrace-1.0.4/test/integration.cpp000066400000000000000000000052311504061443700173300ustar00rootroot00000000000000#include #include #include #include #include #include #ifdef TEST_MODULE import cpptrace; #else #include #endif // preserve line numbers from when the integration test files were generated #line 10 std::string normalize_filename(std::string name) { if(name.find('/') == 0 || (name.find(':') == 1 && std::isupper(name[0]))) { // build/integration if the file is really an object name resolved by libdl auto p = std::min({name.rfind("test/"), name.rfind("test\\"), name.rfind("build/integration")}); return p == std::string::npos ? name : name.substr(p); } else { return name; } } void custom_print(const cpptrace::stacktrace&); void trace() { auto trace = cpptrace::generate_trace(); if(trace.empty()) { std::cerr << "" << std::endl; } custom_print(trace); } // padding to avoid upsetting existing trace expected files void www(std::string&&, const std::string&, std::vector&&) { trace(); } void jjj(void(*const[5])(float)) { www(std::string{}, "", {}); } namespace Foo { struct Bar {}; } void iii(Foo::Bar) { jjj(nullptr); } struct S { int foo(float) const volatile && { return 1; } }; void hhh(int(*(*)[10])[20]) { iii(Foo::Bar{}); } void ggg(const int * const *) { hhh(nullptr); } void fff(int(S::*)(float) const volatile &&) { ggg(nullptr); } //void eee(int(*(*(*)[10])())(float)) { void eee(int(*(* const * volatile(*)[10])())(float)) { fff(&S::foo); } void ddd(int(*(*)[10])()) { eee(nullptr); } void ccc(int(*)[5][6][7][8]) { ddd(nullptr); } void bbb(int(*const (&)[5])(float, const int&)) { ccc(nullptr); } void aaa(int(&)[5]) { int(*const (arr)[5])(float, const int&) = {}; bbb(arr); } int x; void foo(int n) { if(n == 0) { x = 0; int arr[5]; aaa(arr); x = 0; } else { x = 0; foo(n - 1); x = 0; } } template void foo(int, Args... args) { x = 0; foo(args...); x = 0; } void function_two(int, float) { x = 0; foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); x = 0; } void function_one(int) { x = 0; function_two(0, 0); x = 0; } int main() { x = 0; cpptrace::absorb_trace_exceptions(false); function_one(0); x = 0; } void custom_print(const cpptrace::stacktrace& trace) { for(const auto& frame : trace) { std::cout << normalize_filename(frame.filename) << "||" << frame.line.value() << "||" << frame.symbol << std::endl; } } cpptrace-1.0.4/test/jank/000077500000000000000000000000001504061443700152235ustar00rootroot00000000000000cpptrace-1.0.4/test/jank/Dockerfile000066400000000000000000000020001504061443700172050ustar00rootroot00000000000000FROM ubuntu:24.10 RUN apt update RUN apt install -y curl git git-lfs zip build-essential entr libssl-dev libdouble-conversion-dev pkg-config ninja-build cmake zlib1g-dev libffi-dev clang libclang-dev llvm llvm-dev libzip-dev libbz2-dev doctest-dev gcc g++ libgc-dev RUN apt install -y vim gdb lldb file valgrind # Setup a user RUN groupadd cpptracegroup RUN useradd -m -g cpptracegroup -s /bin/bash cpptrace RUN mkdir /opt/work/ && chown cpptrace:cpptracegroup /opt/work/ USER cpptrace WORKDIR /opt/ WORKDIR /opt/work # RUN git clone --recurse-submodules https://github.com/jank-lang/jank.git # WORKDIR /opt/work/jank/compiler+runtime # RUN git checkout 5668b16 # RUN CC=clang CXX=clang++ ./bin/configure -GNinja -DCMAKE_BUILD_TYPE=Debug # RUN ./bin/compile # WORKDIR /opt/work/jank # WORKDIR /opt/work/jank/compiler+runtime COPY ./entry.sh . ENTRYPOINT ["./entry.sh"] # ENTRYPOINT ["/bin/bash"] # podman build -t cpptrace-container . # podman run --user=cpptrace --cap-drop=all --network none -it cpptrace-container cpptrace-1.0.4/test/jank/Makefile000066400000000000000000000024751504061443700166730ustar00rootroot00000000000000default: help # The general philosophy and functionality of this makefile is shamelessly stolen from compiler explorer help: # with thanks to Ben Rady @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' PODMAN=podman data/.built: Dockerfile entry.sh $(PODMAN) build -t cpptrace-container . mkdir -p data touch data/.built .PHONY: build build: data/.built ## build the container .PHONY: run run: data/.built data/.cloned ## run the container rm -rfv data/jank/compiler+runtime/third-party/cpptrace/include rm -rfv data/jank/compiler+runtime/third-party/cpptrace/src cp -avp ../../include data/jank/compiler+runtime/third-party/cpptrace/include cp -avp ../../src data/jank/compiler+runtime/third-party/cpptrace/src cp -v ../../CMakeLists.txt data/jank/compiler+runtime/third-party/cpptrace/ $(PODMAN) run \ --user=cpptrace \ --cap-drop=all \ -v $(realpath data/jank):/opt/work/jank:rw \ --tmpfs /opt/work/jank/compiler+runtime/build:rw \ -it \ cpptrace-container .PHONY: clone clone: data/.cloned ## clone code data/.cloned: mkdir -p data cd data && git clone --recurse-submodules https://github.com/jank-lang/jank.git && cd jank && git checkout 5668b16 touch data/.cloned .PHONY: clean clean: ## clean rm -rf data cpptrace-1.0.4/test/jank/entry.sh000066400000000000000000000006301504061443700167170ustar00rootroot00000000000000#!/bin/sh echo "------------------------------------ BUILDING ------------------------------------" cd jank/compiler+runtime CC=clang CXX=clang++ ./bin/configure -GNinja -DCMAKE_BUILD_TYPE=Debug && ./bin/compile cat > test.jank << EOF (ns test) (defn foo [] (throw "meow")) (defn -main [& args] (foo)) (-main) EOF ./build/jank run test.jank # gdb --args ./build/jank run test.jank exec /bin/bash cpptrace-1.0.4/test/link_test.cpp000066400000000000000000000052501504061443700170020ustar00rootroot00000000000000#include #ifdef TEST_MODULE import cpptrace; #else #include #include #include #include #endif // These are just here to make sure all symbols are found int main() { auto r = cpptrace::raw_trace::current(); r = cpptrace::raw_trace::current(0, 100); r.resolve(); r.clear(); r.empty(); auto o = cpptrace::object_trace::current(); o = cpptrace::object_trace::current(0, 100); o.resolve(); o.clear(); o.empty(); auto s = cpptrace::stacktrace::current(); s = cpptrace::stacktrace::current(0, 100); s.print(); s.print(std::cerr); s.print(std::cerr, true); s.print_with_snippets(); s.print_with_snippets(std::cerr); s.print_with_snippets(std::cerr, true); s.to_string(); std::cerr< #include #include int main() { // generate a trace before LoadLibraryA to initialize dbghelp cpptrace::generate_trace().print(); HMODULE lib = LoadLibraryA("mydll.dll"); if (!lib) { std::cerr << "Failed to load DLL\n"; return 1; } auto foo = reinterpret_cast(GetProcAddress(lib, "foo")); if (!foo) { std::cerr << "Failed to get symbol\n"; return 1; } cpptrace::load_symbols_for_file("mydll.dll"); CPPTRACE_TRY { foo(); } CPPTRACE_CATCH(...) { cpptrace::from_current_exception().print(); } FreeLibrary(lib); } cpptrace-1.0.4/test/load-library/mydll.cpp000066400000000000000000000000621504061443700205040ustar00rootroot00000000000000#include "mydll.hpp" void foo() { throw 0; } cpptrace-1.0.4/test/load-library/mydll.hpp000066400000000000000000000000451504061443700205120ustar00rootroot00000000000000#pragma once extern "C" void foo(); cpptrace-1.0.4/test/signal_demo.cpp000066400000000000000000000050371504061443700172720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include void trace() { *(volatile char*)0 = 2; } void bar() { trace(); } void foo() { bar(); } struct pipe_t { union { struct { int read_end; int write_end; }; int data[2]; }; }; static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing"); void handler(int signo, siginfo_t* info, void* context) { const char* message = "SIGSEGV occurred:\n"; write(STDERR_FILENO, message, strlen(message)); cpptrace::frame_ptr buffer[100]; std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 100); pipe_t input_pipe; pipe(input_pipe.data); const pid_t pid = fork(); if(pid == -1) { const char* fork_failure_message = "fork() failed\n"; write(STDERR_FILENO, fork_failure_message, strlen(fork_failure_message)); _exit(1); } if(pid == 0) { // child dup2(input_pipe.read_end, STDIN_FILENO); close(input_pipe.read_end); close(input_pipe.write_end); execl( "signal_tracer", "signal_tracer", nullptr ); const char* exec_failure_message = "exec(signal_tracer) failed: Make sure the signal_tracer executable is in " "the current working directory and the binary's permissions are correct.\n"; write(STDERR_FILENO, exec_failure_message, strlen(exec_failure_message)); _exit(1); } for(std::size_t i = 0; i < count; i++) { cpptrace::safe_object_frame frame; cpptrace::get_safe_object_frame(buffer[i], &frame); write(input_pipe.write_end, &frame, sizeof(frame)); } close(input_pipe.read_end); close(input_pipe.write_end); waitpid(pid, nullptr, 0); _exit(1); } void warmup_cpptrace() { cpptrace::frame_ptr buffer[10]; std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10); cpptrace::safe_object_frame frame; cpptrace::get_safe_object_frame(buffer[0], &frame); } int main() { cpptrace::absorb_trace_exceptions(false); cpptrace::use_default_stderr_logger(); cpptrace::register_terminate_handler(); warmup_cpptrace(); struct sigaction action = { 0 }; action.sa_flags = 0; action.sa_sigaction = &handler; if (sigaction(SIGSEGV, &action, NULL) == -1) { perror("sigaction"); exit(EXIT_FAILURE); } foo(); } cpptrace-1.0.4/test/signal_tracer.cpp000066400000000000000000000010571504061443700176240ustar00rootroot00000000000000#include #include #include #include int main() { cpptrace::object_trace trace; while(true) { cpptrace::safe_object_frame frame; std::size_t res = fread(&frame, sizeof(frame), 1, stdin); if(res == 0) { break; } else if(res != 1) { std::cerr<<"Oops, size mismatch "<:-gdwarf-4>") #target_compile_options(speedtest PRIVATE "$<$:-gdwarf-4>") #target_compile_options(googletest INTERFACE "$<$:-gdwarf-4>") endif() endif() if(SPEEDTEST_DWARF5) check_cxx_compiler_flag("-gdwarf-5" HAS_DWARF5) if(HAS_DWARF5) add_compile_options("$<$:-gdwarf-5>") #target_compile_options(speedtest PRIVATE "$<$:-gdwarf-4>") #target_compile_options(googletest INTERFACE "$<$:-gdwarf-4>") endif() endif() include(FetchContent) FetchContent_Declare( googletest DOWNLOAD_EXTRACT_TIMESTAMP On URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) set(cpptrace_DIR "../../build/foo/lib/cmake/cpptrace") set(libdwarf_DIR "../../build/foo/lib/cmake/libdwarf") set(zstd_DIR "../../build/foo/lib/cmake/zstd") find_package(cpptrace REQUIRED) add_executable(speedtest speedtest.cpp) target_compile_features(speedtest PRIVATE cxx_std_11) target_link_libraries( speedtest PRIVATE GTest::gtest_main cpptrace::cpptrace ) if(WIN32) add_custom_command( TARGET speedtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMAND_EXPAND_LISTS ) endif() cpptrace-1.0.4/test/speedtest/speedtest.cpp000066400000000000000000000003741504061443700210100ustar00rootroot00000000000000// https://github.com/jeremy-rifkin/libassert/issues/43 #include #include #include TEST(TraceTest, trace_test) { ASSERT_THROW((cpptrace::generate_trace().print(), false), std::logic_error); } cpptrace-1.0.4/test/unit/000077500000000000000000000000001504061443700152575ustar00rootroot00000000000000cpptrace-1.0.4/test/unit/internals/000077500000000000000000000000001504061443700172565ustar00rootroot00000000000000cpptrace-1.0.4/test/unit/internals/general.cpp000066400000000000000000000145331504061443700214050ustar00rootroot00000000000000#include #include #include #include #include "utils/utils.hpp" using cpptrace::detail::byteswap; using cpptrace::detail::n_digits; using cpptrace::detail::to; using cpptrace::detail::raii_wrapper; using cpptrace::detail::raii_wrap; using cpptrace::detail::maybe_owned; namespace { TEST(ByteSwapTest, ByteSwapUint8) { uint8_t input = 0x12; uint8_t expected = 0x12; uint8_t result = byteswap(input); EXPECT_EQ(result, expected); } TEST(ByteSwapTest, ByteSwapInt8) { int8_t input = 0x7F; int8_t expected = 0x7F; int8_t result = byteswap(input); EXPECT_EQ(result, expected); int8_t neg_input = to(0x80); int8_t neg_expected = to(0x80); int8_t neg_result = byteswap(neg_input); EXPECT_EQ(neg_result, neg_expected); } TEST(ByteSwapTest, ByteSwapUint16) { uint16_t input = 0x1234; uint16_t expected = 0x3412; uint16_t result = byteswap(input); EXPECT_EQ(result, expected); EXPECT_EQ(byteswap(to(0x0000)), to(0x0000)); EXPECT_EQ(byteswap(to(0xFFFF)), to(0xFFFF)); } TEST(ByteSwapTest, ByteSwapInt16) { int16_t input = 0x1234; int16_t expected = to(0x3412); int16_t result = byteswap(input); EXPECT_EQ(result, expected); int16_t neg_input = to(0xFEFF); int16_t neg_expected = to(0xFFFE); int16_t neg_result = byteswap(neg_input); EXPECT_EQ(neg_result, neg_expected); } TEST(ByteSwapTest, ByteSwapUint32) { uint32_t input = 0x12345678; uint32_t expected = 0x78563412; uint32_t result = byteswap(input); EXPECT_EQ(result, expected); EXPECT_EQ(byteswap(to(0x00000000)), to(0x00000000)); EXPECT_EQ(byteswap(to(0xFFFFFFFF)), to(0xFFFFFFFF)); } TEST(ByteSwapTest, ByteSwapInt32) { int32_t input = 0x12345678; int32_t expected = to(0x78563412); int32_t result = byteswap(input); EXPECT_EQ(result, expected); int32_t neg_input = to(0xFF000000); int32_t neg_expected = to(0x000000FF); int32_t neg_result = byteswap(neg_input); EXPECT_EQ(neg_result, neg_expected); } TEST(ByteSwapTest, ByteSwapUint64) { uint64_t input = 0x1122334455667788ULL; uint64_t expected = 0x8877665544332211ULL; uint64_t result = byteswap(input); EXPECT_EQ(result, expected); EXPECT_EQ(byteswap(to(0x0000000000000000ULL)), to(0x0000000000000000ULL)); EXPECT_EQ(byteswap(to(0xFFFFFFFFFFFFFFFFULL)), to(0xFFFFFFFFFFFFFFFFULL)); } TEST(ByteSwapTest, ByteSwapInt64) { int64_t input = 0x1122334455667788LL; int64_t expected = to(0x8877665544332211ULL); int64_t result = byteswap(input); EXPECT_EQ(result, expected); int64_t neg_input = to(0xFF00000000000000ULL); int64_t neg_expected = to(0x00000000000000FFULL); int64_t neg_result = byteswap(neg_input); EXPECT_EQ(neg_result, neg_expected); } TEST(NDigitsTest, Basic) { static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result"); static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result"); static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result"); static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result"); static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result"); EXPECT_EQ(n_digits(1), 1); EXPECT_EQ(n_digits(9), 1); EXPECT_EQ(n_digits(10), 2); EXPECT_EQ(n_digits(11), 2); EXPECT_EQ(n_digits(1024), 4); } struct test_deleter { static int call_count; static int last_value; void operator()(int value) { call_count++; last_value = value; } }; int test_deleter::call_count = 0; int test_deleter::last_value = 0; class RaiiWrapperTest : public ::testing::Test { protected: RaiiWrapperTest() { test_deleter::call_count = 0; test_deleter::last_value = 0; } }; TEST_F(RaiiWrapperTest, construct_and_destroy_calls_deleter) { { auto w = raii_wrap(42, test_deleter{}); EXPECT_EQ(test_deleter::call_count, 0); } EXPECT_EQ(test_deleter::call_count, 1); EXPECT_EQ(test_deleter::last_value, 42); } TEST_F(RaiiWrapperTest, move_constructor_transfers_ownership) { { auto w1 = raii_wrap(123, test_deleter{}); auto w2 = std::move(w1); EXPECT_EQ(test_deleter::call_count, 0); EXPECT_EQ(static_cast(w2), 123); } EXPECT_EQ(test_deleter::call_count, 1); EXPECT_EQ(test_deleter::last_value, 123); } TEST_F(RaiiWrapperTest, operator_t_and_get) { { auto w = raii_wrap(999, test_deleter{}); int i = w; EXPECT_EQ(i, 999); w.get() = 1000; EXPECT_EQ(static_cast(w), 1000); } EXPECT_EQ(test_deleter::call_count, 1); EXPECT_EQ(test_deleter::last_value, 1000); } class counting_helper { public: static int active; int value; counting_helper(int value) : value(value) { ++active; } ~counting_helper() { --active; } counting_helper(const counting_helper&) = delete; counting_helper(counting_helper&&) = delete; counting_helper& operator=(const counting_helper&) = delete; counting_helper& operator=(counting_helper&&) = delete; int foo() const { return value; } }; int counting_helper::active = 0; TEST(MaybeOwnedTest, NonOwningPointer) { ASSERT_EQ(counting_helper::active, 0); auto instance = cpptrace::detail::make_unique(42); EXPECT_EQ(counting_helper::active, 1); { maybe_owned non_owning(instance.get()); EXPECT_EQ(counting_helper::active, 1); EXPECT_EQ(non_owning->foo(), 42); } EXPECT_EQ(counting_helper::active, 1); instance.reset(); EXPECT_EQ(counting_helper::active, 0); } TEST(MaybeOwnedTest, OwningPointer) { ASSERT_EQ(counting_helper::active, 0); auto instance = cpptrace::detail::make_unique(42); EXPECT_EQ(counting_helper::active, 1); { maybe_owned non_owning(std::move(instance)); EXPECT_EQ(counting_helper::active, 1); EXPECT_EQ(non_owning->foo(), 42); } EXPECT_EQ(counting_helper::active, 0); instance.reset(); EXPECT_EQ(counting_helper::active, 0); } } cpptrace-1.0.4/test/unit/internals/lru_cache.cpp000066400000000000000000000051361504061443700217140ustar00rootroot00000000000000#include #include "utils/lru_cache.hpp" using cpptrace::detail::lru_cache; using cpptrace::detail::nullopt; namespace { TEST(LruCacheTest, DefaultConstructor) { lru_cache cache; EXPECT_EQ(cache.size(), 0); } TEST(LruCacheTest, MaybeGet) { lru_cache cache; auto result = cache.maybe_get(42); EXPECT_FALSE(result.has_value()); } TEST(LruCacheTest, InsertAndGet) { lru_cache cache; cache.insert(42, 50); auto result = cache.maybe_get(42); ASSERT_TRUE(result.has_value()); EXPECT_EQ(result.unwrap(), 50); } TEST(LruCacheTest, ConstGet) { lru_cache cache; cache.insert(42, 50); const lru_cache& cache_ref = cache; auto result = cache_ref.maybe_get(42); ASSERT_TRUE(result.has_value()); EXPECT_EQ(result.unwrap(), 50); } TEST(LruCacheTest, Set) { lru_cache cache; cache.set(42, 50); auto result = cache.maybe_get(42); ASSERT_TRUE(result.has_value()); EXPECT_EQ(result.unwrap(), 50); cache.set(42, 60); auto result2 = cache.maybe_get(42); ASSERT_TRUE(result2.has_value()); EXPECT_EQ(result2.unwrap(), 60); } TEST(LruCacheTest, NoMaxSize) { lru_cache cache; for(int i = 0; i < 1000; i++) { cache.insert(i, i + 50); } EXPECT_EQ(cache.size(), 1000); for(int i = 0; i < 1000; i++) { EXPECT_EQ(cache.maybe_get(i).unwrap(), i + 50); } } TEST(LruCacheTest, MaxSize) { lru_cache cache(20); for(int i = 0; i < 1000; i++) { cache.insert(i, i + 50); } EXPECT_EQ(cache.size(), 20); for(int i = 0; i < 1000 - 20; i++) { EXPECT_FALSE(cache.maybe_get(i).has_value()); } for(int i = 1000 - 20; i < 1000; i++) { EXPECT_EQ(cache.maybe_get(i).unwrap(), i + 50); } } TEST(LruCacheTest, SizeAfterInserts) { lru_cache cache; for(int i = 0; i < 1000; i++) { cache.insert(i, i + 50); } EXPECT_EQ(cache.size(), 1000); cache.set_max_size(20); EXPECT_EQ(cache.size(), 20); for(int i = 0; i < 1000 - 20; i++) { EXPECT_FALSE(cache.maybe_get(i).has_value()); } for(int i = 1000 - 20; i < 1000; i++) { EXPECT_EQ(cache.maybe_get(i).unwrap(), i + 50); } } TEST(LruCacheTest, Touch) { lru_cache cache(20); for(int i = 0; i < 1000; i++) { cache.maybe_touch(0); cache.insert(i, i + 50); } EXPECT_EQ(cache.size(), 20); for(int i = 1000 - 19; i < 1000; i++) { EXPECT_EQ(cache.maybe_get(i).unwrap(), i + 50); } EXPECT_EQ(cache.maybe_get(0).unwrap(), 50); } } cpptrace-1.0.4/test/unit/internals/optional.cpp000066400000000000000000000114661504061443700216170ustar00rootroot00000000000000#include #include "utils/optional.hpp" using cpptrace::detail::optional; using cpptrace::detail::nullopt; namespace { TEST(OptionalTest, DefaultConstructor) { optional o; EXPECT_FALSE(o.has_value()); EXPECT_FALSE(static_cast(o)); optional o1; EXPECT_FALSE(o1.has_value()); EXPECT_FALSE(static_cast(o1)); } TEST(OptionalTest, ConstructWithNullopt) { optional o(nullopt); EXPECT_FALSE(o.has_value()); optional o1(nullopt); EXPECT_FALSE(o1.has_value()); EXPECT_FALSE(static_cast(o1)); } TEST(OptionalTest, ValueConstructor) { optional o(42); EXPECT_TRUE(o.has_value()); EXPECT_EQ(o.unwrap(), 42); int x = 100; optional o2(x); EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o2.unwrap(), 100); int y = 100; optional o3(y); EXPECT_TRUE(o3.has_value()); EXPECT_EQ(o3.unwrap(), 100); y = 200; EXPECT_EQ(o3.unwrap(), 200); } TEST(OptionalTest, CopyConstructor) { optional o1(42); optional o2(o1); EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o2.unwrap(), 42); optional o3(nullopt); optional o4(o3); EXPECT_FALSE(o4.has_value()); int y = 100; optional o5(y); optional o6(o5); EXPECT_TRUE(o5.has_value()); EXPECT_EQ(o5.unwrap(), 100); EXPECT_TRUE(o6.has_value()); EXPECT_EQ(o6.unwrap(), 100); y = 200; EXPECT_EQ(o5.unwrap(), 200); EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, MoveConstructor) { optional o1(42); optional o2(std::move(o1)); EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o2.unwrap(), 42); optional o3(nullopt); optional o4(std::move(o3)); EXPECT_FALSE(o4.has_value()); int y = 100; optional o5(y); optional o6(std::move(o5)); EXPECT_TRUE(o6.has_value()); EXPECT_EQ(o6.unwrap(), 100); y = 200; EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, CopyAssignmentOperator) { optional o1(42); optional o2; o2 = o1; EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o2.unwrap(), 42); optional o3(nullopt); optional o4(100); o4 = o3; EXPECT_FALSE(o4.has_value()); int y = 100; optional o5(y); optional o6; o6 = o5; EXPECT_TRUE(o5.has_value()); EXPECT_EQ(o5.unwrap(), 100); EXPECT_TRUE(o6.has_value()); EXPECT_EQ(o6.unwrap(), 100); y = 200; EXPECT_EQ(o5.unwrap(), 200); EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, MoveAssignmentOperator) { optional o1(42); optional o2; o2 = std::move(o1); EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o2.unwrap(), 42); optional o3(nullopt); optional o4(99); o4 = std::move(o3); EXPECT_FALSE(o4.has_value()); int y = 100; optional o5(y); optional o6; o6 = std::move(o5); EXPECT_TRUE(o6.has_value()); EXPECT_EQ(o6.unwrap(), 100); y = 200; EXPECT_EQ(o6.unwrap(), 200); } TEST(OptionalTest, AssignmentFromValue) { optional o; o = 123; EXPECT_TRUE(o.has_value()); EXPECT_EQ(o.unwrap(), 123); o = nullopt; EXPECT_FALSE(o.has_value()); optional o1; int x = 100; o1 = x; EXPECT_TRUE(o1.has_value()); EXPECT_EQ(o1.unwrap(), x); EXPECT_EQ(&o1.unwrap(), &x); o1 = nullopt; EXPECT_FALSE(o1.has_value()); } TEST(OptionalTest, Reset) { optional o(42); EXPECT_TRUE(o.has_value()); EXPECT_EQ(o.unwrap(), 42); o.reset(); EXPECT_FALSE(o.has_value()); int x = 44; optional o1(x); EXPECT_TRUE(o1.has_value()); EXPECT_EQ(o1.unwrap(), 44); o1.reset(); EXPECT_FALSE(o1.has_value()); } TEST(OptionalTest, Swap) { optional o1(42); optional o2(100); o1.swap(o2); EXPECT_TRUE(o1.has_value()); EXPECT_TRUE(o2.has_value()); EXPECT_EQ(o1.unwrap(), 100); EXPECT_EQ(o2.unwrap(), 42); // Swap a value-holding optional with an empty optional optional o3(7); optional o4(nullopt); o3.swap(o4); EXPECT_FALSE(o3.has_value()); EXPECT_TRUE(o4.has_value()); EXPECT_EQ(o4.unwrap(), 7); int x = 20; int y = 40; optional o5 = x; optional o6 = y; EXPECT_EQ(o5.unwrap(), 20); EXPECT_EQ(o6.unwrap(), 40); o5.swap(o6); EXPECT_EQ(o5.unwrap(), 40); EXPECT_EQ(o6.unwrap(), 20); EXPECT_EQ(x, 20); EXPECT_EQ(y, 40); } TEST(OptionalTest, ValueOr) { optional o1(42); EXPECT_EQ(o1.value_or(100), 42); optional o2(nullopt); EXPECT_EQ(o2.value_or(100), 100); int x = 20; int y = 100; optional o3(x); EXPECT_EQ(o3.value_or(y), 20); o3.reset(); EXPECT_EQ(o3.value_or(y), 100); EXPECT_EQ(&o3.value_or(y), &y); EXPECT_EQ(x, 20); EXPECT_EQ(y, 100); } } cpptrace-1.0.4/test/unit/internals/result.cpp000066400000000000000000000124011504061443700212760ustar00rootroot00000000000000#include #include "utils/result.hpp" using cpptrace::detail::Result; namespace { // A simple custom error type that behaves like a standard exception. struct error { int x; const char* what() const { return "error..."; } }; class ResultFixture : public testing::Test { public: ResultFixture() { cpptrace::absorb_trace_exceptions(true); } ~ResultFixture() override { cpptrace::absorb_trace_exceptions(false); } }; TEST_F(ResultFixture, ConstructWithValueRValue) { Result result("test"); EXPECT_TRUE(result.has_value()); EXPECT_FALSE(result.is_error()); EXPECT_TRUE(static_cast(result)); EXPECT_EQ(result.unwrap_value(), "test"); EXPECT_FALSE(result.error().has_value()); } TEST_F(ResultFixture, ConstructWithValueLValue) { std::string s = "test"; Result result(s); EXPECT_TRUE(result.has_value()); EXPECT_FALSE(result.is_error()); EXPECT_EQ(result.unwrap_value(), "test"); s = "x"; EXPECT_EQ(result.unwrap_value(), "test"); Result r2(s); EXPECT_EQ(r2.unwrap_value(), "x"); s = "y"; EXPECT_EQ(r2.unwrap_value(), "y"); } TEST_F(ResultFixture, ConstructWithErrorRValue) { Result result(error{1}); EXPECT_FALSE(result.has_value()); EXPECT_TRUE(result.is_error()); EXPECT_FALSE(static_cast(result)); EXPECT_EQ(result.unwrap_error().x, 1); // Check that value() returns nullopt in this scenario EXPECT_FALSE(result.value().has_value()); } TEST_F(ResultFixture, ConstructWithErrorLValue) { error e{1}; Result result(e); EXPECT_FALSE(result.has_value()); EXPECT_TRUE(result.is_error()); EXPECT_EQ(result.unwrap_error().x, 1); } TEST_F(ResultFixture, MoveConstructorValue) { Result original(std::string("move")); Result moved(std::move(original)); EXPECT_TRUE(moved.has_value()); EXPECT_EQ(moved.unwrap_value(), "move"); EXPECT_TRUE(original.has_value()); std::string s = "test"; Result r1(s); Result r2(std::move(r1)); EXPECT_TRUE(r2.has_value()); EXPECT_EQ(r2.unwrap_value(), "test"); s = "foo"; EXPECT_EQ(r2.unwrap_value(), "foo"); EXPECT_TRUE(r2.has_value()); } TEST_F(ResultFixture, MoveConstructorError) { Result original(error{1}); Result moved(std::move(original)); EXPECT_TRUE(moved.is_error()); EXPECT_EQ(moved.unwrap_error().x, 1); EXPECT_TRUE(original.is_error()); } TEST_F(ResultFixture, MoveAssignmentValue) { Result original(std::string("move")); Result target = std::string{}; target = std::move(original); EXPECT_TRUE(target.has_value()); EXPECT_EQ(target.unwrap_value(), "move"); EXPECT_TRUE(original.has_value()); std::string s = "test"; std::string n; Result r1(s); Result r2(n); r2 = std::move(r1); EXPECT_TRUE(r2.has_value()); EXPECT_EQ(r2.unwrap_value(), "test"); s = "foo"; EXPECT_EQ(r2.unwrap_value(), "foo"); EXPECT_TRUE(r2.has_value()); } TEST_F(ResultFixture, MoveAssignmentError) { Result original(error{1}); Result target = std::string{}; target = std::move(original); EXPECT_TRUE(target.is_error()); EXPECT_EQ(target.unwrap_error().x, 1); EXPECT_TRUE(original.is_error()); } TEST_F(ResultFixture, CopyAssignmentValue) { Result original(std::string("move")); Result target = std::string{}; target = original; EXPECT_TRUE(target.has_value()); EXPECT_EQ(target.unwrap_value(), "move"); EXPECT_TRUE(original.has_value()); std::string s = "test"; std::string n; Result r1(s); Result r2(n); r2 = r1; EXPECT_TRUE(r2.has_value()); EXPECT_EQ(r2.unwrap_value(), "test"); s = "foo"; EXPECT_EQ(r2.unwrap_value(), "foo"); EXPECT_TRUE(r2.has_value()); } TEST_F(ResultFixture, CopyAssignmentError) { Result original(error{1}); Result target = std::string{}; target = original; EXPECT_TRUE(target.is_error()); EXPECT_EQ(target.unwrap_error().x, 1); EXPECT_TRUE(original.is_error()); } TEST_F(ResultFixture, ValueOr) { { Result res_with_value(42); EXPECT_EQ(res_with_value.value_or(-1), 42); EXPECT_EQ(std::move(res_with_value).value_or(-1), 42); } { Result res_with_error(error{}); EXPECT_EQ(res_with_error.value_or(-1), -1); EXPECT_EQ(std::move(res_with_error).value_or(-1), -1); } { int x = 2; int y = 3; Result res_with_value(x); EXPECT_EQ(res_with_value.value_or(y), 2); EXPECT_EQ(std::move(res_with_value).value_or(y), 2); } { int x = 2; Result res_with_error(error{}); EXPECT_EQ(res_with_error.value_or(x), 2); EXPECT_EQ(&res_with_error.value_or(x), &x); EXPECT_EQ(std::move(res_with_error).value_or(x), 2); } } } cpptrace-1.0.4/test/unit/internals/span.cpp000066400000000000000000000027271504061443700207330ustar00rootroot00000000000000#include #include #include #include #include #include "utils/span.hpp" using cpptrace::detail::span; using cpptrace::detail::make_span; using cpptrace::detail::make_bspan; namespace { TEST(SpanTest, Basic) { std::array arr{1, 2, 3, 4, 5}; // thanks microsoft for using horrible non-standard iterators, otherwise this would test with begin()/end() auto span = make_span(arr.data(), arr.data() + arr.size()); EXPECT_EQ(span.data(), arr.data()); EXPECT_EQ(span.size(), 5); EXPECT_EQ(span.data()[0], 1); EXPECT_EQ(span.data()[1], 2); EXPECT_EQ(span.data()[2], 3); EXPECT_EQ(span.data()[3], 4); EXPECT_EQ(span.data()[4], 5); } TEST(SpanTest, PtrSize) { std::array arr{1, 2, 3, 4, 5}; auto span = make_span(arr.data(), arr.size()); EXPECT_EQ(span.data(), arr.data()); EXPECT_EQ(span.size(), 5); EXPECT_EQ(span.data()[0], 1); EXPECT_EQ(span.data()[1], 2); EXPECT_EQ(span.data()[2], 3); EXPECT_EQ(span.data()[3], 4); EXPECT_EQ(span.data()[4], 5); } TEST(SpanTest, Bspan) { struct S { std::array data; }; S s{{'a', 'b', 'c', 'd'}}; auto span = make_bspan(s); EXPECT_EQ(span.data(), s.data.data()); EXPECT_EQ(span.size(), 4); EXPECT_EQ(span.data()[0], 'a'); EXPECT_EQ(span.data()[1], 'b'); EXPECT_EQ(span.data()[2], 'c'); EXPECT_EQ(span.data()[3], 'd'); } } cpptrace-1.0.4/test/unit/internals/string_utils.cpp000066400000000000000000000047571504061443700225250ustar00rootroot00000000000000#include #include #include #include #include "utils/utils.hpp" using testing::ElementsAre; using cpptrace::detail::split; using cpptrace::detail::join; using cpptrace::detail::trim; using cpptrace::detail::starts_with; namespace { TEST(SplitTest, SplitBySingleDelimiter) { std::string input = "hello,world"; auto tokens = split(input, ","); EXPECT_THAT(tokens, ElementsAre("hello", "world")); } TEST(SplitTest, SplitByMultipleDelimiters) { std::string input = "hello,world;test"; auto tokens = split(input, ",;"); EXPECT_THAT(tokens, ElementsAre("hello", "world", "test")); } TEST(SplitTest, HandlesNoDelimiterFound) { std::string input = "nodellimitershere"; auto tokens = split(input, ", "); EXPECT_THAT(tokens, ElementsAre("nodellimitershere")); } TEST(SplitTest, HandlesEmptyString) { std::string input = ""; auto tokens = split(input, ","); EXPECT_THAT(tokens, ElementsAre("")); } TEST(SplitTest, HandlesConsecutiveDelimiters) { std::string input = "one,,two,,,three"; auto tokens = split(input, ","); EXPECT_THAT(tokens, ElementsAre("one", "", "two", "", "", "three")); } TEST(SplitTest, HandlesTrailingDelimiter) { std::string input = "abc,"; auto tokens = split(input, ","); EXPECT_THAT(tokens, ElementsAre("abc", "")); } TEST(SplitTest, HandlesLeadingDelimiter) { std::string input = ",abc"; auto tokens = split(input, ","); EXPECT_THAT(tokens, ElementsAre("", "abc")); } TEST(JoinTest, EmptyContainer) { std::vector vec; EXPECT_EQ(join(vec, ","), ""); } TEST(JoinTest, SingleElements) { std::vector vec = {"one"}; EXPECT_EQ(join(vec, ","), "one"); } TEST(JoinTest, MultipleElements) { std::vector vec = {"one", "two", "three"}; EXPECT_EQ(join(vec, ","), "one,two,three"); } TEST(TrimTest, Basic) { EXPECT_EQ(trim(""), ""); EXPECT_EQ(trim("test"), "test"); EXPECT_EQ(trim(" test "), "test"); EXPECT_EQ(trim(" test\n "), "test"); EXPECT_EQ(trim("\t test\n "), "test"); } TEST(StartsWith, Basic) { EXPECT_TRUE(starts_with("", "")); EXPECT_TRUE(starts_with("abc", "")); EXPECT_FALSE(starts_with("", "abc")); EXPECT_FALSE(starts_with("ab", "abc")); EXPECT_TRUE(starts_with("test", "test")); EXPECT_TRUE(starts_with("hello_world", "hello")); EXPECT_FALSE(starts_with("hello_world", "world")); EXPECT_FALSE(starts_with("abcd", "abce")); } } cpptrace-1.0.4/test/unit/internals/string_view.cpp000066400000000000000000000023341504061443700223240ustar00rootroot00000000000000#include #include #include #include #include #include "utils/string_view.hpp" using cpptrace::detail::string_view; using cpptrace::detail::cstring_view; namespace { TEST(StringViewTest, Basic) { string_view sv = "foo"; EXPECT_EQ(sv.size(), 3); EXPECT_EQ(sv.data(), std::string("foo")); EXPECT_EQ(sv[0], 'f'); EXPECT_EQ(sv[1], 'o'); EXPECT_EQ(sv.find_last_of("f"), 0); EXPECT_EQ(sv.find_last_of("o"), 2); EXPECT_EQ(sv.find_last_of("asfd"), 0); EXPECT_EQ(sv.find_last_of("asod"), 2); EXPECT_EQ(sv.find_last_of("bar"), sv.npos); EXPECT_EQ(sv, "foo"); EXPECT_NE(sv, "bar"); string_view sv2 = "abc"; EXPECT_EQ(sv2.front(), 'a'); EXPECT_EQ(sv2.back(), 'c'); EXPECT_TRUE(sv2.starts_with("ab")); EXPECT_TRUE(sv2.ends_with("bc")); EXPECT_TRUE(sv2.starts_with("abc")); EXPECT_TRUE(sv2.ends_with("abc")); EXPECT_FALSE(sv2.starts_with("abce")); EXPECT_FALSE(sv2.ends_with("abce")); EXPECT_EQ(sv2.substr(1), "bc"); EXPECT_EQ(sv2.substr(0, 2), "ab"); sv2.advance(1); EXPECT_EQ(sv2, "bc"); std::string s = "foo"; s += sv2; EXPECT_EQ(s, "foobc"); } } cpptrace-1.0.4/test/unit/lib/000077500000000000000000000000001504061443700160255ustar00rootroot00000000000000cpptrace-1.0.4/test/unit/lib/formatting.cpp000066400000000000000000000523231504061443700207100ustar00rootroot00000000000000#include #include #include #include #include "utils/microfmt.hpp" #include "utils/utils.hpp" #include #ifdef TEST_MODULE import cpptrace; #else #include #endif using cpptrace::detail::split; using testing::ElementsAre; namespace { #if UINTPTR_MAX > 0xffffffff #define ADDR_PREFIX "00000000" #define PADDING_TAG " " #define INLINED_TAG "(inlined) " #else #define PADDING_TAG "" #define ADDR_PREFIX "" #define INLINED_TAG "(inlined) " #endif cpptrace::stacktrace make_test_stacktrace(size_t count = 1) { cpptrace::stacktrace trace; for(size_t i = 0; i < count; i++) { trace.frames.push_back({0x1, 0x1001, {20}, {30}, "foo.cpp", "foo()", false}); trace.frames.push_back({0x2, 0x1002, {30}, {40}, "bar.cpp", "bar()", false}); trace.frames.push_back({0x3, 0x1003, {40}, {25}, "foo.cpp", "main", false}); } return trace; } TEST(FormatterTest, Basic) { auto res = split(cpptrace::get_default_formatter().format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in bar() at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, Inlines) { auto trace = make_test_stacktrace(); trace.frames[1].is_inline = true; trace.frames[1].raw_address = 0; trace.frames[1].object_address = 0; auto res = split(cpptrace::get_default_formatter().format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 " INLINED_TAG " in bar() at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, Header) { auto formatter = cpptrace::formatter{} .header("Stack trace:"); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace:", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in bar() at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, NoColumn) { auto formatter = cpptrace::formatter{} .columns(false); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20", "#1 0x" ADDR_PREFIX "00000002 in bar() at bar.cpp:30", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40" ) ); } TEST(FormatterTest, ObjectAddresses) { auto formatter = cpptrace::formatter{} .addresses(cpptrace::formatter::address_mode::object); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00001001 in foo() at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00001002 in bar() at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00001003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, NoAddresses) { auto formatter = cpptrace::formatter{} .addresses(cpptrace::formatter::address_mode::none); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 in foo() at foo.cpp:20:30", "#1 in bar() at bar.cpp:30:40", "#2 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, BreakBeforeFilename) { auto formatter = cpptrace::formatter{} .break_before_filename(true); EXPECT_THAT( split(formatter.format(make_test_stacktrace()), "\n"), ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo()", " " PADDING_TAG " at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in bar()", " " PADDING_TAG " at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main", " " PADDING_TAG " at foo.cpp:40:25" ) ); } TEST(FormatterTest, BreakBeforeFilenameNoAddresses) { auto formatter = cpptrace::formatter{} .break_before_filename(true) .addresses(cpptrace::formatter::address_mode::none); // Check that if no address is present, the filename indent is reduced EXPECT_THAT( split(formatter.format(make_test_stacktrace()), "\n"), ElementsAre( "Stack trace (most recent call first):", "#0 in foo()", " at foo.cpp:20:30", "#1 in bar()", " at bar.cpp:30:40", "#2 in main", " at foo.cpp:40:25" ) ); } TEST(FormatterTest, BreakBeforeFilenameInlines) { auto formatter = cpptrace::formatter{} .break_before_filename(true); // Check that indentation is computed correctly when elements are inlined auto trace = make_test_stacktrace(); trace.frames[1].is_inline = true; EXPECT_THAT( split(formatter.format(trace), "\n"), ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo()", " " PADDING_TAG " at foo.cpp:20:30", "#1 " INLINED_TAG " in bar()", " " PADDING_TAG " at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main", " " PADDING_TAG " at foo.cpp:40:25" ) ); } TEST(FormatterTest, BreakBeforeFilenameLongTrace) { // Check that indentation is computed correctly for longer traces (where the // frame number is padded) auto formatter = cpptrace::formatter{} .break_before_filename(true); EXPECT_THAT( split(formatter.format(make_test_stacktrace(4)), "\n"), ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo()", " " PADDING_TAG " at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in bar()", " " PADDING_TAG " at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main", " " PADDING_TAG " at foo.cpp:40:25", "#3 0x" ADDR_PREFIX "00000001 in foo()", " " PADDING_TAG " at foo.cpp:20:30", "#4 0x" ADDR_PREFIX "00000002 in bar()", " " PADDING_TAG " at bar.cpp:30:40", "#5 0x" ADDR_PREFIX "00000003 in main", " " PADDING_TAG " at foo.cpp:40:25", "#6 0x" ADDR_PREFIX "00000001 in foo()", " " PADDING_TAG " at foo.cpp:20:30", "#7 0x" ADDR_PREFIX "00000002 in bar()", " " PADDING_TAG " at bar.cpp:30:40", "#8 0x" ADDR_PREFIX "00000003 in main", " " PADDING_TAG " at foo.cpp:40:25", "#9 0x" ADDR_PREFIX "00000001 in foo()", " " PADDING_TAG " at foo.cpp:20:30", "#10 0x" ADDR_PREFIX "00000002 in bar()", " " PADDING_TAG " at bar.cpp:30:40", "#11 0x" ADDR_PREFIX "00000003 in main", " " PADDING_TAG " at foo.cpp:40:25" ) ); } TEST(FormatterTest, BreakBeforeFilenameColors) { // Check that indentation is computed correctly with colors enabled // (If microfmt is updated to count the number of characters printed, // it will need to _exclude_ colors for the purposes of computing // alignment) auto formatter = cpptrace::formatter{} .break_before_filename(true) .colors(cpptrace::formatter::color_mode::always); EXPECT_THAT( split(formatter.format(make_test_stacktrace()), "\n"), ElementsAre( "Stack trace (most recent call first):", "#0 \x1B[34m0x" ADDR_PREFIX "00000001\x1B[0m in \x1B[33mfoo()\x1B[0m", " " PADDING_TAG " at \x1B[32mfoo.cpp\x1B[0m:\x1B[34m20\x1B[0m:\x1B[34m30\x1B[0m", "#1 \x1B[34m0x" ADDR_PREFIX "00000002\x1B[0m in \x1B[33mbar()\x1B[0m", " " PADDING_TAG " at \x1B[32mbar.cpp\x1B[0m:\x1B[34m30\x1B[0m:\x1B[34m40\x1B[0m", "#2 \x1B[34m0x" ADDR_PREFIX "00000003\x1B[0m in \x1B[33mmain\x1B[0m", " " PADDING_TAG " at \x1B[32mfoo.cpp\x1B[0m:\x1B[34m40\x1B[0m:\x1B[34m25\x1B[0m" ) ); } TEST(FormatterTest, PathShortening) { cpptrace::stacktrace trace; trace.frames.push_back({0x1, 0x1001, {20}, {30}, "/home/foo/foo.cpp", "foo()", false}); trace.frames.push_back({0x2, 0x1002, {30}, {40}, "/bar.cpp", "bar()", false}); trace.frames.push_back({0x3, 0x1003, {40}, {25}, "baz/foo.cpp", "main", false}); trace.frames.push_back({0x3, 0x1003, {50}, {25}, "C:\\foo\\bar\\baz.cpp", "main", false}); auto formatter = cpptrace::formatter{} .paths(cpptrace::formatter::path_mode::basename); auto res = split(formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in bar() at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25", "#3 0x" ADDR_PREFIX "00000003 in main at baz.cpp:50:25" ) ); } #ifndef CPPTRACE_NO_TEST_SNIPPETS TEST(FormatterTest, Snippets) { cpptrace::stacktrace trace; unsigned line = __LINE__ + 1; trace.frames.push_back({0x1, 0x1001, {line}, {20}, __FILE__, "foo()", false}); trace.frames.push_back({0x2, 0x1002, {line + 1}, {}, __FILE__, "foo()", false}); auto formatter = cpptrace::formatter{} .snippets(true); auto res = split(formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", // frame 1 cpptrace::microfmt::format("#0 0x" ADDR_PREFIX "00000001 in foo() at {}:{}:20", __FILE__, line), cpptrace::microfmt::format(" {}: cpptrace::stacktrace trace;", line - 2), cpptrace::microfmt::format(" {}: unsigned line = __LINE__ + 1;", line - 1), cpptrace::microfmt::format( " > {}: trace.frames.push_back({0x1, 0x1001, {line}, {{20}}, __FILE__, \"foo()\", false});", line ), cpptrace::microfmt::format(" ^"), cpptrace::microfmt::format( " {}: trace.frames.push_back({0x2, 0x1002, {line + 1}, {{}}, __FILE__, \"foo()\", false});", line + 1 ), cpptrace::microfmt::format(" {}: auto formatter = cpptrace::formatter{{}}", line + 2), // frame 2 cpptrace::microfmt::format("#1 0x" ADDR_PREFIX "00000002 in foo() at {}:{}", __FILE__, line + 1), cpptrace::microfmt::format(" {}: unsigned line = __LINE__ + 1;", line - 1), cpptrace::microfmt::format( " {}: trace.frames.push_back({0x1, 0x1001, {line}, {{20}}, __FILE__, \"foo()\", false});", line ), cpptrace::microfmt::format( " > {}: trace.frames.push_back({0x2, 0x1002, {line + 1}, {{}}, __FILE__, \"foo()\", false});", line + 1 ), cpptrace::microfmt::format(" {}: auto formatter = cpptrace::formatter{{}}", line + 2), cpptrace::microfmt::format(" {}: .snippets(true);", line + 3) ) ); formatter.snippet_context(1); res = split(formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", // frame 1 cpptrace::microfmt::format("#0 0x" ADDR_PREFIX "00000001 in foo() at {}:{}:20", __FILE__, line), cpptrace::microfmt::format(" {}: unsigned line = __LINE__ + 1;", line - 1), cpptrace::microfmt::format( " > {}: trace.frames.push_back({0x1, 0x1001, {line}, {{20}}, __FILE__, \"foo()\", false});", line ), cpptrace::microfmt::format(" ^"), cpptrace::microfmt::format( " {}: trace.frames.push_back({0x2, 0x1002, {line + 1}, {{}}, __FILE__, \"foo()\", false});", line + 1 ), // frame 2 cpptrace::microfmt::format("#1 0x" ADDR_PREFIX "00000002 in foo() at {}:{}", __FILE__, line + 1), cpptrace::microfmt::format( " {}: trace.frames.push_back({0x1, 0x1001, {line}, {{20}}, __FILE__, \"foo()\", false});", line ), cpptrace::microfmt::format( " > {}: trace.frames.push_back({0x2, 0x1002, {line + 1}, {{}}, __FILE__, \"foo()\", false});", line + 1 ), cpptrace::microfmt::format(" {}: auto formatter = cpptrace::formatter{{}}", line + 2) ) ); } #endif TEST(FormatterTest, Colors) { auto formatter = cpptrace::formatter{} .colors(cpptrace::formatter::color_mode::always); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 \x1B[34m0x" ADDR_PREFIX "00000001\x1B[0m in \x1B[33mfoo()\x1B[0m at \x1B[32mfoo.cpp\x1B[0m:\x1B[34m20\x1B[0m:\x1B[34m30\x1B[0m", "#1 \x1B[34m0x" ADDR_PREFIX "00000002\x1B[0m in \x1B[33mbar()\x1B[0m at \x1B[32mbar.cpp\x1B[0m:\x1B[34m30\x1B[0m:\x1B[34m40\x1B[0m", "#2 \x1B[34m0x" ADDR_PREFIX "00000003\x1B[0m in \x1B[33mmain\x1B[0m at \x1B[32mfoo.cpp\x1B[0m:\x1B[34m40\x1B[0m:\x1B[34m25\x1B[0m" ) ); } TEST(FormatterTest, Filtering) { auto formatter = cpptrace::formatter{} .filter([] (const cpptrace::stacktrace_frame& frame) -> bool { return frame.filename.find("foo.cpp") != std::string::npos; }); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 (filtered)", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, DontShowFilteredFrames) { auto formatter = cpptrace::formatter{} .filter([] (const cpptrace::stacktrace_frame& frame) -> bool { return frame.filename.find("foo.cpp") != std::string::npos; }) .filtered_frame_placeholders(false); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, Transforming) { auto formatter = cpptrace::formatter{} .transform([](cpptrace::stacktrace_frame frame) { static size_t count = 0; frame.symbol = cpptrace::microfmt::format("sym{}", count++); return frame; }); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in sym0 at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in sym1 at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in sym2 at foo.cpp:40:25" ) ); auto frame_res = split(formatter.format(make_test_stacktrace().frames[1]), "\n"); EXPECT_THAT( frame_res, ElementsAre( "0x" ADDR_PREFIX "00000002 in sym3 at bar.cpp:30:40" ) ); } TEST(FormatterTest, TransformingRvalueRef) { auto formatter = cpptrace::formatter{} .transform([](cpptrace::stacktrace_frame&& frame) { static size_t count = 0; frame.symbol = cpptrace::microfmt::format("sym{}", count++); return std::move(frame); }); auto res = split(formatter.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in sym0 at foo.cpp:20:30", "#1 0x" ADDR_PREFIX "00000002 in sym1 at bar.cpp:30:40", "#2 0x" ADDR_PREFIX "00000003 in sym2 at foo.cpp:40:25" ) ); auto frame_res = split(formatter.format(make_test_stacktrace().frames[1]), "\n"); EXPECT_THAT( frame_res, ElementsAre( "0x" ADDR_PREFIX "00000002 in sym3 at bar.cpp:30:40" ) ); } TEST(FormatterTest, MoveSemantics) { auto formatter = cpptrace::formatter{} .filter([] (const cpptrace::stacktrace_frame& frame) -> bool { return frame.filename.find("foo.cpp") != std::string::npos; }); auto formatter2 = std::move(formatter); auto res = split(formatter2.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 (filtered)", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); cpptrace::formatter formatter3; formatter3 = std::move(formatter); auto res2 = split(formatter2.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res2, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 (filtered)", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, CopySemantics) { auto formatter = cpptrace::formatter{} .filter([] (const cpptrace::stacktrace_frame& frame) -> bool { return frame.filename.find("foo.cpp") != std::string::npos; }); auto formatter2 = formatter; auto res = split(formatter2.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 (filtered)", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); cpptrace::formatter formatter3; formatter3 = formatter; auto res2 = split(formatter2.format(make_test_stacktrace()), "\n"); EXPECT_THAT( res2, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo() at foo.cpp:20:30", "#1 (filtered)", "#2 0x" ADDR_PREFIX "00000003 in main at foo.cpp:40:25" ) ); } TEST(FormatterTest, PrettySymbols) { auto normal_formatter = cpptrace::formatter{} .symbols(cpptrace::formatter::symbol_mode::full); cpptrace::stacktrace trace; trace.frames.push_back({0x1, 0x1001, {20}, {30}, "foo.cpp", "foo(std::__cxx11::basic_string, std::allocator >)", false}); auto res = split(normal_formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo(std::__cxx11::basic_string, std::allocator >) at foo.cpp:20:30" ) ); auto pretty_formatter = cpptrace::formatter{} .symbols(cpptrace::formatter::symbol_mode::pretty); res = split(pretty_formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in foo(std::string) at foo.cpp:20:30" ) ); } TEST(FormatterTest, PrunedSymbols) { auto normal_formatter = cpptrace::formatter{} .symbols(cpptrace::formatter::symbol_mode::full); cpptrace::stacktrace trace; trace.frames.push_back({0x1, 0x1001, {20}, {30}, "foo.cpp", "ns::S::foo(int, int, int)", false}); auto res = split(normal_formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in ns::S::foo(int, int, int) at foo.cpp:20:30" ) ); auto pruned_formatter = cpptrace::formatter{} .symbols(cpptrace::formatter::symbol_mode::pruned); res = split(pruned_formatter.format(trace), "\n"); EXPECT_THAT( res, ElementsAre( "Stack trace (most recent call first):", "#0 0x" ADDR_PREFIX "00000001 in ns::S::foo at foo.cpp:20:30" ) ); } } cpptrace-1.0.4/test/unit/lib/nullable.cpp000066400000000000000000000033331504061443700203310ustar00rootroot00000000000000#include #include #include #include #ifdef TEST_MODULE import cpptrace; #else #include #endif using cpptrace::nullable; namespace { TEST(NullableTest, Basic) { nullable a{12}; EXPECT_EQ(a.value(), 12); EXPECT_EQ(a.raw_value, 12); nullable b = 20; EXPECT_EQ(b.value(), 20); } TEST(NullableTest, Null) { auto a = nullable::null(); EXPECT_FALSE(a.has_value()); EXPECT_EQ(a.raw_value, (std::numeric_limits::max)()); nullable b; EXPECT_FALSE(b.has_value()); EXPECT_EQ(b.raw_value, nullable::null_value()); } TEST(NullableTest, Assignment) { nullable a; a = 12; EXPECT_EQ(a.value(), 12); nullable b = 20; a = b; EXPECT_EQ(a.value(), 20); } TEST(NullableTest, Reset) { nullable a{12}; a.reset(); EXPECT_FALSE(a.has_value()); } TEST(NullableTest, ValueOr) { auto a = nullable::null(); EXPECT_EQ(a.value_or(20), 20); } TEST(NullableTest, Comparison) { EXPECT_EQ(nullable{12}, nullable{12}); EXPECT_NE(nullable{12}, nullable{20}); EXPECT_NE(nullable{12}, nullable::null()); EXPECT_EQ(nullable::null(), nullable::null()); } TEST(NullableTest, Swap) { auto a = nullable::null(); nullable b = 12; EXPECT_FALSE(a.has_value()); EXPECT_EQ(b.value(), 12); a.swap(b); EXPECT_FALSE(b.has_value()); EXPECT_EQ(a.value(), 12); } } cpptrace-1.0.4/test/unit/lib/prune_symbol.cpp000066400000000000000000000725641504061443700212650ustar00rootroot00000000000000#include #include #include #include #ifdef TEST_MODULE import cpptrace; #else #include #endif namespace { #define DO_TEST(symbol, expected) EXPECT_EQ(cpptrace::prune_symbol(symbol), expected) << "Input: " << symbol TEST(PruneSymbolTests, Basic) { // https://godbolt.org/z/Weas1ETPv DO_TEST("foo()", "foo"); DO_TEST("foo(int, char)", "foo"); DO_TEST("void foo(void)", "foo"); DO_TEST("void foo(int, char)", "foo"); } TEST(PruneSymbolTests, Namespaces) { // https://godbolt.org/z/WzfsrPhE4 DO_TEST("foo()", "foo"); DO_TEST("ns::foo()", "ns::foo"); DO_TEST("ns::ns2::foo()", "ns::ns2::foo"); DO_TEST("ns::v1::bar()", "ns::v1::bar"); DO_TEST("(anonymous namespace)::bar()", "(anonymous namespace)::bar"); DO_TEST("void foo(void)", "foo"); DO_TEST("void ns::foo(void)", "ns::foo"); DO_TEST("void ns::ns2::foo(void)", "ns::ns2::foo"); DO_TEST("void ns::v1::bar(void)", "ns::v1::bar"); DO_TEST("void `anonymous namespace'::bar(void)", "(anonymous namespace)::bar"); } TEST(PruneSymbolTests, BasicTemplates) { // https://godbolt.org/z/czWo9bdrn DO_TEST("void foo(int const&)", "foo"); DO_TEST("void foo(int const&, double const&)", "foo"); DO_TEST("void foo>(S const&)", "foo"); // llvm DO_TEST("void foo >(S const&)", "foo"); // gnutils DO_TEST("void foo>(S const&)", "foo"); DO_TEST("void foo >(S const&)", "foo"); DO_TEST("void foo, S>>(S, S> const&)", "foo"); DO_TEST("void foo, S > >(S, S > const&)", "foo"); DO_TEST("void foo(int const &)", "foo"); DO_TEST("void foo(int const &,double const &)", "foo"); DO_TEST("void foo >(S const &)", "foo"); DO_TEST("void foo >(S const &)", "foo"); DO_TEST("void foo,S > >(S,S > const &)", "foo"); } TEST(PruneSymbolTests, MemberFunctions) { // https://godbolt.org/z/re9zfzPq5 DO_TEST("S::S()", "S::S"); DO_TEST("S::~S()", "S::~S"); DO_TEST("S::foo()", "S::foo"); DO_TEST("S::bar() const", "S::bar"); DO_TEST("void S::bar() const", "S::bar"); DO_TEST("ns::SS<>::SS()", "ns::SS::SS"); DO_TEST("ns::SS<>::~SS()", "ns::SS::~SS"); DO_TEST("ns::SS<>::foo()", "ns::SS::foo"); DO_TEST("ns::SS::SS()", "ns::SS::SS"); DO_TEST("ns::SS::~SS()", "ns::SS::~SS"); DO_TEST("ns::SS::foo()", "ns::SS::foo"); DO_TEST("ns::SS, ns::SS>::SS()", "ns::SS::SS"); DO_TEST("ns::SS, ns::SS>::~SS()", "ns::SS::~SS"); DO_TEST("ns::SS, ns::SS>::foo()", "ns::SS::foo"); DO_TEST("ns::SS<>::SS<>(void)", "ns::SS::SS"); DO_TEST("ns::SS<>::~SS<>(void)", "ns::SS::~SS"); DO_TEST("void ns::SS<>::foo(void)", "ns::SS::foo"); DO_TEST("ns::SS::SS(void)", "ns::SS::SS"); DO_TEST("ns::SS::~SS(void)", "ns::SS::~SS"); DO_TEST("void ns::SS::foo(void)", "ns::SS::foo"); DO_TEST("ns::SS,ns::SS >::SS,ns::SS >(void)", "ns::SS::SS"); DO_TEST("ns::SS,ns::SS >::~SS,ns::SS >(void)", "ns::SS::~SS"); DO_TEST("void ns::SS,ns::SS >::foo(void)", "ns::SS::foo"); } TEST(PruneSymbolTests, TemplatedMemberFunctions) { // https://godbolt.org/z/dc3TEheK9 DO_TEST("ns::SS<>::SS<>()", "ns::SS::SS"); DO_TEST("void ns::SS<>::foo<>()", "ns::SS::foo"); DO_TEST("ns::SS::SS(int, float)", "ns::SS::SS"); DO_TEST("void ns::SS::foo()", "ns::SS::foo"); DO_TEST("ns::SS, ns::SS>::SS, ns::SS>(ns::SS, ns::SS)", "ns::SS::SS"); DO_TEST("void ns::SS, ns::SS>::foo, ns::SS>()", "ns::SS::foo"); DO_TEST("ns::SS, ns::SS >::SS, ns::SS >(ns::SS, ns::SS)", "ns::SS::SS"); DO_TEST("void ns::SS, ns::SS >::foo, ns::SS >()", "ns::SS::foo"); DO_TEST("ns::SS<>::SS<><>(void)", "ns::SS::SS"); DO_TEST("void ns::SS<>::foo<>(void)", "ns::SS::foo"); DO_TEST("ns::SS::SS(int,float)", "ns::SS::SS"); DO_TEST("void ns::SS::foo(void)", "ns::SS::foo"); DO_TEST("ns::SS,ns::SS >::SS,ns::SS >,ns::SS >(ns::SS,ns::SS)", "ns::SS::SS"); DO_TEST("void ns::SS,ns::SS >::foo,ns::SS >(void)", "ns::SS::foo"); } TEST(PruneSymbolTests, Decltype) { // https://godbolt.org/z/dc3TEheK9 DO_TEST("decltype(declval() + declval()) foo(int)", "foo"); DO_TEST("decltype (((declval)())+((declval)())) foo(int)", "foo"); DO_TEST("decltype(std::declval() + std::declval()) foo(int)", "foo"); DO_TEST("int foo(int)", "foo"); DO_TEST("decltype(declval() + declval()) bar(decltype(declval()))", "bar"); DO_TEST("decltype (((declval)())+((declval)())) bar(decltype ((declval)()))", "bar"); DO_TEST("decltype(std::declval() + std::declval()) bar(decltype(std::declval()))", "bar"); DO_TEST("int bar(int &&)", "bar"); DO_TEST("decltype(declval() < declval()) baz(int)", "baz"); DO_TEST("decltype (((declval)())<((declval)())) baz(int)", "baz"); DO_TEST("decltype(std::declval() < std::declval()) baz(int)", "baz"); DO_TEST("bool baz(int)", "baz"); // https://godbolt.org/z/4M1xfW5rP DO_TEST("decltype(int{} + int{}) foo(int)", "foo"); DO_TEST("decltype (int{}+int{}) foo(int)", "foo"); DO_TEST("int foo(int)", "foo"); } TEST(PruneSymbolTests, Operators) { // https://godbolt.org/z/qMKEKW656 DO_TEST("S::operator*() const", "S::operator*"); DO_TEST("S::operator+(S const&) const", "S::operator+"); DO_TEST("S::operator>(S const&) const", "S::operator>"); DO_TEST("S::operator<<(S const&) const", "S::operator<<"); DO_TEST("S::operator()() const", "S::operator()"); DO_TEST("S::operator[](int) const", "S::operator[]"); DO_TEST("void S::operator*(void)const", "S::operator*"); DO_TEST("void S::operator+(S const &)const", "S::operator+"); DO_TEST("void S::operator>(S const &)const", "S::operator>"); DO_TEST("void S::operator<<(S const &)const", "S::operator<<"); DO_TEST("void S::operator()(void)const", "S::operator()"); DO_TEST("void S::operator[](int)const", "S::operator[]"); // https://godbolt.org/z/E1bqKf8vv DO_TEST("operator*(S, S)", "operator*"); DO_TEST("operator\"\" _w(unsigned long long)", "operator\"\"_w"); DO_TEST("unsigned __int64 operator \"\" _w(unsigned __int64)", "operator\"\"_w"); } TEST(PruneSymbolTests, TemplatedOperators) { // https://godbolt.org/z/nfcrTfj7M DO_TEST("void operator+(S, S)", "operator+"); // DO_TEST("void operator<(S, S)", "operator<"); // TODO FAIL DO_TEST("void operator<<(S, S)", "operator<<"); DO_TEST("void operator< (S, S)", "operator<"); DO_TEST("void operator<< (S, S)", "operator<<"); } TEST(PruneSymbolTests, OperatorNewDeleteCoAwait) { // https://godbolt.org/z/rq16K9sK3 DO_TEST("operator new(unsigned long)", "operator new"); DO_TEST("operator new[](unsigned long)", "operator new[]"); DO_TEST("operator delete(unsigned long)", "operator delete"); DO_TEST("operator delete[](unsigned long)", "operator delete[]"); DO_TEST("S::operator new(unsigned long)", "S::operator new"); DO_TEST("S::operator new[](unsigned long)", "S::operator new[]"); DO_TEST("S::operator delete(void*)", "S::operator delete"); DO_TEST("S::operator delete[](void*)", "S::operator delete[]"); DO_TEST("void * operator new(unsigned __int64)", "operator new"); DO_TEST("void * operator new[](unsigned __int64)", "operator new[]"); DO_TEST("void operator delete(void *)", "operator delete"); DO_TEST("void operator delete[](void *)", "operator delete[]"); DO_TEST("static void * S::operator new(unsigned __int64)", "S::operator new"); DO_TEST("static void * S::operator new[](unsigned __int64)", "S::operator new[]"); DO_TEST("static void S::operator delete(void *)", "S::operator delete"); DO_TEST("static void S::operator delete[](void *)", "S::operator delete[]"); // https://godbolt.org/z/a3GeKjh5a DO_TEST("operator co_await(A)", "operator co_await"); DO_TEST("B::operator co_await()", "B::operator co_await"); DO_TEST("void operator co_await(A)", "operator co_await"); DO_TEST("void B::operator co_await(void)", "B::operator co_await"); } TEST(PruneSymbolTests, NTTPs) { // https://godbolt.org/z/4aPavzsba DO_TEST("void foo<12, 20, 256, 1>()", "foo"); DO_TEST("void foo()", "foo"); DO_TEST("void foo<(char)97, (char)98, (char)0, (char)39>()", "foo"); DO_TEST("void foo<0x1p-1, 0x1.8p+3f, 0x1.00aabbccp+10>()", "foo"); DO_TEST("void foo<&p>()", "foo"); DO_TEST("void foo<&main>()", "foo"); DO_TEST("void foo<&void foo<20, true>()>()", "foo"); DO_TEST("void foo<12,20,256,1>(void)", "foo"); DO_TEST("void foo<1,0>(void)", "foo"); DO_TEST("void foo<0.500000,12.000000,1026.667712>(void)", "foo"); DO_TEST("void foo<&int p>(void)", "foo"); DO_TEST("void foo<&void foo<20,1>(void)>(void)", "foo"); // https://godbolt.org/z/WEz836Yv7 DO_TEST("void foo{\"foobar`'\\\"bar\"}>()", "foo"); DO_TEST("void foo{char{102,111,111,98,97,114,96,39,34,98,97,114,0}}>(void)", "foo"); DO_TEST("void foo{\"foobar`'\\\"bar\"}>()::test", "foo::test"); DO_TEST("void foo{\"foobar`\\\"bar\\\"'\"}>()::test", "foo::test"); DO_TEST("void foo{\"foobar`\\\"bar'\"}>()::test", "foo::test"); DO_TEST("void foo{char{102,111,111,98,97,114,96,34,98,97,114,34,39,0}}>(void)::test", "foo::test"); // test that unterminated strings cause errors DO_TEST("void foo::baz", "foo::baz"); DO_TEST("void foo::baz", "foo::baz"); DO_TEST("void foo::baz", "void foo::baz"); DO_TEST("void foo::baz", "void foo::baz"); } TEST(PruneSymbolTests, OperatorNTTPs) { // https://godbolt.org/z/foY7WfGv3 DO_TEST("void foo<&S::operator<(S const&)>()", "foo"); DO_TEST("void foo<&S::operator>(S const&)>()", "foo"); DO_TEST("void foo<&S::operator>>(S const&)>()", "foo"); DO_TEST("void foo<&S::operator>>=(S const&)>()", "foo"); DO_TEST("void foo<&S::operator<=(S const&)>()", "foo"); DO_TEST("void foo<&S::operator<<=(S const&)>()", "foo"); // DO_TEST("void foo<&bool X::operator<(X const&)>()", "foo"); // TODO: FAIL DO_TEST("void foo<&bool X::operator>(X const&)>()", "foo"); DO_TEST("void foo<&bool X::operator>>(X const&)>()", "foo"); DO_TEST("void foo<&bool X::operator>>=(X const&)>()", "foo"); DO_TEST("void foo<&bool X::operator<=(X const&)>()", "foo"); DO_TEST("void foo<&bool X::operator<<=(X const&)>()", "foo"); DO_TEST("void foo<&bool S::operator<(S const &)>(void)", "foo"); DO_TEST("void foo<&bool S::operator>(S const &)>(void)", "foo"); DO_TEST("void foo<&bool S::operator>>(S const &)>(void)", "foo"); DO_TEST("void foo<&bool S::operator>>=(S const &)>(void)", "foo"); DO_TEST("void foo<&bool S::operator<=(S const &)>(void)", "foo"); DO_TEST("void foo<&bool S::operator<<=(S const &)>(void)", "foo"); // DO_TEST("void foo<&bool X::operator<(X const &)>(void)", "foo"); // TODO: FAIL DO_TEST("void foo<&bool X::operator>(X const &)>(void)", "foo"); DO_TEST("void foo<&bool X::operator>>(X const &)>(void)", "foo"); DO_TEST("void foo<&bool X::operator>>=(X const &)>(void)", "foo"); DO_TEST("void foo<&bool X::operator<=(X const &)>(void)", "foo"); DO_TEST("void foo<&bool X::operator<<=(X const &)>(void)", "foo"); DO_TEST("void foo<&S::operator>(S const&)>()::test", "foo::test"); DO_TEST("void foo<&S::operator>>(S const&)>()::test", "foo::test"); DO_TEST("void foo<&S::operator>>=(S const&)>()::test", "foo::test"); DO_TEST("void foo<&bool S::operator>(S const &)>(void)::test", "foo::test"); DO_TEST("void foo<&bool S::operator>>(S const &)>(void)::test", "foo::test"); DO_TEST("void foo<&bool S::operator>>=(S const &)>(void)::test", "foo::test"); DO_TEST("void foo<&bool X::operator>(X const &)>(void)::test", "foo::test"); DO_TEST("void foo<&bool X::operator>>(X const &)>(void)::test", "foo::test"); DO_TEST("void foo<&bool X::operator>>=(X const &)>(void)::test", "foo::test"); DO_TEST("void foo<&S::operator>(S const&), bar>()::test", "foo::test"); DO_TEST("void foo<&S::operator>>(S const&), bar>()::test", "foo::test"); DO_TEST("void foo<&S::operator>>=(S const&), bar>()::test", "foo::test"); DO_TEST("void foo<&bool S::operator>(S const &)>(vo, barid)::test", "foo::test"); DO_TEST("void foo<&bool S::operator>>(S const &)>(vo, barid)::test", "foo::test"); DO_TEST("void foo<&bool S::operator>>=(S const &)>(vo, barid)::test", "foo::test"); DO_TEST("void foo<&bool X::operator>(X const &)>(vo, barid)::test", "foo::test"); DO_TEST("void foo<&bool X::operator>>(X const &)>(vo, barid)::test", "foo::test"); DO_TEST("void foo<&bool X::operator>>=(X const &)>(vo, barid)::test", "foo::test"); // TODO: foo<&S::operator>>() isn't legal C++ but maybe it could appear in demangled output? } TEST(PruneSymbolTests, BasicLambdas) { // https://godbolt.org/z/5n83rGK8j DO_TEST("main::'lambda'()::operator()() const", "main::::operator()"); DO_TEST("main::'lambda0'()::operator()() const", "main::::operator()"); DO_TEST("main::{lambda()#1}::operator()() const", "main::::operator()"); DO_TEST("main::{lambda()#2}::operator()() const", "main::::operator()"); DO_TEST("main::$_0::operator()() const", "main::$_0::operator()"); DO_TEST("main::$_1::operator()() const", "main::$_1::operator()"); DO_TEST("`int main(void)'::`2'::::operator()(void)const", "`main'::`2'::::operator()"); DO_TEST("`int main(void)'::`2'::::operator()(void)const", "`main'::`2'::::operator()"); } TEST(PruneSymbolTests, TemplatedLambdas) { // https://godbolt.org/z/GGEWfE144 DO_TEST("auto main::'lambda'($T)::operator()($T) const", "main::::operator()"); DO_TEST("auto main::{lambda($T0)#1}::operator()(int) const", "main::::operator()"); // DO_TEST("", ""); // TODO: llvm-symbolizer can't handle currently DO_TEST("auto `int main(void)'::`2'::::operator()(int)const", "`main'::`2'::::operator()"); } TEST(PruneSymbolTests, NestedLambdas) { // https://godbolt.org/z/s4GseqrGK DO_TEST("main::'lambda'()::operator()() const", "main::::operator()"); DO_TEST("main::'lambda'()::operator()() const::'lambda'()::operator()() const", "main::::operator()::::operator()"); DO_TEST("main::{lambda()#1}::operator()() const", "main::::operator()"); DO_TEST("main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const", "main::::operator()::::operator()"); DO_TEST("main::$_0::operator()() const", "main::$_0::operator()"); DO_TEST("main::$_0::operator()() const::'lambda'()::operator()() const", "main::$_0::operator()::::operator()"); DO_TEST("`int main(void)'::`2'::::operator()(void)const", "`main'::`2'::::operator()"); DO_TEST("``int main(void)'::`2'::::operator()(void)const '::`2'::::operator()(void)const", "``main'::`2'::::operator()'::`2'::::operator()"); // https://godbolt.org/z/fnvW63819 DO_TEST("auto main::'lambda'($T)::operator()($T) const", "main::::operator()"); DO_TEST("auto auto main::'lambda'($T)::operator()($T) const::'lambda'($T)::operator()($T) const", "main::::operator()::::operator()"); DO_TEST("auto main::{lambda($T0)#1}::operator()(int) const", "main::::operator()"); DO_TEST("auto main::{lambda($T0)#1}::operator()(int) const::{lambda($T0)#1}::operator()(int) const", "main::::operator()::::operator()"); // TODO: LLVM // TODO: LLVM DO_TEST("auto `int main(void)'::`2'::::operator()(int)const", "`main'::`2'::::operator()"); DO_TEST("auto `auto `int main(void)'::`2'::::operator()(int)const '::`2'::::operator()(int)const", "``main'::`2'::::operator()'::`2'::::operator()"); } TEST(PruneSymbolTests, LambdaTemplateArgs) { // https://godbolt.org/z/9f53KezPE DO_TEST("S::foo()", "S::foo"); DO_TEST("SS::foo()", "SS::foo"); DO_TEST("S::foo()", "S::foo"); DO_TEST("SS::foo()", "SS::foo"); DO_TEST("void S<`int main(void)'::`2':: >::foo(void)", "S::foo"); DO_TEST("void SS<`int main(void)'::`2'::{}>::foo(void)", "SS::foo"); } TEST(PruneSymbolTests, LambdasInTemplates) { // https://godbolt.org/z/qKv8xz7Mv DO_TEST("S::foo()::'lambda'()::operator()() const", "S::foo::::operator()"); DO_TEST("S::foo()::{lambda()#1}::operator()() const", "S::foo::::operator()"); DO_TEST("`void S::foo(void)'::`2'::::operator()(void)const", "`S::foo'::`2'::::operator()"); } TEST(PruneSymbolTests, LocalTypes) { // https://godbolt.org/z/51fbhMTMe DO_TEST("foo()::S::bar()", "foo::S::bar"); DO_TEST("void `void foo(void)'::`2'::S::bar(void)", "`foo'::`2'::S::bar"); DO_TEST("foo()::S::bar()::'lambda'()::operator()() const::V::boo()", "foo::S::bar::::operator()::V::boo"); DO_TEST("foo()::S::bar()::{lambda()#1}::operator()() const::V::boo()", "foo::S::bar::::operator()::V::boo"); DO_TEST("void ``void `void foo(void)'::`2'::S::bar(void)'::`2'::::operator()(void)const '::`2'::V::boo(void)", "```foo'::`2'::S::bar'::`2'::::operator()'::`2'::V::boo"); // https://godbolt.org/z/hGaW8j9r3 DO_TEST("auto auto A::foo()::'lambda'(auto)::operator()(auto) const", "A::foo::::operator()"); DO_TEST("auto auto A::foo()::'lambda'(auto)::operator()(auto) const::S::foo()", "A::foo::::operator()::S::foo"); DO_TEST("auto A::foo()::{lambda(auto:1)#1}::operator()(int) const", "A::foo::::operator()"); DO_TEST("A::foo()::{lambda(auto:1)#1}::operator()(int) const::S::foo()", "A::foo::::operator()::S::foo"); DO_TEST("auto `auto A::foo(void)'::`2'::::operator()(int)const", "`A::foo'::`2'::::operator()"); DO_TEST("void `auto `auto A::foo(void)'::`2'::::operator()(int)const '::`2'::S::foo(void)", "``A::foo'::`2'::::operator()'::`2'::S::foo"); } TEST(PruneSymbolTests, QualifiersAndAttributes) { // https://godbolt.org/z/rG5Ed5qed DO_TEST("S::foo() const volatile &&", "S::foo"); DO_TEST("int const && S::foo(void)const volatile &&", "S::foo"); DO_TEST("auto main::'lambda'(auto const volatile&&)::operator()<'lambda'(auto const volatile&&)>(this auto const volatile&&)", "main::::operator()"); DO_TEST("auto main::{lambda(auto:1 const volatile&&)#1}::operator()<{lambda(auto:1 const volatile&&)#1}>(this {lambda(auto:1 const volatile&&)#1} const volatile&&)", "main::::operator()"); DO_TEST("static auto `int main(void)'::`2'::::operator()<`int main(void)'::`2':: >(UNKNOWN,`int main(void)'::`2':: const volatile &&)", "`main'::`2'::::operator()"); } TEST(PruneSymbolTests, ConversionOperator) { // https://godbolt.org/z/v8hc1vb9P DO_TEST("S::operator int()", "S::operator int"); DO_TEST("S::operator void*()", "S::operator void*"); DO_TEST("S::operator std::nullptr_t()", "S::operator std::nullptr_t"); DO_TEST("S::operator X()", "S::operator X"); DO_TEST("S::operator ns::Z()", "S::operator ns::Z"); DO_TEST("S::operator ns::Y()", "S::operator ns::Y"); DO_TEST("S::operator main::'lambda'()()", "S::operator main::"); DO_TEST("S::operator main::'lambda'()()::'lambda'()::operator()() const", "S::operator main::::::operator()"); DO_TEST("S::operator decltype(nullptr)()", "S::operator decltype(nullptr)"); DO_TEST("S::operator main::{lambda()#1}()", "S::operator main::"); DO_TEST("S::operator main::{lambda()#1}()::{lambda()#1}::operator()() const", "S::operator main::::::operator()"); DO_TEST("S::operator main::$_0()", "S::operator main::$_0"); DO_TEST("S::operator main::$_0()::'lambda'()::operator()() const", "S::operator main::$_0::::operator()"); // https://godbolt.org/z/4qqP9reqr DO_TEST("S::operator S::operator*()::X()", "S::operator S::operator*::X"); // DO_TEST("S::operator<`S::operator*(void)'::`2'::X> `S::operator*(void)'::`2'::X(void)", "S::operator `S::operator*'::`2'::X"); // TODO FAIL DO_TEST("S::operator T&()", "S::operator T&"); DO_TEST("S::operator T&&()", "S::operator T&&"); // https://godbolt.org/z/PTe1xh6Go DO_TEST("S::operator int X::*()", "S::operator int X::*"); DO_TEST("S::operator int Y::*()", "S::operator int Y::*"); DO_TEST("S::operator std::vector X::*()", "S::operator std::vector X::*"); DO_TEST("S::operator std::vector Y::*()", "S::operator std::vector Y::*"); DO_TEST("S::operator std::vector ns::ns::X::*()", "S::operator std::vector ns::ns::X::*"); DO_TEST("S::operator std::vector ns::ns::Y::*()", "S::operator std::vector ns::ns::Y::*"); DO_TEST("S::operator std::vector ns::ns::X::*()::test", "S::operator std::vector ns::ns::X::*::test"); DO_TEST("S::operator std::vector ns::ns::Y::*()::test", "S::operator std::vector ns::ns::Y::*::test"); } TEST(PruneSymbolTests, DeducedConversionOperator) { // https://godbolt.org/z/9rzdKvGh7 DO_TEST("S::operator auto()", "S::operator auto"); DO_TEST("S::operator auto()", "S::operator auto"); DO_TEST("S::operator decltype(auto)()", "S::operator decltype(auto)"); // DO_TEST("S::operator (void)", "S::operator"); // Microsoft's lovely demangling // DO_TEST("S::operator (void)::test", "S::operator::test"); DO_TEST("S::operator float(void)", "S::operator float"); } TEST(PruneSymbolTests, FunctionPointers) { // https://godbolt.org/z/TWfa4f6Kc DO_TEST("void (*foo())(int, double)", "foo"); DO_TEST("void (**foo())(int, double)", "foo"); DO_TEST("void (&baz())(int, double)", "baz"); DO_TEST("void (** (**bar())(int, double))(int, double)", "bar"); DO_TEST("void (**(**bar())(int, double))(int, double)", "bar"); DO_TEST("void (__cdecl** foo(void))(int,double)", "foo"); DO_TEST("void (__cdecl** (__cdecl** bar(void))(int,double))(int,double)", "bar"); DO_TEST("void (__cdecl&baz(void))(int,double)", "baz"); } TEST(PruneSymbolTests, UnnamedTypes) { // https://godbolt.org/z/jx8GnrW4v DO_TEST("main::'unnamed'::foo()", "main::::foo"); DO_TEST("main::{unnamed type#1}::foo()", "main::::foo"); } TEST(PruneSymbolTests, TemplateHeavySymbols) { // https://godbolt.org/z/z1nrMsYfs DO_TEST("__find_if<__gnu_cxx::__normal_iterator >, __gnu_cxx::__ops::_Iter_pred > >", "__find_if"); // DO_TEST("operator()<__gnu_cxx::__normal_iterator >, __gnu_cxx::__normal_iterator >, std::identity, main():: >", "ns::SS::SS"); DO_TEST("std::__1::find_if[abi:ne200100], main::$_1>(std::__1::__wrap_iter, std::__1::__wrap_iter, main::$_1)", "std::__1::find_if"); DO_TEST("std::__1::_IfImpl>::_Select())), std::__1::ranges::dangling> std::__1::ranges::__find_if::operator()[abi:ne200100]>&, std::__1::identity, main::$_0>(T&&, main::$_0, T0) const", "std::__1::ranges::__find_if::operator()"); DO_TEST("std::__1::ranges::__find_if_impl[abi:ne200100], std::__1::__wrap_iter, main::$_0, std::__1::identity>(std::__1::__wrap_iter, std::__1::__wrap_iter, main::$_0&, std::__1::identity&)", "std::__1::ranges::__find_if_impl"); } TEST(PruneSymbolTests, StorageClasses) { // https://godbolt.org/z/xPYKW8Pz5 DO_TEST("static void S::foo(void)", "S::foo"); } TEST(PruneSymbolTests, Noexcept) { // https://godbolt.org/z/xjsM67s17 DO_TEST("void foo(X (*)() noexcept(X::n))", "foo"); } TEST(PruneSymbolTests, MiscNesting) { // https://godbolt.org/z/5Gj99ernr DO_TEST("void use1<5>(Wrapper<(5)<(5)>)", "use1"); DO_TEST("void use2<5>(Wrapper<((5)>(5))>)", "use2"); DO_TEST("void use1<5>(Wrapper<5 < 5>)", "use1"); DO_TEST("void use2<5>(Wrapper<(5 > 5)>)", "use2"); DO_TEST("void use1<5>(Wrapper<0>)", "use1"); DO_TEST("void use2<5>(Wrapper<0>)", "use2"); } TEST(PruneSymbolTests, Misc) { // https://godbolt.org/z/cqfW7MK57 DO_TEST("foo(...)", "foo"); } TEST(PruneSymbolTests, Extra) { DO_TEST("operator<<(std::ostream&, Foo const&)", "operator<<"); DO_TEST("std::ostream & operator<<(std::ostream &, Foo const &)", "operator<<"); DO_TEST("Foo::operator+=(int)", "Foo::operator+="); DO_TEST("Foo::operator+=(int) const", "Foo::operator+="); DO_TEST("Foo::operator bool() const noexcept", "Foo::operator bool"); DO_TEST("Foo::operator int() const", "Foo::operator int"); DO_TEST("Foo::bar() const", "Foo::bar"); DO_TEST("Foo::bar() volatile", "Foo::bar"); DO_TEST("Foo::bar() const &", "Foo::bar"); DO_TEST("Foo::bar() &&", "Foo::bar"); DO_TEST("Foo::bar() const noexcept", "Foo::bar"); DO_TEST("Foo::bar() const & noexcept", "Foo::bar"); DO_TEST("void foo<3>()", "foo"); DO_TEST("void foo<'a'>()", "foo"); DO_TEST("void foo()", "foo"); DO_TEST("void foo<&Foo::bar>()", "foo"); DO_TEST("void foo<(int)5>()", "foo"); DO_TEST("main::{lambda()#1}::operator()() const", "main::::operator()"); DO_TEST("main::$_0::operator()() const", "main::$_0::operator()"); DO_TEST("virtual void __cdecl ns::Foo::bar(int) const", "ns::Foo::bar"); DO_TEST("static int __cdecl ns::Foo::baz(char)", "ns::Foo::baz"); DO_TEST("__thiscall Foo::Foo(void)", "Foo::Foo"); DO_TEST("virtual __thiscall Foo::~Foo(void)", "Foo::~Foo"); DO_TEST("virtual thunk to Foo::bar()", "Foo::bar"); DO_TEST("void foo>>(std::vector> const&)", "foo"); DO_TEST("void foo>(std::basic_string const&)", "foo"); } TEST(PruneSymbolTests, RegressionTests) { // Symbols I've observed not pruning correctly DO_TEST("void (* const&std::_Any_data::_M_access() const)(cpptrace::v1::log_level, char const*)", "std::_Any_data::_M_access"); DO_TEST("void (* const*std::__addressof(void (* const&)(cpptrace::v1::log_level, char const*)))(cpptrace::v1::log_level, char const*)", "std::__addressof"); DO_TEST("void (* const&std::forward(std::remove_reference::type&))(cpptrace::v1::log_level, char const*)", "std::forward"); DO_TEST("fmt::v10::detail::parse_format_specs(char const*, char const*, fmt::v10::detail::dynamic_format_specs&, fmt::v10::basic_format_parse_context&, fmt::v10::detail::type)::{unnamed type#1}::operator()(fmt::v10::detail::state, bool)", "fmt::v10::detail::parse_format_specs::::operator()"); DO_TEST("fmt::v10::detail::parse_format_specs(char const*, char const*, fmt::v10::detail::dynamic_format_specs&, fmt::v10::basic_format_parse_context&, fmt::v10::detail::type)::{unnamed type#2}::operator()(fmt::v10::presentation_type, int)", "fmt::v10::detail::parse_format_specs::::operator()"); } } cpptrace-1.0.4/test/unit/main.cpp000066400000000000000000000004601504061443700167070ustar00rootroot00000000000000#include #ifdef TEST_MODULE import cpptrace; #else #include #endif int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); cpptrace::absorb_trace_exceptions(false); cpptrace::use_default_stderr_logger(); return RUN_ALL_TESTS(); } cpptrace-1.0.4/test/unit/tracing/000077500000000000000000000000001504061443700167065ustar00rootroot00000000000000cpptrace-1.0.4/test/unit/tracing/common.hpp000066400000000000000000000010261504061443700207060ustar00rootroot00000000000000#ifndef TRACING_COMMON_HPP #define TRACING_COMMON_HPP template using void_t = void; #ifndef CPPTRACE_BUILD_NO_SYMBOLS #define EXPECT_FILE(A, B) EXPECT_THAT((A), testing::EndsWith(B)) #define EXPECT_LINE(A, B) EXPECT_EQ((A), (B)) #else #define EXPECT_FILE(A, B) (void_t)0 #define EXPECT_LINE(A, B) (void_t)0 #endif #ifdef _MSC_VER #define CPPTRACE_FORCE_NO_INLINE __declspec(noinline) #else #define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline)) #endif #endif cpptrace-1.0.4/test/unit/tracing/from_current.cpp000066400000000000000000000215121504061443700221200ustar00rootroot00000000000000#include #include #include #include #include #include #include "common.hpp" #include "utils/utils.hpp" #ifdef TEST_MODULE import cpptrace; #include #else #include #include #endif static volatile int truthy = 2; // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; if(truthy) { // due to a MSVC warning about unreachable code line_numbers.insert(line_numbers.begin(), __LINE__ + 1); throw std::runtime_error("foobar"); } return 2; } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_2(line_numbers) * rand(); } TEST(FromCurrent, Basic) { std::vector line_numbers; bool does_enter_catch = false; auto guard = cpptrace::detail::scope_exit([&] { EXPECT_TRUE(does_enter_catch); }); CPPTRACE_TRY { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); static volatile int tco_guard = stacktrace_from_current_1(line_numbers); (void)tco_guard; } CPPTRACE_CATCH(const std::runtime_error& e) { does_enter_catch = true; EXPECT_FALSE(cpptrace::current_exception_was_rethrown()); EXPECT_EQ(e.what(), std::string("foobar")); const auto& trace = cpptrace::from_current_exception(); ASSERT_GE(trace.frames.size(), 4); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_3") != std::string::npos; } ); ASSERT_NE(it, trace.frames.end()) << trace; size_t i = static_cast(it - trace.frames.begin()); int j = 0; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_3")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_2")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_1")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("FromCurrent_Basic_Test::TestBody")); } } TEST(FromCurrent, CorrectHandler) { std::vector line_numbers; bool wrong_handler = false; CPPTRACE_TRY { CPPTRACE_TRY { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_from_current_1(line_numbers); } CPPTRACE_CATCH(const std::logic_error&) { wrong_handler = true; } } CPPTRACE_CATCH(const std::exception& e) { EXPECT_EQ(e.what(), std::string("foobar")); const auto& trace = cpptrace::from_current_exception(); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_3") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("FromCurrent_CorrectHandler_Test::TestBody") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); } if(wrong_handler) { FAIL(); } } TEST(FromCurrent, RawTrace) { std::vector line_numbers; CPPTRACE_TRY { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); static volatile int tco_guard = stacktrace_from_current_1(line_numbers); (void)tco_guard; } CPPTRACE_CATCH(const std::exception& e) { EXPECT_EQ(e.what(), std::string("foobar")); const auto& raw_trace = cpptrace::raw_trace_from_current_exception(); auto trace = raw_trace.resolve(); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_3") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("FromCurrent_RawTrace_Test::TestBody") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); } } TEST(FromCurrent, NonThrowingPath) { bool does_enter_catch = false; bool does_reach_end = false; auto guard = cpptrace::detail::scope_exit([&] { EXPECT_FALSE(does_enter_catch); EXPECT_TRUE(does_reach_end); }); CPPTRACE_TRY { // pass } CPPTRACE_CATCH(const std::runtime_error&) { does_enter_catch = true; } does_reach_end = true; } #ifdef _MSC_VER CPPTRACE_FORCE_NO_INLINE int my_div_function(int x, int y) { return x / y; } int divide_zero_filter(int code) { if(code == STATUS_INTEGER_DIVIDE_BY_ZERO || code == EXCEPTION_FLT_DIVIDE_BY_ZERO) { return EXCEPTION_EXECUTE_HANDLER; } return EXCEPTION_CONTINUE_SEARCH; } TEST(FromCurrent, SEHBasic) { bool does_enter_catch = false; auto guard = cpptrace::detail::scope_exit([&] { EXPECT_TRUE(does_enter_catch); }); [&] () { CPPTRACE_SEH_TRY { [&] () { volatile auto res = my_div_function(10, 0); (void)res; } (); } CPPTRACE_SEH_EXCEPT(divide_zero_filter(GetExceptionCode())) { [&] () { does_enter_catch = true; EXPECT_FALSE(cpptrace::current_exception_was_rethrown()); const auto& trace = cpptrace::from_current_exception(); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("my_div_function") != std::string::npos; } ); ASSERT_NE(it, trace.frames.end()) << trace; size_t i = static_cast(it - trace.frames.begin()); EXPECT_FILE(trace.frames[i].filename, "from_current.cpp"); } (); } } (); EXPECT_TRUE(does_enter_catch); } TEST(FromCurrent, SEHCorrectHandler) { bool does_enter_catch = false; auto guard = cpptrace::detail::scope_exit([&] { EXPECT_TRUE(does_enter_catch); }); [&] () { CPPTRACE_SEH_TRY { [&] () { CPPTRACE_SEH_TRY { [&] () { volatile auto res = my_div_function(10, 0); (void)res; } (); } CPPTRACE_SEH_EXCEPT(EXCEPTION_CONTINUE_SEARCH) { [&] () { FAIL(); } (); } } (); } CPPTRACE_SEH_EXCEPT(divide_zero_filter(GetExceptionCode())) { does_enter_catch = true; } } (); EXPECT_TRUE(does_enter_catch); } #endif cpptrace-1.0.4/test/unit/tracing/from_current_try_catch.cpp000066400000000000000000000150561504061443700241660ustar00rootroot00000000000000#include #include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #else #include #include #endif static volatile int truthy = 2; // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_try_catch_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; if(truthy) { // due to a MSVC warning about unreachable code line_numbers.insert(line_numbers.begin(), __LINE__ + 1); throw std::runtime_error("foobar"); } return 2; } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_try_catch_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_try_catch_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_try_catch_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_try_catch_2(line_numbers) * rand(); } TEST(FromCurrentTryCatch, Basic) { std::vector line_numbers; cpptrace::try_catch( [&] { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); static volatile int tco_guard = stacktrace_from_current_try_catch_1(line_numbers); (void)tco_guard; }, [&] (const std::runtime_error& e) { EXPECT_FALSE(cpptrace::current_exception_was_rethrown()); EXPECT_EQ(e.what(), std::string("foobar")); const auto& trace = cpptrace::from_current_exception(); ASSERT_GE(trace.frames.size(), 4); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_try_catch_3") != std::string::npos; } ); ASSERT_NE(it, trace.frames.end()); size_t i = static_cast(it - trace.frames.begin()); int j = 0; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current_try_catch.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_try_catch_3")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current_try_catch.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_try_catch_2")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current_try_catch.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_try_catch_1")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "from_current_try_catch.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); } ); } TEST(FromCurrentTryCatch, CorrectHandler) { std::vector line_numbers; cpptrace::try_catch( [&] { cpptrace::try_catch( [&] { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_from_current_try_catch_1(line_numbers); }, [&] (const std::logic_error&) { FAIL(); } ); }, [&] (const std::logic_error&) { FAIL(); }, [&] (const std::runtime_error& e) { EXPECT_EQ(e.what(), std::string("foobar")); const auto& trace = cpptrace::from_current_exception(); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_try_catch_3") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("FromCurrentTryCatch_CorrectHandler_Test::TestBody") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); } ); } TEST(FromCurrentTryCatch, RawTrace) { std::vector line_numbers; cpptrace::try_catch( [&] { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); static volatile int tco_guard = stacktrace_from_current_try_catch_1(line_numbers); (void)tco_guard; }, [&] (const std::runtime_error& e) { EXPECT_EQ(e.what(), std::string("foobar")); const auto& raw_trace = cpptrace::raw_trace_from_current_exception(); auto trace = raw_trace.resolve(); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_try_catch_3") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("FromCurrentTryCatch_RawTrace_Test::TestBody") != std::string::npos; } ); EXPECT_NE(it, trace.frames.end()); } ); } cpptrace-1.0.4/test/unit/tracing/object_trace.cpp000066400000000000000000000103301504061443700220330ustar00rootroot00000000000000#include #include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #else #include #endif TEST(ObjectTrace, Empty) { cpptrace::object_trace empty; EXPECT_TRUE(empty.empty()); EXPECT_EQ(empty.resolve().to_string(), "Stack trace (most recent call first):\n"); } CPPTRACE_FORCE_NO_INLINE void object_basic() { static volatile int lto_guard; lto_guard = lto_guard + 1; auto trace = cpptrace::generate_object_trace(); EXPECT_FALSE(trace.empty()); EXPECT_NE(trace.frames[0].raw_address, 0); EXPECT_NE(trace.frames[0].object_address, 0); EXPECT_THAT(trace.frames[0].object_path, testing::HasSubstr("unittest")); } TEST(ObjectTrace, Basic) { object_basic(); } CPPTRACE_FORCE_NO_INLINE void object_basic_resolution() { static volatile int lto_guard; lto_guard = lto_guard + 1; auto line = __LINE__ + 1; auto trace = cpptrace::generate_object_trace().resolve(); ASSERT_GE(trace.frames.size(), 1); EXPECT_FILE(trace.frames[0].filename, "object_trace.cpp"); EXPECT_EQ(trace.frames[0].line.value(), line); EXPECT_THAT(trace.frames[0].symbol, testing::HasSubstr("object_basic_resolution")); } TEST(ObjectTrace, BasicResolution) { object_basic(); } // TODO: dbghelp uses raw address, not object #ifndef _MSC_VER CPPTRACE_FORCE_NO_INLINE int object_resolve_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto dummy = cpptrace::generate_trace(); auto dummy_otrace = cpptrace::generate_object_trace(); cpptrace::object_trace otrace; otrace.frames.push_back( cpptrace::object_frame{0, dummy.frames[0].object_address, dummy_otrace.frames[0].object_path} ); otrace.frames.push_back( cpptrace::object_frame{0, dummy.frames[1].object_address, dummy_otrace.frames[1].object_path} ); otrace.frames.push_back( cpptrace::object_frame{0, dummy.frames[2].object_address, dummy_otrace.frames[2].object_path} ); otrace.frames.push_back( cpptrace::object_frame{0, dummy.frames[3].object_address, dummy_otrace.frames[3].object_path} ); auto trace = otrace.resolve(); if(trace.frames.size() < 4) { ADD_FAILURE() << "trace.frames.size() >= 4"; return 2; } int i = 0; EXPECT_FILE(trace.frames[i].filename, "object_trace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("object_resolve_3")); i++; EXPECT_FILE(trace.frames[i].filename, "object_trace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("object_resolve_2")); i++; EXPECT_FILE(trace.frames[i].filename, "object_trace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("object_resolve_1")); i++; EXPECT_FILE(trace.frames[i].filename, "object_trace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("ObjectTrace_Resolution_Test::TestBody")); return 2; } // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int object_resolve_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return object_resolve_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int object_resolve_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return object_resolve_2(line_numbers) * rand(); } TEST(ObjectTrace, Resolution) { std::vector line_numbers; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); object_resolve_1(line_numbers); } #endif cpptrace-1.0.4/test/unit/tracing/raw_trace.cpp000066400000000000000000000130571504061443700213670ustar00rootroot00000000000000#include #include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #else #include #endif // Raw trace tests // This is fickle, however, it's the only way to do it really. I've gotten it reasonably reliable test in practice. // Sanitizers do interfere. #ifndef CPPTRACE_SANITIZER_BUILD // NOTE: MSVC likes creating trampoline-like entries for non-static functions CPPTRACE_FORCE_NO_INLINE static void raw_trace_basic() { static volatile int lto_guard; lto_guard = lto_guard + 1; auto raw_trace = cpptrace::generate_raw_trace(); // look for within 90 bytes of the start of the function ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_basic)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_basic) + 90); } #ifndef _MSC_VER CPPTRACE_FORCE_NO_INLINE void raw_trace_basic_precise() { static volatile int lto_guard; lto_guard = lto_guard + 1; a: auto raw_trace = cpptrace::generate_raw_trace(); b: // This is stupid, but without it gcc was optimizing both &&a and &&b to point to the start of the function's body volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); } #endif TEST(RawTrace, Basic) { raw_trace_basic(); #ifndef _MSC_VER raw_trace_basic_precise(); #endif volatile int x = 0; // prevent raw_trace_basic_precise() above being a jmp (void)x; } CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_2( cpptrace::frame_ptr parent_low_bound, cpptrace::frame_ptr parent_high_bound ) { static volatile int lto_guard; lto_guard = lto_guard + 1; auto raw_trace = cpptrace::generate_raw_trace(); ASSERT_GE(raw_trace.frames.size(), 2); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_2)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_2) + 100); EXPECT_GE(raw_trace.frames[1], parent_low_bound); EXPECT_LE(raw_trace.frames[1], parent_high_bound); } CPPTRACE_FORCE_NO_INLINE static void raw_trace_multi_1() { static volatile int lto_guard; lto_guard = lto_guard + 1; auto raw_trace = cpptrace::generate_raw_trace(); raw_trace_multi_2(reinterpret_cast(raw_trace_multi_1), reinterpret_cast(raw_trace_multi_1) + 300); ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1) + 90); } std::vector> parents; CPPTRACE_FORCE_NO_INLINE void record_parent(uintptr_t low_bound, uintptr_t high_bound) { static volatile int lto_guard; lto_guard = lto_guard + 1; parents.insert(parents.begin(), {low_bound, high_bound}); } #ifndef _MSC_VER CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_3() { static volatile int lto_guard; lto_guard = lto_guard + 1; a: auto raw_trace = cpptrace::generate_raw_trace(); b: volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), parents.size() + 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); // this frame EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); for(size_t i = 0; i < parents.size(); i++) { // parent frames EXPECT_GE(raw_trace.frames[i + 1], parents[i].first); EXPECT_LE(raw_trace.frames[i + 1], parents[i].second); } } CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2() { static volatile int lto_guard; lto_guard = lto_guard + 1; a: auto raw_trace = cpptrace::generate_raw_trace(); b: volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), parents.size() + 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); // this frame EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); for(size_t i = 0; i < parents.size(); i++) { // parent frames EXPECT_GE(raw_trace.frames[i + 1], parents[i].first); EXPECT_LE(raw_trace.frames[i + 1], parents[i].second); } record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: raw_trace_multi_precise_3(); d: if(x) { goto* &&c; } if(x) { goto* &&d; } } CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_1() { static volatile int lto_guard; lto_guard = lto_guard + 1; a: auto raw_trace = cpptrace::generate_raw_trace(); b: volatile auto x = 0; if(x) { goto* &&a; } if(x) { goto* &&b; } ASSERT_GE(raw_trace.frames.size(), 1); EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: raw_trace_multi_precise_2(); d: if(x) { goto* &&c; } if(x) { goto* &&d; } } #endif TEST(RawTrace, MultipleCalls) { parents.clear(); raw_trace_multi_1(); #ifndef _MSC_VER raw_trace_multi_precise_1(); #endif } #endif cpptrace-1.0.4/test/unit/tracing/rethrow.cpp000066400000000000000000000273511504061443700211140ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #include #else #include #include #endif static volatile int truthy = 2; // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_rethrow_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; if(truthy) { // due to a MSVC warning about unreachable code line_numbers.insert(line_numbers.begin(), __LINE__ + 1); throw std::runtime_error("foobar"); } return 2; } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_rethrow_2(std::vector& line_numbers, std::vector& rethrow_line_numbers) { int ret; CPPTRACE_TRY { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); ret = stacktrace_from_current_rethrow_3(line_numbers) * rand(); } CPPTRACE_CATCH(const std::exception&) { rethrow_line_numbers.insert(rethrow_line_numbers.begin(), __LINE__ + 1); cpptrace::rethrow(); } return ret; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4702) // unreachable code #endif CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_rethrow_1(std::vector& line_numbers, std::vector& rethrow_line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; rethrow_line_numbers.insert(rethrow_line_numbers.begin(), __LINE__ + 2); line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_rethrow_2(line_numbers, rethrow_line_numbers) * rand(); } #ifdef _MSC_VER #pragma warning(pop) #endif void clean_trace(cpptrace::stacktrace& trace, std::vector::iterator it) { // because stacktrace_from_current_rethrow_2 has a try/catch which uses lambdas under msvc, we need to filter // all but the first frame mentioning stacktrace_from_current_rethrow_2. auto frame_2_it = std::find_if( it, trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_rethrow_2") != std::string::npos; } ); ASSERT_NE(frame_2_it, trace.frames.end()); frame_2_it++; auto remove_it = std::remove_if( frame_2_it, trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_rethrow_2") != std::string::npos; } ); if(remove_it != trace.frames.end()) { trace.frames.erase(remove_it); } } TEST(Rethrow, RethrowPreservesTrace) { std::vector line_numbers; std::vector rethrow_line_numbers; CPPTRACE_TRY { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); static volatile int tco_guard = stacktrace_from_current_rethrow_1(line_numbers, rethrow_line_numbers); (void)tco_guard; } CPPTRACE_CATCH(const std::runtime_error& e) { EXPECT_TRUE(cpptrace::current_exception_was_rethrown()); EXPECT_EQ(e.what(), std::string("foobar")); auto trace = cpptrace::from_current_exception(); ASSERT_GE(trace.frames.size(), 4); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_rethrow_3") != std::string::npos; } ); ASSERT_NE(it, trace.frames.end()) << trace; clean_trace(trace, it); size_t i = static_cast(it - trace.frames.begin()); int j = 0; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_rethrow_3")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); #ifndef _MSC_VER EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); #endif EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_rethrow_2")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_rethrow_1")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Rethrow_RethrowPreservesTrace_Test::TestBody")); } } TEST(Rethrow, RethrowTraceCorrect) { std::vector line_numbers; std::vector rethrow_line_numbers; CPPTRACE_TRY { rethrow_line_numbers.insert(rethrow_line_numbers.begin(), __LINE__ + 2); line_numbers.insert(line_numbers.begin(), __LINE__ + 1); static volatile int tco_guard = stacktrace_from_current_rethrow_1(line_numbers, rethrow_line_numbers); (void)tco_guard; } CPPTRACE_CATCH(const std::runtime_error& e) { EXPECT_TRUE(cpptrace::current_exception_was_rethrown()); EXPECT_EQ(e.what(), std::string("foobar")); auto rethrow_trace = cpptrace::from_current_exception_rethrow(); ASSERT_GE(rethrow_trace.frames.size(), 4); // reverse to get the last one matching instead of "`stacktrace_from_current_rethrow_2'::`1'::catch$4()" on msvc auto rit = std::find_if( rethrow_trace.frames.rbegin(), rethrow_trace.frames.rend(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_rethrow_2") != std::string::npos && frame.symbol.find("::catch") == std::string::npos; } ); ASSERT_NE(rit, rethrow_trace.frames.rend()) << rethrow_trace; size_t i = static_cast(&*rit - &*rethrow_trace.frames.begin()); auto it = rethrow_trace.frames.begin() + i; clean_trace(rethrow_trace, it); int j = 0; ASSERT_LT(i, rethrow_trace.frames.size()); ASSERT_LT(j, rethrow_line_numbers.size()); EXPECT_FILE(rethrow_trace.frames[i].filename, "rethrow.cpp"); #ifndef _MSC_VER EXPECT_LINE(rethrow_trace.frames[i].line.value(), rethrow_line_numbers[j]); #endif EXPECT_THAT(rethrow_trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_rethrow_2")); i++; j++; ASSERT_LT(i, rethrow_trace.frames.size()); ASSERT_LT(j, rethrow_line_numbers.size()); EXPECT_FILE(rethrow_trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(rethrow_trace.frames[i].line.value(), rethrow_line_numbers[j]); EXPECT_THAT(rethrow_trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_rethrow_1")); i++; j++; ASSERT_LT(i, rethrow_trace.frames.size()); ASSERT_LT(j, rethrow_line_numbers.size()); EXPECT_FILE(rethrow_trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(rethrow_trace.frames[i].line.value(), rethrow_line_numbers[j]); EXPECT_THAT(rethrow_trace.frames[i].symbol, testing::HasSubstr("Rethrow_RethrowTraceCorrect_Test::TestBody")); } } // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_basic_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; if(truthy) { // due to a MSVC warning about unreachable code line_numbers.insert(line_numbers.begin(), __LINE__ + 1); throw std::runtime_error("foobar"); } return 2; } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_basic_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_basic_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int stacktrace_from_current_basic_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_from_current_basic_2(line_numbers) * rand(); } TEST(Rethrow, RethrowDoesntInterfereWithSubsequentTraces) { std::vector line_numbers; std::vector rethrow_line_numbers; CPPTRACE_TRY { // do rethrow path so a rethrow happens try { static volatile int tco_guard = stacktrace_from_current_rethrow_1(line_numbers, rethrow_line_numbers); (void)tco_guard; } catch(...) {} // now do real test line_numbers.clear(); line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_from_current_basic_1(line_numbers); } CPPTRACE_CATCH(const std::runtime_error& e) { EXPECT_FALSE(cpptrace::current_exception_was_rethrown()); EXPECT_EQ(e.what(), std::string("foobar")); auto trace = cpptrace::from_current_exception(); ASSERT_GE(trace.frames.size(), 4); auto it = std::find_if( trace.frames.begin(), trace.frames.end(), [](const cpptrace::stacktrace_frame& frame) { return frame.symbol.find("stacktrace_from_current_basic_3") != std::string::npos; } ); ASSERT_NE(it, trace.frames.end()) << trace; size_t i = static_cast(it - trace.frames.begin()); int j = 0; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_basic_3")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_basic_2")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_from_current_basic_1")); i++; j++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(j, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "rethrow.cpp"); #ifndef _MSC_VER EXPECT_LINE(trace.frames[i].line.value(), line_numbers[j]); #endif EXPECT_THAT( trace.frames[i].symbol, testing::HasSubstr("Rethrow_RethrowDoesntInterfereWithSubsequentTraces_Test::TestBody") ); } } cpptrace-1.0.4/test/unit/tracing/stacktrace.cpp000066400000000000000000000174631504061443700215510ustar00rootroot00000000000000#include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #else #include #endif #ifdef _MSC_VER #define CPPTRACE_FORCE_INLINE [[msvc::flatten]] #else #define CPPTRACE_FORCE_INLINE [[gnu::always_inline]] static #endif TEST(Stacktrace, Empty) { cpptrace::stacktrace empty; EXPECT_TRUE(empty.empty()); EXPECT_EQ(empty.to_string(), "Stack trace (most recent call first):\n"); } CPPTRACE_FORCE_NO_INLINE void stacktrace_basic() { static volatile int lto_guard; lto_guard = lto_guard + 1; auto line = __LINE__ + 1; auto trace = cpptrace::generate_trace(); ASSERT_GE(trace.frames.size(), 1); EXPECT_FILE(trace.frames[0].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[0].line.value(), line); EXPECT_THAT(trace.frames[0].symbol, testing::HasSubstr("stacktrace_basic")); } TEST(Stacktrace, Basic) { stacktrace_basic(); } // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int stacktrace_multi_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto trace = cpptrace::generate_trace(); if(trace.frames.size() < 4) { ADD_FAILURE() << "trace.frames.size() >= 4"; return 2; } int i = 0; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_multi_3")); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_multi_2")); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_multi_1")); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_MultipleFrames_Test::TestBody")); return 2; } CPPTRACE_FORCE_NO_INLINE int stacktrace_multi_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_multi_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int stacktrace_multi_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_multi_2(line_numbers) * rand(); } TEST(Stacktrace, MultipleFrames) { std::vector line_numbers; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_multi_1(line_numbers); } CPPTRACE_FORCE_NO_INLINE cpptrace::raw_trace stacktrace_raw_resolve_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return cpptrace::generate_raw_trace(); } CPPTRACE_FORCE_NO_INLINE cpptrace::raw_trace stacktrace_raw_resolve_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_raw_resolve_3(line_numbers); } CPPTRACE_FORCE_NO_INLINE cpptrace::raw_trace stacktrace_raw_resolve_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_raw_resolve_2(line_numbers); } TEST(Stacktrace, RawTraceResolution) { std::vector line_numbers; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto raw = stacktrace_raw_resolve_1(line_numbers); auto trace = raw.resolve(); ASSERT_GE(trace.frames.size(), 4); int i = 0; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_raw_resolve_3")); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_raw_resolve_2")); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_raw_resolve_1")); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_RawTraceResolution_Test::TestBody")); } #if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && !defined(CPPTRACE_BUILD_NO_SYMBOLS) CPPTRACE_FORCE_NO_INLINE int stacktrace_inline_resolution_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); auto trace = cpptrace::generate_trace(); if(trace.frames.size() < 4) { ADD_FAILURE() << "trace.frames.size() >= 4"; return 2; } int i = 0; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_inline_resolution_3")); EXPECT_FALSE(trace.frames[i].is_inline); EXPECT_NE(trace.frames[i].raw_address, 0); EXPECT_NE(trace.frames[i].object_address, 0); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_inline_resolution_2")); EXPECT_TRUE(trace.frames[i].is_inline); EXPECT_EQ(trace.frames[i].raw_address, 0); EXPECT_EQ(trace.frames[i].object_address, 0); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_inline_resolution_1")); EXPECT_FALSE(trace.frames[i].is_inline); EXPECT_NE(trace.frames[i].raw_address, 0); EXPECT_NE(trace.frames[i].object_address, 0); i++; EXPECT_FILE(trace.frames[i].filename, "stacktrace.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_InlineResolution_Test::TestBody")); EXPECT_FALSE(trace.frames[i].is_inline); EXPECT_NE(trace.frames[i].raw_address, 0); EXPECT_NE(trace.frames[i].object_address, 0); return 2; } CPPTRACE_FORCE_INLINE int stacktrace_inline_resolution_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_inline_resolution_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int stacktrace_inline_resolution_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_inline_resolution_2(line_numbers) * rand(); } TEST(Stacktrace, InlineResolution) { std::vector line_numbers; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_inline_resolution_1(line_numbers); } #endif cpptrace-1.0.4/test/unit/tracing/traced_exception.cpp000066400000000000000000000060571504061443700227420ustar00rootroot00000000000000#include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #else #include #endif static volatile int truthy = 2; // NOTE: returning something and then return stacktrace_multi_3(line_numbers) * rand(); is done to prevent TCO even // under LTO https://github.com/jeremy-rifkin/cpptrace/issues/179#issuecomment-2467302052 CPPTRACE_FORCE_NO_INLINE int stacktrace_traced_object_3(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; if(truthy) { // due to a MSVC warning about unreachable code line_numbers.insert(line_numbers.begin(), __LINE__ + 1); throw cpptrace::runtime_error("foobar"); } return 2; } CPPTRACE_FORCE_NO_INLINE int stacktrace_traced_object_2(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_traced_object_3(line_numbers) * rand(); } CPPTRACE_FORCE_NO_INLINE int stacktrace_traced_object_1(std::vector& line_numbers) { static volatile int lto_guard; lto_guard = lto_guard + 1; line_numbers.insert(line_numbers.begin(), __LINE__ + 1); return stacktrace_traced_object_2(line_numbers) * rand(); } TEST(TracedException, Basic) { std::vector line_numbers; try { line_numbers.insert(line_numbers.begin(), __LINE__ + 1); stacktrace_traced_object_1(line_numbers); } catch(cpptrace::exception& e) { EXPECT_EQ(e.message(), std::string("foobar")); const auto& trace = e.trace(); ASSERT_GE(trace.frames.size(), 4); size_t i = 0; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(i, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "traced_exception.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_traced_object_3")); i++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(i, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "traced_exception.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_traced_object_2")); i++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(i, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "traced_exception.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_traced_object_1")); i++; ASSERT_LT(i, trace.frames.size()); ASSERT_LT(i, line_numbers.size()); EXPECT_FILE(trace.frames[i].filename, "traced_exception.cpp"); EXPECT_LINE(trace.frames[i].line.value(), line_numbers[i]); EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("TracedException_Basic_Test::TestBody")); } } cpptrace-1.0.4/test/unit/tracing/try_catch.cpp000066400000000000000000000235001504061443700213720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "common.hpp" #ifdef TEST_MODULE import cpptrace; #include #else #include #include #include "cpptrace/basic.hpp" #endif namespace { template CPPTRACE_FORCE_NO_INLINE void do_throw(Args&&... args) { throw E(std::forward(args)...); } void check_trace(const cpptrace::stacktrace& trace, std::string file, int line) { (void)trace; (void)file; (void)line; #ifndef CPPTRACE_BUILD_NO_SYMBOLS for(const auto& frame : trace) { if(frame.filename.find(file) != std::string::npos && frame.line == line) { SUCCEED(); return; } } FAIL() << "Trace does not contain "<("foobar"); }, [&] (const std::runtime_error& e) { did_catch = true; EXPECT_EQ(e.what(), std::string("foobar")); check_trace(cpptrace::from_current_exception(), "try_catch.cpp", line); check_trace(cpptrace::from_current_exception(), test_name); } ); EXPECT_TRUE(did_catch); } TEST(TryCatch, NoException) { bool did_try = false; cpptrace::try_catch( [&] { did_try = true; }, [&] (const std::runtime_error&) { FAIL(); } ); EXPECT_TRUE(did_try); } TEST(TryCatch, Upcast) { std::string test_name = __func__; int line = 0; bool did_catch = false; cpptrace::try_catch( [&] { line = __LINE__ + 1; do_throw("foobar"); }, [&] (const std::exception& e) { did_catch = true; EXPECT_EQ(e.what(), std::string("foobar")); check_trace(cpptrace::from_current_exception(), "try_catch.cpp", line); check_trace(cpptrace::from_current_exception(), test_name); } ); EXPECT_TRUE(did_catch); } TEST(TryCatch, NoHandler) { int line = 0; bool did_catch = false; try { cpptrace::try_catch( [&] { line = __LINE__ + 1; do_throw(); }, [&] (const std::runtime_error&) { FAIL(); } ); FAIL(); } catch(...) { did_catch = true; } EXPECT_TRUE(did_catch); } TEST(TryCatch, NoMatchingHandler) { int line = 0; bool did_catch = false; try { cpptrace::try_catch( [&] { line = __LINE__ + 1; do_throw(); } ); FAIL(); } catch(...) { did_catch = true; } EXPECT_TRUE(did_catch); } TEST(TryCatch, CorrectHandler) { std::string test_name = __func__; int line = 0; bool did_catch = false; cpptrace::try_catch( [&] { line = __LINE__ + 1; do_throw("foobar"); }, [&] (int) { FAIL(); }, [&] (const std::logic_error&) { FAIL(); }, [&] (const std::runtime_error& e) { did_catch = true; EXPECT_EQ(e.what(), std::string("foobar")); check_trace(cpptrace::from_current_exception(), "try_catch.cpp", line); check_trace(cpptrace::from_current_exception(), test_name); }, [&] (const std::exception&) { FAIL(); } ); EXPECT_TRUE(did_catch); } TEST(TryCatch, BlanketHandler) { std::string test_name = __func__; int line = 0; bool did_catch = false; cpptrace::try_catch( [&] { line = __LINE__ + 1; do_throw(); }, [&] (int) { FAIL(); }, [&] (const std::logic_error&) { FAIL(); }, [&] (const std::runtime_error&) { FAIL(); }, [&] () { did_catch = true; check_trace(cpptrace::from_current_exception(), "try_catch.cpp", line); check_trace(cpptrace::from_current_exception(), test_name); } ); EXPECT_TRUE(did_catch); } TEST(TryCatch, CatchOrdering) { std::string test_name = __func__; int line = 0; bool did_catch = false; cpptrace::try_catch( [&] { line = __LINE__ + 1; do_throw("foobar"); }, [&] (int) { FAIL(); }, [&] (const std::logic_error&) { FAIL(); }, [&] () { did_catch = true; check_trace(cpptrace::from_current_exception(), "try_catch.cpp", line); check_trace(cpptrace::from_current_exception(), test_name); }, [&] (const std::runtime_error&) { FAIL(); } ); EXPECT_TRUE(did_catch); } namespace { struct copy_move_tracker { static int copy; static int move; copy_move_tracker() = default; copy_move_tracker(const copy_move_tracker&) { copy++; } copy_move_tracker(copy_move_tracker&&) { move++; } copy_move_tracker& operator=(const copy_move_tracker&) { copy++; return *this; } copy_move_tracker& operator=(copy_move_tracker&&) { move++; return *this; } static void reset() { copy = 0; move = 0; } }; int copy_move_tracker::copy = 0; int copy_move_tracker::move = 0; } TEST(TryCatch, Value) { bool did_catch = false; copy_move_tracker::reset(); cpptrace::try_catch( [&] { do_throw(); }, [&] (copy_move_tracker) { did_catch = true; } ); EXPECT_TRUE(did_catch); EXPECT_EQ(copy_move_tracker::copy, 1); // from the catch EXPECT_EQ(copy_move_tracker::move, 1); // from the forward to the catcher } TEST(TryCatch, Ref) { bool did_catch = false; copy_move_tracker::reset(); cpptrace::try_catch( [&] { do_throw(); }, [&] (copy_move_tracker&) { did_catch = true; } ); EXPECT_TRUE(did_catch); EXPECT_EQ(copy_move_tracker::copy, 0); EXPECT_EQ(copy_move_tracker::move, 0); } TEST(TryCatch, ConstRef) { bool did_catch = false; copy_move_tracker::reset(); cpptrace::try_catch( [&] { do_throw(); }, [&] (const copy_move_tracker&) { did_catch = true; } ); EXPECT_TRUE(did_catch); EXPECT_EQ(copy_move_tracker::copy, 0); EXPECT_EQ(copy_move_tracker::move, 0); } namespace { struct copy_move_tracker_callable { static int copy; static int move; copy_move_tracker_callable() = default; copy_move_tracker_callable(const copy_move_tracker&) { copy++; } copy_move_tracker_callable(copy_move_tracker&&) { move++; } copy_move_tracker_callable& operator=(const copy_move_tracker&) { copy++; return *this; } copy_move_tracker_callable& operator=(copy_move_tracker&&) { move++; return *this; } static void reset() { copy = 0; move = 0; } void operator()() const {} }; int copy_move_tracker_callable::copy; int copy_move_tracker_callable::move; } TEST(TryCatch, LvalueCallable) { copy_move_tracker_callable::reset(); copy_move_tracker_callable c; cpptrace::try_catch( c, c ); EXPECT_EQ(copy_move_tracker_callable::copy, 0); EXPECT_EQ(copy_move_tracker_callable::move, 0); } TEST(TryCatch, RvalueCallable) { copy_move_tracker_callable::reset(); cpptrace::try_catch( copy_move_tracker_callable{}, copy_move_tracker_callable{} ); EXPECT_EQ(copy_move_tracker_callable::copy, 0); EXPECT_EQ(copy_move_tracker_callable::move, 0); } TEST(TryCatch, ClvalueCallable) { copy_move_tracker_callable::reset(); const copy_move_tracker_callable c; cpptrace::try_catch( c, c ); EXPECT_EQ(copy_move_tracker_callable::copy, 0); EXPECT_EQ(copy_move_tracker_callable::move, 0); } TEST(TryCatch, CrvalueCallable) { copy_move_tracker_callable::reset(); cpptrace::try_catch( static_cast(copy_move_tracker_callable{}), static_cast(copy_move_tracker_callable{}) ); EXPECT_EQ(copy_move_tracker_callable::copy, 0); EXPECT_EQ(copy_move_tracker_callable::move, 0); } cpptrace-1.0.4/tools/000077500000000000000000000000001504061443700144615ustar00rootroot00000000000000cpptrace-1.0.4/tools/CMakeLists.txt000066400000000000000000000026321504061443700172240ustar00rootroot00000000000000include(FetchContent) FetchContent_Declare( lyra GIT_SHALLOW TRUE GIT_REPOSITORY "https://github.com/bfgroup/Lyra.git" GIT_TAG "ee3c076fa6b9d64c9d249a21f5b9b5a8dae92cd8" ) FetchContent_MakeAvailable(lyra) FetchContent_Declare( fmt GIT_SHALLOW TRUE GIT_REPOSITORY "https://github.com/fmtlib/fmt.git" GIT_TAG "e69e5f977d458f2650bb346dadf2ad30c5320281" # v10.2.1 ) FetchContent_MakeAvailable(fmt) set( COMMON_LIBS cpptrace::cpptrace bfg::lyra fmt::fmt ) function(binary TARGET) cmake_parse_arguments(BINARY "" "" "SOURCES;LIBS;FLAGS;DEFS" ${ARGN}) add_executable(${TARGET} main.cpp) if(BINARY_SOURCES) add_library(${TARGET}_OBJ OBJECT ${BINARY_SOURCES}) target_link_libraries(${TARGET}_OBJ PUBLIC ${COMMON_LIBS}) endif() target_link_libraries(${TARGET} PUBLIC ${COMMON_LIBS}) target_link_libraries(${TARGET} PUBLIC ${BINARY_LIBS}) target_compile_definitions(${TARGET} PUBLIC ${BINARY_DEFS}) target_compile_options(${TARGET} PUBLIC ${BINARY_FLAGS} ${debug} ${warning_options} $<$:/Zc:__cplusplus>) target_include_directories(${TARGET} PUBLIC "${PROJECT_SOURCE_DIR}/src") target_compile_features(${TARGET} PRIVATE cxx_std_20) set_target_properties( ${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) endfunction() add_subdirectory(dwarfdump) add_subdirectory(symbol_tables) add_subdirectory(resolver) cpptrace-1.0.4/tools/dwarfdump/000077500000000000000000000000001504061443700164525ustar00rootroot00000000000000cpptrace-1.0.4/tools/dwarfdump/CMakeLists.txt000066400000000000000000000001251504061443700212100ustar00rootroot00000000000000if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) binary(dwarfdump LIBS ${dwarf_lib}) endif() cpptrace-1.0.4/tools/dwarfdump/main.cpp000066400000000000000000000131321504061443700201020ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "symbols/dwarf/dwarf.hpp" using namespace std::literals; using namespace cpptrace::detail::libdwarf; template<> struct fmt::formatter : ostream_formatter {}; class DwarfDumper { std::string object_path; Dwarf_Debug dbg = nullptr; // Error handling helper // For some reason R (*f)(Args..., void*)-style deduction isn't possible, seems like a bug in all compilers // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56190 // TODO: Duplicate template< typename... Args, typename... Args2, typename std::enable_if< std::is_same< decltype( (void)std::declval()(std::forward(std::declval())..., nullptr) ), void >::value, int >::type = 0 > int wrap(int (*f)(Args...), Args2&&... args) const { Dwarf_Error error = nullptr; int ret = f(std::forward(args)..., &error); if(ret == DW_DLV_ERROR) { handle_dwarf_error(dbg, error); } return ret; } // TODO: Duplicate // walk all CU's in a dbg, callback is called on each die and should return true to // continue traversal void walk_compilation_units(const std::function& fn) { // libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull Dwarf_Unsigned next_cu_header; Dwarf_Half header_cu_type; while(true) { int ret = wrap( dwarf_next_cu_header_d, dbg, true, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &next_cu_header, &header_cu_type ); if(ret == DW_DLV_NO_ENTRY) { fmt::println("End walk_dbg"); return; } if(ret != DW_DLV_OK) { PANIC("Unexpected return code from dwarf_next_cu_header_d"); return; } // 0 passed as the die to the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d // to fetch the cu die die_object cu_die(dbg, nullptr); cu_die = cu_die.get_sibling(); if(!cu_die) { break; } if(!walk_die_list(cu_die, fn)) { break; } } fmt::println("End walk_compilation_units"); } void dump_die_tree(const die_object& die, int depth) { walk_die_list( die, [this, depth] (const die_object& die) { fmt::println("{:016x}{: <{}} {}", die.get_global_offset(), "", depth * 2, die.get_tag_name()); fmt::println("{: <16}{: <{}} name: {}", "", "", depth * 2, die.get_name()); fmt::println(""); auto child = die.get_child(); if(child) { dump_die_tree(child, depth + 1); } return true; } ); } void dump_cu(const die_object& cu_die) { Dwarf_Half offset_size = 0; Dwarf_Half dwversion = 0; dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size); fmt::println("{:016x} Compile Unit: version = {}, unit type = {}", cu_die.get_global_offset(), dwversion, cu_die.get_tag_name()); dump_die_tree(cu_die, 0); } public: DwarfDumper() = default; ~DwarfDumper() { dwarf_finish(dbg); } void dump(std::filesystem::path path) { object_path = path; auto ret = wrap( dwarf_init_path_a, object_path.c_str(), nullptr, 0, DW_GROUPNUMBER_ANY, 0, nullptr, nullptr, &dbg ); if(ret == DW_DLV_OK) { // ok } else if(ret == DW_DLV_NO_ENTRY) { // fail, no debug info fmt::println(stderr, "No debug info"); std::exit(1); } else { fmt::println(stderr, "Error: Unknown return code from dwarf_init_path {}", ret); std::exit(1); } walk_compilation_units([this] (const die_object& cu_die) { dump_cu(cu_die); return true; }); } }; int main(int argc, char** argv) CPPTRACE_TRY { bool show_help = false; std::filesystem::path path; auto cli = lyra::cli() | lyra::help(show_help) | lyra::arg(path, "binary path")("binary to dwarfdump").required(); if(auto result = cli.parse({ argc, argv }); !result) { fmt::println(stderr, "Error in command line: {}", result.message()); fmt::println("{}", cli); return 1; } if(show_help) { fmt::println("{}", cli); return 0; } if(!std::filesystem::exists(path)) { fmt::println(stderr, "Error: Path doesn't exist {}", path); return 1; } if(!std::filesystem::is_regular_file(path)) { fmt::println(stderr, "Error: Path isn't a regular file {}", path); return 1; } DwarfDumper{}.dump(path); } CPPTRACE_CATCH(const std::exception& e) { fmt::println(stderr, "Caught exception {}: {}", cpptrace::demangle(typeid(e).name()), e.what()); cpptrace::from_current_exception().print(); } cpptrace-1.0.4/tools/resolver/000077500000000000000000000000001504061443700163225ustar00rootroot00000000000000cpptrace-1.0.4/tools/resolver/CMakeLists.txt000066400000000000000000000000211504061443700210530ustar00rootroot00000000000000binary(resolver) cpptrace-1.0.4/tools/resolver/main.cpp000066400000000000000000000101731504061443700177540ustar00rootroot00000000000000#include "cpptrace/basic.hpp" #include "cpptrace/formatting.hpp" #include "cpptrace/forward.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "symbols/symbols.hpp" #include "demangle/demangle.hpp" using namespace std::literals; using namespace cpptrace::detail; template<> struct fmt::formatter : ostream_formatter {}; auto formatter = cpptrace::formatter{}.addresses(cpptrace::formatter::address_mode::object); struct options { bool show_help = false; std::filesystem::path path; std::vector address_strings; bool from_stdin = false; bool keepalive = false; bool timing = false; bool disable_aranges = false; cpptrace::nullable line_table_cache_size; }; void resolve(const options& opts, cpptrace::frame_ptr address) { cpptrace::object_frame obj_frame{0, address, opts.path.string()}; auto start = std::chrono::high_resolution_clock::now(); std::vector trace = cpptrace::detail::resolve_frames({obj_frame}); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); if(trace.size() != 1) { throw std::runtime_error("Something went wrong, trace vector size didn't match"); } trace[0].symbol = cpptrace::demangle(trace[0].symbol); formatter.print(trace[0]); std::cout<> word) { resolve(opts, std::stoi(word, nullptr, 16)); } } if(opts.keepalive) { fmt::println("Done"); while(true) { std::this_thread::sleep_for(std::chrono::seconds(60)); } } return 0; } int main(int argc, char** argv) { int ret = 0; CPPTRACE_TRY { ret = resolver(argc, argv); } CPPTRACE_CATCH(const std::exception& e) { fmt::println(stderr, "Caught exception {}: {}", cpptrace::demangle(typeid(e).name()), e.what()); cpptrace::from_current_exception().print(); ret = 1; } return ret; } cpptrace-1.0.4/tools/symbol_tables/000077500000000000000000000000001504061443700173205ustar00rootroot00000000000000cpptrace-1.0.4/tools/symbol_tables/CMakeLists.txt000066400000000000000000000000261504061443700220560ustar00rootroot00000000000000binary(symbol_tables) cpptrace-1.0.4/tools/symbol_tables/main.cpp000066400000000000000000000105171504061443700207540ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "binary/elf.hpp" #include "binary/mach-o.hpp" #include "cpptrace/utils.hpp" using namespace std::literals; using namespace cpptrace::detail; template<> struct fmt::formatter : ostream_formatter {}; struct options { std::filesystem::path path; bool demangle = false; bool prune = false; }; #if IS_LINUX void dump_symtab_result( const Result>, internal_error>& res, const options& options ) { if(!res) { fmt::println(stderr, "Error loading: {}", res.unwrap_error().what()); } const auto& entries_ = res.unwrap_value(); if(!entries_) { fmt::println("Empty symbol table"); } const auto& entries = entries_.unwrap(); fmt::println("{:16} {:16} {:4} {}", "value", "size", "shdx", "symbol"); for(const auto& entry : entries) { std::string name; if(options.demangle) { name = cpptrace::demangle(entry.st_name); if(options.prune) { name = cpptrace::prune_symbol(name); } } else { name = entry.st_name; } fmt::println("{:016x} {:016x} {:04x} {}", entry.st_value, entry.st_size, entry.st_shndx, name); } } auto get_elf(const std::filesystem::path& path) { auto elf_ = elf::open(path.native()); if(!elf_) { fmt::println(stderr, "Error reading file: {}", elf_.unwrap_error().what()); } return std::move(elf_).unwrap_value(); } void dump_symbols(const options& options) { auto elf = get_elf(options.path); fmt::println("Symtab:"); dump_symtab_result(elf.get_symtab_entries(), options); fmt::println("Dynamic symtab:"); dump_symtab_result(elf.get_dynamic_symtab_entries(), options); } void lookup_symbol(const options& options, cpptrace::frame_ptr address) { auto elf = get_elf(options.path); if(auto symbol = elf.lookup_symbol(address)) { fmt::println("Symbol: {}", options.demangle ? cpptrace::demangle(symbol.unwrap()) : symbol.unwrap()); } else { fmt::println("Could not find symbol"); } } #elif IS_APPLE void dump_symbols(const options&) { fmt::println("Not implemented yet (TODO)"); } void lookup_symbol(const options&, cpptrace::frame_ptr) { fmt::println("Not implemented yet (TODO)"); } #else void dump_symbols(const std::filesystem::path&) { fmt::println("Not implemented yet (TODO)"); } void lookup_symbol(const std::filesystem::path&, cpptrace::frame_ptr) { fmt::println("Not implemented yet (TODO)"); } #endif int symbol_tables(int argc, char** argv) { bool show_help = false; std::optional lookup; options options; auto cli = lyra::cli() | lyra::help(show_help) | lyra::opt(lookup, "address")["--lookup"]("address in hex to lookup") | lyra::opt(options.demangle)["--demangle"]("demangle the symbol") | lyra::opt(options.prune)["--prune"]("prune the symbol") | lyra::arg(options.path, "binary path")("binary to dump symbol tables for").required(); if(auto result = cli.parse({ argc, argv }); !result) { fmt::println(stderr, "Error in command line: {}", result.message()); fmt::println("{}", cli); return 1; } if(show_help) { fmt::println("{}", cli); return 0; } if(!std::filesystem::exists(options.path)) { fmt::println(stderr, "Error: Path doesn't exist {}", options.path); return 1; } if(!std::filesystem::is_regular_file(options.path)) { fmt::println(stderr, "Error: Path isn't a regular file {}", options.path); return 1; } if(lookup) { auto address = std::stoull(*lookup, nullptr, 16); fmt::println(stderr, "Looking up address {:016x}", address); lookup_symbol(options, address); return 0; } dump_symbols(options); return 0; } int main(int argc, char** argv) { int ret = 0; CPPTRACE_TRY { ret = symbol_tables(argc, argv); } CPPTRACE_CATCH(const std::exception& e) { fmt::println(stderr, "Caught exception {}: {}", cpptrace::demangle(typeid(e).name()), e.what()); cpptrace::from_current_exception().print(); ret = 1; } return ret; }