- name: build
run: |
- sudo apt-get update && sudo apt-get install ninja-build libboost-dev libboost-context-dev pybind11-dev
- sudo apt-get install libunwind-dev libdw-dev libelf-dev libevent-dev
+ sudo apt-get update && sudo apt-get install ninja-build libboost-dev libboost-context-dev pybind11-dev libevent-dev
mkdir build ; cd build
cmake -GNinja -Denable_debug=ON -Denable_documentation=OFF -Denable_coverage=OFF \
-Denable_compile_optimizations=ON -Denable_compile_warnings=ON \
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Check for the compiler #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-##
-## Check the C/C++ standard that we need
-## See also tools/cmake/Flags.cmake that sets our paranoid warning flags
-INCLUDE(CheckCCompilerFlag)
-CHECK_C_COMPILER_FLAG(-fstack-cleaner HAVE_C_STACK_CLEANER)
## Request full debugging flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3")
message(FATAL_ERROR "Cannot build without sysconf.")
endif()
CHECK_FUNCTION_EXISTS(process_vm_readv HAVE_PROCESS_VM_READV)
-CHECK_FUNCTION_EXISTS(mmap HAVE_MMAP)
CHECK_FUNCTION_EXISTS(mremap HAVE_MREMAP)
CHECK_SYMBOL_EXISTS(vasprintf stdio.h HAVE_VASPRINTF)
endif()
SET(SIMGRID_HAVE_MC OFF)
-SET(SIMGRID_HAVE_STATEFUL_MC OFF)
-SET(HAVE_MMALLOC 0)
-
-find_package(Libevent)
-if(Libevent_FOUND)
- message(STATUS "Found libevent. The stateless model-checking can be enabled.")
- include_directories(${LIBEVENT_INCLUDE_DIR})
- set(SIMGRID_DEP "${SIMGRID_DEP} ${LIBEVENT_LIBRARIES}")
- SET(SIMGRID_HAVE_MC ON)
-else()
- message(STATUS "libevent not found. Please install libevent-dev to enable the SimGrid model checker.")
-endif()
-mark_as_advanced(LIBEVENT_LIBRARY)
-mark_as_advanced(LIBEVENT_THREADS_LIBRARY)
if(enable_model-checking)
- include(FindLibunwind)
- find_package(Libdw)
- find_package(Libelf)
- if(HAVE_MMAP AND HAVE_LIBUNWIND AND Libdw_FOUND AND Libelf_FOUND AND Libevent_FOUND)
- message(STATUS "All dependencies found. The stateful model-checking can be enabled.")
- SET(SIMGRID_HAVE_STATEFUL_MC ON)
- SET(HAVE_MMALLOC 1)
- SET(SIMGRID_DEP "${SIMGRID_DEP} ${LIBUNWIND_LIBRARIES} ${LIBELF_LIBRARIES} ${LIBDW_LIBRARIES}")
- include_directories(${LIBDW_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR})
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gdwarf-4")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4")
+ find_package(Libevent)
+ if(Libevent_FOUND)
+ message(STATUS "Found libevent. The stateless model-checking can be enabled.")
+ include_directories(${LIBEVENT_INCLUDE_DIR})
+ set(SIMGRID_DEP "${SIMGRID_DEP} ${LIBEVENT_LIBRARIES}")
+ SET(SIMGRID_HAVE_MC ON)
else()
- message(STATUS "Please install libunwind-dev libdw-dev libelf-dev libevent-dev to enable the stateful model checker.")
- set(HAVE_MMALLOC 0)
+ message(STATUS "libevent not found. Please install libevent-dev to enable the SimGrid model checker.")
endif()
-endif()
-mark_as_advanced(PATH_LIBDW_H)
-mark_as_advanced(PATH_LIBDW_LIB)
-
-if (SIMGRID_HAVE_STATEFUL_MC AND enable_ns3)
- message(WARNING "Activating both model-checking and ns-3 bindings is considered experimental.")
+ mark_as_advanced(LIBEVENT_LIBRARY)
+ mark_as_advanced(LIBEVENT_THREADS_LIBRARY)
endif()
if(enable_smpi)
message(" Compile Boost.Context support: ${HAVE_BOOST_CONTEXTS}")
message("")
message(" Model checking ..............: ${SIMGRID_HAVE_MC}")
-message(" Stateful model checking ...: ${SIMGRID_HAVE_STATEFUL_MC}")
message(" MBI testsuite .............: ${enable_testsuite_smpi_MBI}")
message(" McMini testsuite ..........: ${enable_testsuite_McMini}")
message("")
include examples/cpp/io-priority/s4u-io-priority.tesh
include examples/cpp/maestro-set/s4u-maestro-set.cpp
include examples/cpp/maestro-set/s4u-maestro-set.tesh
-include examples/cpp/mc-bugged1-liveness/promela_bugged1_liveness
-include examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness-stack-cleaner
-include examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness-visited.tesh
-include examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness.cpp
-include examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness.tesh
include examples/cpp/mc-bugged1/s4u-mc-bugged1.cpp
include examples/cpp/mc-bugged1/s4u-mc-bugged1.tesh
-include examples/cpp/mc-bugged2-liveness/promela_bugged2_liveness
-include examples/cpp/mc-bugged2-liveness/s4u-mc-bugged2-liveness.cpp
-include examples/cpp/mc-bugged2-liveness/s4u-mc-bugged2-liveness.tesh
include examples/cpp/mc-bugged2/s4u-mc-bugged2.cpp
include examples/cpp/mc-bugged2/s4u-mc-bugged2.tesh
include examples/cpp/mc-centralized-mutex/s4u-mc-centralized-mutex.cpp
include examples/cpp/mc-electric-fence/s4u-mc-electric-fence.cpp
include examples/cpp/mc-electric-fence/s4u-mc-electric-fence.tesh
include examples/cpp/mc-failing-assert/s4u-mc-failing-assert-nodpor.tesh
-include examples/cpp/mc-failing-assert/s4u-mc-failing-assert-statequality.tesh
include examples/cpp/mc-failing-assert/s4u-mc-failing-assert.cpp
include examples/cpp/mc-failing-assert/s4u-mc-failing-assert.tesh
include examples/cpp/mess-wait/s4u-mess-wait.cpp
include examples/cpp/synchro-condition-variable-waituntil/s4u-synchro-condition-variable-waituntil.tesh
include examples/cpp/synchro-condition-variable/s4u-synchro-condition-variable.cpp
include examples/cpp/synchro-condition-variable/s4u-synchro-condition-variable.tesh
-include examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh
include examples/cpp/synchro-mutex/s4u-mc-synchro-mutex.tesh
include examples/cpp/synchro-mutex/s4u-synchro-mutex.cpp
include examples/cpp/synchro-mutex/s4u-synchro-mutex.tesh
include examples/smpi/gemm/gemm.tesh
include examples/smpi/hostfile
include examples/smpi/mc/bugged1.c
-include examples/smpi/mc/bugged1_liveness.c
include examples/smpi/mc/bugged2.c
include examples/smpi/mc/hostfile_bugged1
-include examples/smpi/mc/hostfile_bugged1_liveness
include examples/smpi/mc/hostfile_bugged2
include examples/smpi/mc/hostfile_mutual_exclusion
-include examples/smpi/mc/hostfile_non_termination
include examples/smpi/mc/hostfile_only_send_deterministic
include examples/smpi/mc/mutual_exclusion.c
-include examples/smpi/mc/non_termination1.c
-include examples/smpi/mc/non_termination2.c
-include examples/smpi/mc/non_termination3.c
-include examples/smpi/mc/non_termination4.c
include examples/smpi/mc/only_send_deterministic.c
include examples/smpi/mc/only_send_deterministic.tesh
-include examples/smpi/mc/promela_bugged1_liveness
include examples/smpi/mc/sendsend.c
include examples/smpi/mc/sendsend.tesh
include examples/smpi/replay/actions0.txt
include teshsuite/kernel/context-defaults/factory_ucontext.tesh
include teshsuite/kernel/stack-overflow/stack-overflow.cpp
include teshsuite/kernel/stack-overflow/stack-overflow.tesh
-include teshsuite/mc/dwarf-expression/dwarf-expression.cpp
-include teshsuite/mc/dwarf-expression/dwarf-expression.tesh
-include teshsuite/mc/dwarf/dwarf.cpp
-include teshsuite/mc/dwarf/dwarf.tesh
include teshsuite/mc/mcmini/barber_shop_deadlock.c
include teshsuite/mc/mcmini/barber_shop_deadlock.tesh
include teshsuite/mc/mcmini/barber_shop_ok.c
include teshsuite/xbt/log_usage/log_usage.c
include teshsuite/xbt/log_usage/log_usage.tesh
include teshsuite/xbt/log_usage/log_usage_ndebug.tesh
-include teshsuite/xbt/mmalloc/mmalloc_32.tesh
-include teshsuite/xbt/mmalloc/mmalloc_64.tesh
-include teshsuite/xbt/mmalloc/mmalloc_test.cpp
include teshsuite/xbt/parallel_log_crashtest/parallel_log_crashtest.cpp
include teshsuite/xbt/parallel_log_crashtest/parallel_log_crashtest.tesh
include teshsuite/xbt/parmap_bench/parmap_bench.cpp
include teshsuite/xbt/signals/signals.tesh
include tools/address_sanitizer.supp
include tools/fix-paje-trace.sh
-include tools/generate-dwarf-functions
include tools/graphicator/graphicator.cpp
include tools/graphicator/graphicator.tesh
include tools/normalize-pointers.py
include src/kernel/xml/simgrid.dtd
include src/kernel/xml/simgrid_dtd.c
include src/kernel/xml/simgrid_dtd.h
-include src/mc/AddressSpace.hpp
-include src/mc/VisitedState.cpp
-include src/mc/VisitedState.hpp
include src/mc/api/ActorState.hpp
include src/mc/api/ClockVector.cpp
include src/mc/api/ClockVector.hpp
include src/mc/api/strategy/MinMatchComm.hpp
include src/mc/api/strategy/Strategy.hpp
include src/mc/api/strategy/UniformStrategy.hpp
-include src/mc/compare.cpp
include src/mc/datatypes.h
include src/mc/explo/CommunicationDeterminismChecker.cpp
include src/mc/explo/DFSExplorer.cpp
include src/mc/explo/DFSExplorer.hpp
include src/mc/explo/Exploration.cpp
include src/mc/explo/Exploration.hpp
-include src/mc/explo/LivenessChecker.cpp
-include src/mc/explo/LivenessChecker.hpp
include src/mc/explo/UdporChecker.cpp
include src/mc/explo/UdporChecker.hpp
include src/mc/explo/odpor/ClockVector_test.cpp
include src/mc/explo/udpor/maximal_subsets_iterator.hpp
include src/mc/explo/udpor/udpor_forward.hpp
include src/mc/explo/udpor/udpor_tests_private.hpp
-include src/mc/inspect/DwarfExpression.cpp
-include src/mc/inspect/DwarfExpression.hpp
-include src/mc/inspect/Frame.cpp
-include src/mc/inspect/Frame.hpp
-include src/mc/inspect/LocationList.cpp
-include src/mc/inspect/LocationList.hpp
-include src/mc/inspect/ObjectInformation.cpp
-include src/mc/inspect/ObjectInformation.hpp
-include src/mc/inspect/Type.hpp
-include src/mc/inspect/Variable.hpp
-include src/mc/inspect/mc_dwarf.cpp
-include src/mc/inspect/mc_dwarf.hpp
-include src/mc/inspect/mc_dwarf_attrnames.cpp
-include src/mc/inspect/mc_dwarf_tagnames.cpp
-include src/mc/inspect/mc_member.cpp
-include src/mc/inspect/mc_unw.cpp
-include src/mc/inspect/mc_unw.hpp
-include src/mc/inspect/mc_unw_vmread.cpp
include src/mc/mc.h
include src/mc/mc_base.cpp
include src/mc/mc_base.hpp
include src/mc/remote/CheckerSide.hpp
include src/mc/remote/RemotePtr.hpp
include src/mc/remote/mc_protocol.h
-include src/mc/sosp/ChunkedData.cpp
-include src/mc/sosp/ChunkedData.hpp
-include src/mc/sosp/PageStore.cpp
-include src/mc/sosp/PageStore.hpp
-include src/mc/sosp/PageStore_test.cpp
-include src/mc/sosp/Region.cpp
-include src/mc/sosp/Region.hpp
-include src/mc/sosp/RemoteProcessMemory.cpp
-include src/mc/sosp/RemoteProcessMemory.hpp
-include src/mc/sosp/Snapshot.cpp
-include src/mc/sosp/Snapshot.hpp
-include src/mc/sosp/Snapshot_test.cpp
include src/mc/transition/Transition.cpp
include src/mc/transition/Transition.hpp
include src/mc/transition/TransitionActor.cpp
include src/xbt/mallocator_private.h
include src/xbt/memory_map.cpp
include src/xbt/memory_map.hpp
-include src/xbt/mmalloc/mfree.c
-include src/xbt/mmalloc/mm.c
-include src/xbt/mmalloc/mm_interface.c
-include src/xbt/mmalloc/mm_legacy.c
-include src/xbt/mmalloc/mm_module.c
-include src/xbt/mmalloc/mmalloc.c
-include src/xbt/mmalloc/mmalloc.h
-include src/xbt/mmalloc/mmalloc.info
-include src/xbt/mmalloc/mmalloc.texi
-include src/xbt/mmalloc/mmorecore.c
-include src/xbt/mmalloc/mmprivate.h
-include src/xbt/mmalloc/mrealloc.c
-include src/xbt/mmalloc/swag.c
-include src/xbt/mmalloc/swag.h
include src/xbt/parmap.cpp
include src/xbt/parmap.hpp
include src/xbt/random.cpp
include tools/cmake/MaintainerMode.cmake
include tools/cmake/MakeLib.cmake
include tools/cmake/Modules/FindGraphviz.cmake
-include tools/cmake/Modules/FindLibdw.cmake
-include tools/cmake/Modules/FindLibelf.cmake
include tools/cmake/Modules/FindLibevent.cmake
-include tools/cmake/Modules/FindLibunwind.cmake
include tools/cmake/Modules/FindNS3.cmake
include tools/cmake/Modules/FindPAPI.cmake
include tools/cmake/Modules/FindValgrind.cmake
- **maxmin/concurrency-limit:** :ref:`cfg=maxmin/concurrency-limit`
- **model-check:** :ref:`options_modelchecking`
-- **model-check/checkpoint:** :ref:`cfg=model-check/checkpoint`
- **model-check/communications-determinism:** :ref:`cfg=model-check/communications-determinism`
- **model-check/dot-output:** :ref:`cfg=model-check/dot-output`
- **model-check/max-depth:** :ref:`cfg=model-check/max-depth`
-- **model-check/property:** :ref:`cfg=model-check/property`
- **model-check/reduction:** :ref:`cfg=model-check/reduction`
- **model-check/replay:** :ref:`cfg=model-check/replay`
- **model-check/send-determinism:** :ref:`cfg=model-check/send-determinism`
- **model-check/setenv:** :ref:`cfg=model-check/setenv`
-- **model-check/termination:** :ref:`cfg=model-check/termination`
- **model-check/timeout:** :ref:`cfg=model-check/timeout`
-- **model-check/visited:** :ref:`cfg=model-check/visited`
- **network/bandwidth-factor:** :ref:`cfg=network/bandwidth-factor`
- **network/crosstraffic:** :ref:`cfg=network/crosstraffic`
- **zero:** means that buffering should be disabled. All communications are actually blocking.
- **infty:** means that buffering should be made infinite. All communications are non-blocking.
-.. _cfg=model-check/property:
-
-Specifying a liveness property
-..............................
-
-**Option** ``model-check/property`` **Default:** unset
-
-If you want to specify liveness properties, you have to pass them on
-the command line, specifying the name of the file containing the
-property, as formatted by the `ltl2ba <https://github.com/utwente-fmt/ltl2ba>`_ program.
-Note that ltl2ba is not part of SimGrid and must be installed separately.
-
-.. code-block:: console
-
- $ simgrid-mc ./my_program --cfg=model-check/property:<filename>
-
-.. _cfg=model-check/checkpoint:
-
-Going for Stateful Verification
-...............................
-
-By default, the system is backtracked to its initial state to explore
-another path, instead of backtracking to the exact step before the fork
-that we want to explore (this is called stateless verification). This
-is done this way because saving intermediate states can rapidly
-exhaust the available memory. If you want, you can change the value of
-the ``model-check/checkpoint`` item. For example,
-``--cfg=model-check/checkpoint:1`` asks to take a checkpoint every
-step. Beware, this will certainly explode your memory. Larger values
-are probably better, make sure to experiment a bit to find the right
-setting for your specific system.
-
.. _cfg=model-check/reduction:
Specifying the kind of reduction
``--cfg=model-check/reduction:<technique>``. For now, this
configuration variable can take 2 values:
- - **none:** Do not apply any kind of reduction (mandatory for liveness properties, as our current DPOR algorithm breaks cycles)
+ - **none:** Do not apply any kind of reduction
- **dpor:** Apply Dynamic Partial Ordering Reduction. Only valid if you verify local safety properties (default value for
safety checks).
- **sdpor:** Source-set DPOR, as described in "Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction"
- **odpor:** Optimal DPOR, as described in "Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction"
by Abdulla et al.
-Another way to mitigate the state space explosion is to search for
-cycles in the exploration with the :ref:`cfg=model-check/visited`
-configuration. Note that DPOR and state-equality reduction may not
-play well together. You should choose between them.
-
Our current DPOR implementation could be improved in may ways. We are
currently improving its efficiency (both in term of reduction ability
-and computational speed), and future work could make it compatible
-with liveness properties.
+and computational speed).
.. _cfg=model-check/strategy:
- **uniform**: this is a boring random strategy where choices are based on a uniform sampling of possible choices.
Surprisingly, it gives really really good results.
-.. _cfg=model-check/visited:
-
-Size of Cycle Detection Set (state equality reduction)
-......................................................
-
-Mc SimGrid can be asked to search for cycles during the exploration,
-i.e. situations where a new explored state is in fact the same state
-than a previous one.. This can prove useful to mitigate the state
-space explosion with safety properties, and this is the crux when
-searching for counter-examples to the liveness properties.
-
-Note that this feature may break the current implementation of the
-DPOR reduction technique.
-
-The ``model-check/visited`` item is the maximum number of states, which
-are stored in memory. If the maximum number of snapshotted state is
-reached, some states will be removed from the memory and some cycles
-might be missed. Small values can lead to incorrect verifications, but
-large values can exhaust your memory and be CPU intensive as each new
-state must be compared to that amount of older saved states.
-
-The default settings depend on the kind of exploration. With safety
-checking, no state is snapshotted and cycles cannot be detected. With
-liveness checking, all states are snapshotted because missing a cycle
-could hinder the exploration soundness.
-
-.. _cfg=model-check/termination:
-
-Non-Termination Detection
-.........................
-
-The ``model-check/termination`` configuration item can be used to
-report if a non-termination execution path has been found. This is a
-path with a cycle, which means that the program might never terminate.
-
-This only works in safety mode, not in liveness mode.
-
-This options is disabled by default.
-
.. _cfg=model-check/dot-output:
Dot Output
If set, the ``model-check/dot-output`` configuration item is the name
of a file in which to write a dot file of the path leading to the
-property violation discovered (safety or liveness violation), as well
-as the cycle for liveness properties. This dot file can then be fed to the
+property violation discovered (safety violation). This dot file can then be fed to the
graphviz dot tool to generate a corresponding graphical representation.
.. _cfg=model-check/max-depth:
debugging tools.
When the model checker finds an interesting path in the application
-execution graph (where a safety or liveness property is violated), it
+execution graph (where a safety property is violated), it
generates an identifier for this path. Here is an example of the output:
.. code-block:: console
- On Debian / Ubuntu: ``apt install pybind11-dev python3-dev``
Model-checking mandatory dependencies
- On Debian / Ubuntu: ``apt install libevent-dev``
-Model-checking optional dependencies
- - On Debian / Ubuntu: ``apt install libunwind-dev libdw-dev libelf-dev``
Eigen3 (optional)
- On Debian / Ubuntu: ``apt install libeigen3-dev``
- On CentOS / Fedora: ``dnf install eigen3-devel``
Activates our internal memory caching mechanism. This produces faster
code, but it may fool the debuggers.
-enable_model-checking (on/OFF)
- Activates the liveness verification mode. This will hinder simulation speed even when the model checker is not activated at run
- time, because some optimizations such as LTO must be disabled at compile time. You need to have the :ref:`required
- build-dependencies <install_src_deps>` to activate this option.
+enable_model-checking (ON/off)
+ Activates the verification mode. This should not impact the performance of your simulations if you build it but don't use it,
+ but you can still disable it to save some compilation time.
enable_ns3 (on/OFF)
Activates the ns-3 bindings. See section :ref:`models_ns3`.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The best solution to get SimGrid working on windows is to install the
-Ubuntu subsystem of Windows 10. All of SimGrid (but the liveness model checker)
+Ubuntu subsystem of Windows 10. All of SimGrid
works in this setting. Native builds never really worked, and they are
disabled starting with SimGrid v3.33.
======================================
SimGrid can not only predict the performance of your application, but also assess its correctness through formal methods. Mc SimGrid is
-a full-featured model-checker that is embedded in the SimGrid framework. It can be used to formally verify safety and liveness
+a full-featured model-checker that is embedded in the SimGrid framework. It can be used to formally verify safety
properties on codes running on top of SimGrid, be it :ref:`simple algorithms <usecase_simalgo>` or :ref:`full MPI applications
<usecase_smpi>`.
multithreaded programs. It can however be used to detect misuses of the synchronization functions, such as the ones resulting in
deadlocks.
-Mc SimGrid can be used to verify classical `safety and liveness properties <https://en.wikipedia.org/wiki/Linear_time_property>`_, but
+Mc SimGrid can be used to verify classical `safety properties <https://en.wikipedia.org/wiki/Linear_time_property>`_, but
also `communication determinism <https://hal.inria.fr/hal-01953167/document>`_, a property that allows more efficient solutions toward
fault-tolerance. It can alleviate the state space explosion problem through `Dynamic Partial Ordering Reduction (DPOR)
<https://en.wikipedia.org/wiki/Partial_order_reduction>`_ and `state equality <https://hal.inria.fr/hal-01900120/document>`_. Note that
-------------
This tutorial is not complete yet, as there is nothing on reduction
-techniques nor on liveness properties. For now, the best source of
+techniques. For now, the best source of
information on these topics is `this old tutorial
<https://simgrid.org/tutorials/simgrid-mc-101.pdf>`_ and `that old
presentation
-<http://people.irisa.fr/Martin.Quinson/blog/2018/0123/McSimGrid-Boston.pdf>`_.
+<http://people.irisa.fr/Martin.Quinson/blog/2018/0123/McSimGrid-Boston.pdf>`_. But be warned that these source of
+information are very old: the liveness verification was removed in v3.35, even if these docs still mention it.
.. |br| raw:: html
endforeach()
endif()
-set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh)
-if(SIMGRID_HAVE_STATEFUL_MC)
- if(HAVE_C_STACK_CLEANER)
- add_executable (s4u-mc-bugged1-liveness-cleaner-on EXCLUDE_FROM_ALL s4u-mc-bugged1-liveness/s4u-mc-bugged1-liveness.cpp)
- target_link_libraries(s4u-mc-bugged1-liveness-cleaner-on simgrid)
- set_target_properties(s4u-mc-bugged1-liveness-cleaner-on PROPERTIES COMPILE_FLAGS "-DGARBAGE_STACK -fstack-cleaner")
- add_dependencies(tests-mc s4u-mc-bugged1-liveness-cleaner-on)
-
- add_executable (s4u-mc-bugged1-liveness-cleaner-off EXCLUDE_FROM_ALL s4u-mc-bugged1-liveness/s4u-mc-bugged1-liveness.cpp)
- target_link_libraries(s4u-mc-bugged1-liveness-cleaner-off simgrid)
- set_target_properties(s4u-mc-bugged1-liveness-cleaner-off PROPERTIES COMPILE_FLAGS "-DGARBAGE_STACK -fno-stack-cleaner")
- add_dependencies(tests-mc s4u-mc-bugged1-liveness-cleaner-off)
- endif()
-
- ADD_TESH(s4u-mc-synchro-mutex-stateful
- --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/synchro-mutex
- --setenv libdir=${CMAKE_BINARY_DIR}/lib
- --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
- --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/synchro-mutex
- --cd ${CMAKE_CURRENT_SOURCE_DIR}/synchro-mutex
- ${CMAKE_HOME_DIRECTORY}/examples/cpp/synchro-mutex/s4u-mc-synchro-mutex-stateful.tesh)
-
- # Model-checking liveness
- if(HAVE_UCONTEXT_CONTEXTS AND SIMGRID_PROCESSOR_x86_64)
- # liveness model-checking works only on 64bits (for now ...)
- set(_mc-bugged1-liveness_factories "ucontext")
- add_dependencies(tests-mc s4u-mc-bugged1-liveness)
- set(_mc-bugged2-liveness_factories "ucontext")
-
- # This example never ends, disable it for now
- set(_mc-bugged2-liveness_disable 1)
-
- if ("${CMAKE_SYSTEM}" MATCHES "Linux")
- # timeout under FreeBSD (test never stops)
- ADD_TESH(s4u-mc-bugged1-liveness-visited-ucontext --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/mc-bugged1-liveness
- --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
- --cd ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged1-liveness
- ${CMAKE_HOME_DIRECTORY}/examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness-visited.tesh)
- endif()
- IF(HAVE_C_STACK_CLEANER)
- add_dependencies(tests-mc s4u-mc-bugged1-liveness-stack-cleaner)
- # This test checks if the stack cleaner is making a difference:
- ADD_TEST(s4u-mc-bugged1-liveness-stack-cleaner ${CMAKE_HOME_DIRECTORY}/examples/cpp/mc-bugged1-liveness/s4u-mc-bugged1-liveness-stack-cleaner
- ${CMAKE_HOME_DIRECTORY}/examples/cpp/mc-bugged1-liveness/
- ${CMAKE_CURRENT_BINARY_DIR}/mc-bugged1-liveness/)
- ENDIF()
- else()
- set(_mc-bugged1-liveness_disable 1)
- set(_mc-bugged2-liveness_disable 1)
- endif()
-
- if(enable_coverage)
- ADD_TEST(cover-mc-bugged1-liveness ${CMAKE_CURRENT_BINARY_DIR}/mc-bugged1-liveness/s4u-mc-bugged1-liveness ${CMAKE_HOME_DIRECTORY}/examples/platforms/small_platform.xml 1 1001)
- endif()
-
-else()
- foreach (example mc-bugged1-liveness mc-bugged2-liveness)
- set(_${example}_disable 1)
- endforeach()
-endif()
-
# Hijack some regular tests to run them on top of the MC
foreach (example synchro-barrier synchro-mutex synchro-semaphore)
set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/${example}/s4u-mc-${example}.tesh)
exec-async exec-basic exec-dvfs exec-remote exec-waitfor exec-dependent exec-unassigned
exec-ptask-multicore exec-ptask-multicore-latency exec-cpu-nonlinear exec-cpu-factors exec-failure exec-threads
maestro-set
- mc-bugged1 mc-bugged1-liveness mc-bugged2 mc-bugged2-liveness mc-centralized-mutex mc-electric-fence mc-failing-assert
+ mc-bugged1 mc-bugged2 mc-centralized-mutex mc-electric-fence mc-failing-assert
mess-wait
network-ns3 network-ns3-wifi network-wifi
io-async io-priority io-degradation io-file-system io-file-remote io-disk-raw io-dependent
# Test non-DPOR reductions on a given MC test
foreach(example mc-failing-assert)
-# State equality is not tested because it would take about 15 hours to run that test on my machine.
-# We should first optimize mmalloc_heap_differ() which takes ~4sec for each pair to compare (maybe {175 x 174/ 2} pairs here)
-# See the comment on mmalloc_heap_differ() in compare.cpp for more info on why it's hard to optimize.
-#
-# if(SIMGRID_HAVE_STATEFUL_MC)
-# ADD_TESH(s4u-${example}-statequality --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/${example}
-# --setenv libdir=${CMAKE_BINARY_DIR}/lib
-# --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms
-# --setenv srcdir=${CMAKE_CURRENT_SOURCE_DIR}/${example}
-# --cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
-# ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-statequality.tesh)
-# endif()
if(SIMGRID_HAVE_MC)
ADD_TESH(s4u-${example}-nodpor --setenv bindir=${CMAKE_CURRENT_BINARY_DIR}/${example}
--cd ${CMAKE_CURRENT_SOURCE_DIR}/${example}
${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-nodpor.tesh)
endif()
- set(tesh_files ${tesh_files} ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-statequality.tesh)
set(tesh_files ${tesh_files} ${CMAKE_HOME_DIRECTORY}/examples/cpp/${example}/s4u-${example}-nodpor.tesh)
endforeach()
# Add all extra files to the archive
####################################
-set(examples_src ${examples_src} ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged1-liveness/s4u-mc-bugged1-liveness.cpp PARENT_SCOPE)
-set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/comm-pingpong/debug-breakpoint.tesh
- ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged1-liveness/s4u-mc-bugged1-liveness.tesh
- ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged1-liveness/s4u-mc-bugged1-liveness-visited.tesh PARENT_SCOPE)
+set(examples_src ${examples_src} PARENT_SCOPE)
+set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/comm-pingpong/debug-breakpoint.tesh PARENT_SCOPE)
set(xml_files ${xml_files} ${CMAKE_CURRENT_SOURCE_DIR}/actor-create/s4u-actor-create_d.xml
${CMAKE_CURRENT_SOURCE_DIR}/actor-lifetime/s4u-actor-lifetime_d.xml
${CMAKE_CURRENT_SOURCE_DIR}/app-bittorrent/s4u-app-bittorrent_d.xml
${CMAKE_CURRENT_SOURCE_DIR}/network-ns3/onelink_d.xml
${CMAKE_CURRENT_SOURCE_DIR}/network-ns3/one_cluster_d.xml PARENT_SCOPE)
set(bin_files ${bin_files} ${CMAKE_CURRENT_SOURCE_DIR}/battery-degradation/plot_battery_degradation.py
- ${CMAKE_CURRENT_SOURCE_DIR}/dht-kademlia/generate.py
- ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged1-liveness/s4u-mc-bugged1-liveness-stack-cleaner
- ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged1-liveness/promela_bugged1_liveness
- ${CMAKE_CURRENT_SOURCE_DIR}/mc-bugged2-liveness/promela_bugged2_liveness PARENT_SCOPE)
+ ${CMAKE_CURRENT_SOURCE_DIR}/dht-kademlia/generate.py PARENT_SCOPE)
set(txt_files ${txt_files} ${CMAKE_CURRENT_SOURCE_DIR}/dag-from-dax/simple_dax_with_cycle.xml
${CMAKE_CURRENT_SOURCE_DIR}/dag-from-dax/smalldax.xml
${CMAKE_CURRENT_SOURCE_DIR}/dag-from-dax-simple/dag.xml
+++ /dev/null
-never { /* !G(r->Fcs) */
-T0_init : /* init */
- if
- :: (1) -> goto T0_init
- :: (!cs && r) -> goto accept_S2
- fi;
-accept_S2 : /* 1 */
- if
- :: (!cs) -> goto accept_S2
- fi;
-}
+++ /dev/null
-#!/usr/bin/env sh
-# Run the same test compiled with -fstack-cleaner / f-no-stack-cleaner
-# and compare the output.
-
-srcdir="$1"
-bindir="$2"
-
-cd "$srcdir"
-
-die() {
- echo "$@" >&2
- exit 1
-}
-
-assert() {
- if ! eval "$1"; then
- die "Assertion failed: $*"
- fi
-}
-
-# If we don't have timeout, fake it:
-if ! which timeout > /dev/null; then
- timeout() {
- shift
- "$@"
- }
-fi
-
-run() {
- state=$1
- shift
- timeout 30s ${bindir:=.}/bugged1_liveness_cleaner_$state \
- ${srcdir:=.}/../../platforms/platform.xml \
- ${srcdir:=.}/deploy_bugged1_liveness.xml \
- "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" \
- --cfg=contexts/factory:ucontext \
- --cfg=contexts/stack-size:256
- assert 'test $? = 134'
-}
-
-get_states() {
- echo "$1" | grep "Expanded pairs = " | sed "s/^.*Expanded pairs = //" | head -n1
-}
-
-RES_ON="$(run on 2>&1 1>/dev/null)"
-RES_OFF="$(run off 2>&1 1>/dev/null)"
-
-STATES_ON=$(get_states "$RES_ON")
-STATES_OFF=$(get_states "$RES_OFF")
-
-# Both runs finished:
-assert 'test -n "$STATES_ON"'
-assert 'test -n "$STATES_OFF"'
-
-# We expect 21 visited pairs with the stack cleaner:
-assert 'test "$STATES_ON" = 21'
-
-# We expect more states without the stack cleaner:
-assert 'test "$STATES_ON" -lt "$STATES_OFF"'
+++ /dev/null
-#!/usr/bin/env tesh
-
-! expect return 2
-! timeout 30
-! output display
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged1-liveness ${platfdir:=.}/small_platform.xml 1 --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=model-check/visited:100 --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged1_liveness
-> [ 0.000000] (0:maestro@) Check the liveness property promela_bugged1_liveness
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (3:client@Fafard) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS already used. Queue the request.
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. Grant to queued requests (queue size: 1)
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS already used. Queue the request.
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. Grant to queued requests (queue size: 1)
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS already used. Queue the request.
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. Grant to queued requests (queue size: 1)
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (1:coordinator@Tremblay) CS already used. Queue the request.
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. Grant to queued requests (queue size: 1)
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (3:client@Fafard) Propositions changed : r=1, cs=0
-> [ 0.000000] (1:coordinator@Tremblay) CS release. Grant to queued requests (queue size: 1)
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (2:client@Boivin) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (0:maestro@) Pair 58 already reached (equal to pair 46) !
-> [ 0.000000] (0:maestro@) *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-> [ 0.000000] (0:maestro@) | ACCEPTANCE CYCLE |
-> [ 0.000000] (0:maestro@) *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-> [ 0.000000] (0:maestro@) Counter-example that violates formula :
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iSend(src=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iRecv(dst=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iRecv(dst=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] iSend(src=(3)Fafard (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iSend(src=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iSend(src=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iRecv(dst=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iSend(src=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iRecv(dst=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (2)Boivin (client)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) Expanded pairs = 58
-> [ 0.000000] (0:maestro@) Visited pairs = 202
-> [ 0.000000] (0:maestro@) Executed transitions = 208
-> [ 0.000000] (0:maestro@) Counter-example depth : 51
+++ /dev/null
-/* Copyright (c) 2012-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/***************** Centralized Mutual Exclusion Algorithm *******************/
-/* This example implements a centralized mutual exclusion algorithm. */
-/* Bug : CS requests of client 1 not satisfied */
-/* LTL property checked : G(r->F(cs)); (r=request of CS, cs=CS ok) */
-/****************************************************************************/
-
-#ifdef GARBAGE_STACK
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
-
-#include <simgrid/modelchecker.h>
-#include <simgrid/s4u.hpp>
-#include <xbt/dynar.h>
-
-XBT_LOG_NEW_DEFAULT_CATEGORY(bugged1_liveness, "my log messages");
-namespace sg4 = simgrid::s4u;
-
-class Message {
-public:
- enum class Kind { GRANT, REQUEST, RELEASE };
- Kind kind = Kind::GRANT;
- sg4::Mailbox* return_mailbox = nullptr;
- explicit Message(Message::Kind kind, sg4::Mailbox* mbox) : kind(kind), return_mailbox(mbox) {}
-};
-
-int r = 0;
-int cs = 0;
-
-#ifdef GARBAGE_STACK
-/** Do not use a clean stack */
-static void garbage_stack(void)
-{
- const size_t size = 256;
- int fd = open("/dev/urandom", O_RDONLY);
- char foo[size];
- read(fd, foo, size);
- close(fd);
-}
-#endif
-
-static void coordinator()
-{
- bool CS_used = false;
- std::queue<sg4::Mailbox*> requests;
-
- sg4::Mailbox* mbox = sg4::Mailbox::by_name("coordinator");
-
- while (true) {
- auto m = mbox->get_unique<Message>();
- if (m->kind == Message::Kind::REQUEST) {
- if (CS_used) {
- XBT_INFO("CS already used. Queue the request.");
- requests.push(m->return_mailbox);
- } else {
- if (m->return_mailbox->get_name() != "1") {
- XBT_INFO("CS idle. Grant immediately");
- m->return_mailbox->put(new Message(Message::Kind::GRANT, mbox), 1000);
- CS_used = true;
- }
- }
- } else {
- if (not requests.empty()) {
- XBT_INFO("CS release. Grant to queued requests (queue size: %zu)", requests.size());
- sg4::Mailbox* req = requests.front();
- requests.pop();
- if (req->get_name() != "1") {
- req->put(new Message(Message::Kind::GRANT, mbox), 1000);
- } else {
- requests.push(req);
- CS_used = false;
- }
- } else {
- XBT_INFO("CS release. resource now idle");
- CS_used = false;
- }
- }
- }
-}
-
-static void client(int id)
-{
- aid_t my_pid = sg4::this_actor::get_pid();
-
- sg4::Mailbox* my_mailbox = sg4::Mailbox::by_name(std::to_string(id));
-
- while (true) {
- XBT_INFO("Ask the request");
- sg4::Mailbox::by_name("coordinator")->put(new Message(Message::Kind::REQUEST, my_mailbox), 1000);
-
- if (id == 1) {
- r = 1;
- cs = 0;
- XBT_INFO("Propositions changed : r=1, cs=0");
- }
-
- auto grant = my_mailbox->get_unique<Message>();
- xbt_assert(grant->kind == Message::Kind::GRANT);
-
- if (id == 1) {
- cs = 1;
- r = 0;
- XBT_INFO("Propositions changed : r=0, cs=1");
- }
-
- XBT_INFO("%d got the answer. Sleep a bit and release it", id);
-
- sg4::this_actor::sleep_for(1);
-
- sg4::Mailbox::by_name("coordinator")->put(new Message(Message::Kind::RELEASE, my_mailbox), 1000);
-
- sg4::this_actor::sleep_for(static_cast<double>(my_pid));
-
- if (id == 1) {
- cs = 0;
- r = 0;
- XBT_INFO("Propositions changed : r=0, cs=0");
- }
- }
-}
-
-static void raw_client(int id)
-{
-#ifdef GARBAGE_STACK
- // At this point the stack of the callee (client) is probably filled with
- // zeros and uninitialized variables will contain 0. This call will place
- // random byes in the stack of the callee:
- garbage_stack();
-#endif
- client(id);
-}
-
-int main(int argc, char* argv[])
-{
- sg4::Engine e(&argc, argv);
-
- MC_automaton_new_propositional_symbol_pointer("r", &r);
- MC_automaton_new_propositional_symbol_pointer("cs", &cs);
-
- e.load_platform(argv[1]);
-
- sg4::Actor::create("coordinator", e.host_by_name("Tremblay"), coordinator)
- ->set_kill_time(argc > 3 ? std::stod(argv[3]) : -1.0);
- if (std::stod(argv[2]) == 0) {
- sg4::Actor::create("client", e.host_by_name("Boivin"), raw_client, 1);
- sg4::Actor::create("client", e.host_by_name("Fafard"), raw_client, 2);
- } else { // "Visited" case
- sg4::Actor::create("client", e.host_by_name("Boivin"), raw_client, 2);
- sg4::Actor::create("client", e.host_by_name("Fafard"), raw_client, 1);
- }
- e.run();
-
- return 0;
-}
+++ /dev/null
-#!/usr/bin/env tesh
-
-! expect return 2
-! timeout 20
-! output display
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged1-liveness ${platfdir:=.}/small_platform.xml 0 --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged1_liveness
-> [ 0.000000] (0:maestro@) Check the liveness property promela_bugged1_liveness
-> [ 0.000000] (2:client@Boivin) Ask the request
-> [ 0.000000] (3:client@Fafard) Ask the request
-> [ 0.000000] (2:client@Boivin) Propositions changed : r=1, cs=0
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (3:client@Fafard) 2 got the answer. Sleep a bit and release it
-> [ 0.000000] (1:coordinator@Tremblay) CS release. resource now idle
-> [ 0.000000] (3:client@Fafard) Ask the request
-> [ 0.000000] (1:coordinator@Tremblay) CS idle. Grant immediately
-> [ 0.000000] (0:maestro@) Pair 22 already reached (equal to pair 10) !
-> [ 0.000000] (0:maestro@) *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-> [ 0.000000] (0:maestro@) | ACCEPTANCE CYCLE |
-> [ 0.000000] (0:maestro@) *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-> [ 0.000000] (0:maestro@) Counter-example that violates formula :
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iSend(src=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] Wait(comm=(verbose only) [(2)Boivin (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(2)Boivin (client)] iRecv(dst=(2)Boivin (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] iSend(src=(3)Fafard (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iSend(src=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] iRecv(dst=(3)Fafard (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (3)Fafard (client)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] Wait(comm=(verbose only) [(1)Tremblay (coordinator)-> (3)Fafard (client)])
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] iSend(src=(3)Fafard (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] iRecv(dst=(1)Tremblay (coordinator), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) [(3)Fafard (client)] iSend(src=(3)Fafard (client), buff=(verbose only), size=(verbose only))
-> [ 0.000000] (0:maestro@) [(1)Tremblay (coordinator)] Wait(comm=(verbose only) [(3)Fafard (client)-> (1)Tremblay (coordinator)])
-> [ 0.000000] (0:maestro@) Expanded pairs = 22
-> [ 0.000000] (0:maestro@) Visited pairs = 20
-> [ 0.000000] (0:maestro@) Executed transitions = 20
-> [ 0.000000] (0:maestro@) Counter-example depth : 21
+++ /dev/null
-never { /* !(!(GFcs)) */
-T0_init : /* init */
- if
- :: (cs) -> goto accept_S1
- :: (1) -> goto T0_init
- fi;
-accept_S1 : /* 1 */
- if
- :: (cs) -> goto accept_S1
- :: (1) -> goto T0_init
- fi;
-}
+++ /dev/null
-/* Copyright (c) 2012-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/***************************** Bugged2 ****************************************/
-/* This example implements a centralized mutual exclusion algorithm. */
-/* One client stay always in critical section */
-/* LTL property checked : !(GFcs) */
-/******************************************************************************/
-
-#include <simgrid/modelchecker.h>
-#include <simgrid/s4u.hpp>
-
-XBT_LOG_NEW_DEFAULT_CATEGORY(bugged2_liveness, "my log messages");
-namespace sg4 = simgrid::s4u;
-
-class Message {
-public:
- enum class Kind { GRANT, NOT_GRANT, REQUEST };
- Kind kind = Kind::GRANT;
- sg4::Mailbox* return_mailbox = nullptr;
- explicit Message(Message::Kind kind, sg4::Mailbox* mbox) : kind(kind), return_mailbox(mbox) {}
-};
-
-int cs = 0;
-
-static void coordinator()
-{
- bool CS_used = false; // initially the CS is idle
- std::queue<sg4::Mailbox*> requests;
-
- sg4::Mailbox* mbox = sg4::Mailbox::by_name("coordinator");
-
- while (true) {
- auto m = mbox->get_unique<Message>();
- if (m->kind == Message::Kind::REQUEST) {
- if (CS_used) {
- XBT_INFO("CS already used.");
- m->return_mailbox->put(new Message(Message::Kind::NOT_GRANT, mbox), 1000);
- } else { // can serve it immediately
- XBT_INFO("CS idle. Grant immediately");
- m->return_mailbox->put(new Message(Message::Kind::GRANT, mbox), 1000);
- CS_used = true;
- }
- } else { // that's a release. Check if someone was waiting for the lock
- XBT_INFO("CS release. resource now idle");
- CS_used = false;
- }
- }
-}
-
-static void client(int id)
-{
- aid_t my_pid = sg4::this_actor::get_pid();
-
- sg4::Mailbox* my_mailbox = sg4::Mailbox::by_name(std::to_string(id));
-
- while (true) {
- XBT_INFO("Client (%d) asks the request", id);
- sg4::Mailbox::by_name("coordinator")->put(new Message(Message::Kind::REQUEST, my_mailbox), 1000);
-
- auto grant = my_mailbox->get_unique<Message>();
-
- if (grant->kind == Message::Kind::GRANT) {
- XBT_INFO("Client (%d) got the answer (grant). Sleep a bit and release it", id);
- if (id == 1)
- cs = 1;
- } else {
- XBT_INFO("Client (%d) got the answer (not grant). Try again", id);
- }
-
- sg4::this_actor::sleep_for(my_pid);
- }
-}
-
-int main(int argc, char* argv[])
-{
- sg4::Engine e(&argc, argv);
-
- MC_automaton_new_propositional_symbol_pointer("cs", &cs);
-
- e.load_platform(argv[1]);
-
- sg4::Actor::create("coordinator", e.host_by_name("Tremblay"), coordinator);
- sg4::Actor::create("client", e.host_by_name("Fafard"), client, 1);
- sg4::Actor::create("client", e.host_by_name("Boivin"), client, 2);
-
- e.run();
-
- return 0;
-}
+++ /dev/null
-#!/usr/bin/env tesh
-
-! expect return 2
-! timeout 20
-! output ignore
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/s4u-mc-bugged2-liveness ${platfdir:=.}/small_platform.xml --log=xbt_cfg.thresh:warning "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --cfg=contexts/factory:ucontext --cfg=contexts/stack-size:256 --cfg=model-check/property:promela_bugged2_liveness
+++ /dev/null
-#!/usr/bin/env tesh
-
-! expect return 1
-! timeout 300
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/visited:10000 -- ${bindir:=.}/s4u-mc-failing-assert ${platfdir}/small_platform.xml --log=root.thresh:critical
-> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/visited' to '20'
-> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_ModelChecker/INFO] **************************
-> [0.000000] [mc_ModelChecker/INFO] *** PROPERTY NOT VALID ***
-> [0.000000] [mc_ModelChecker/INFO] **************************
-> [0.000000] [mc_ModelChecker/INFO] Counter-example execution trace:
-> [0.000000] [mc_ModelChecker/INFO] 1: iRecv(mbox=0)
-> [0.000000] [mc_ModelChecker/INFO] 3: iSend(mbox=0)
-> [0.000000] [mc_ModelChecker/INFO] 1: WaitComm(from 3 to 1, mbox=0, no timeout)
-> [0.000000] [mc_ModelChecker/INFO] 1: iRecv(mbox=0)
-> [0.000000] [mc_ModelChecker/INFO] 2: iSend(mbox=0)
-> [0.000000] [mc_ModelChecker/INFO] 1: WaitComm(from 2 to 1, mbox=0, no timeout)
-> [0.000000] [mc_ModelChecker/INFO] Path = 1;3;1;1;2;1
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 18 unique states visited; 4 backtracks (22 transition replays, 0 states visited overall)
+++ /dev/null
-#!/usr/bin/env tesh
-
-p This file tests the cfg=model-check/checkpoint option for DFS explorer
-
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc --cfg=model-check/checkpoint:5 -- ${bindir:=.}/s4u-synchro-mutex --cfg=actors:2 --log=s4u_test.thres:critical
-> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/checkpoint' to '5'
-> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'actors' to '2'
-> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
-> [0.000000] [mc_dfs/INFO] DFS exploration ended. 66 unique states visited; 11 backtracks (22 transition replays, 99 states visited overall)
-
-p The stats without checkpoints is: 130 unique states visited; 27 backtracks (308 transition replays, 151 states visited overall)
-p But it runs much faster (0.6 sec vs. 1.6 sec), damn slow checkpointing code.
set(_ampi_test_sources ${CMAKE_CURRENT_SOURCE_DIR}/ampi_test/ampi_test.cpp)
# These tests are only used when MC is actived
-set(MC_tests bugged1 bugged2 bugged1_liveness only_send_deterministic mutual_exclusion non_termination1
- non_termination2 non_termination3 non_termination4 sendsend)
+set(MC_tests bugged1 bugged2 only_send_deterministic mutual_exclusion sendsend)
foreach(x ${MC_tests})
- if(NOT SIMGRID_HAVE_STATEFUL_MC)
+ if(NOT SIMGRID_HAVE_MC)
set(_${x}_disable 1)
endif()
set(_${x}_sources ${CMAKE_CURRENT_SOURCE_DIR}/mc/${x}.c)
${CMAKE_CURRENT_SOURCE_DIR}/replay/replay.tesh PARENT_SCOPE)
set(bin_files ${bin_files} ${CMAKE_CURRENT_SOURCE_DIR}/hostfile
${CMAKE_CURRENT_SOURCE_DIR}/energy/hostfile
- ${CMAKE_CURRENT_SOURCE_DIR}/mc/promela_bugged1_liveness
- ${CMAKE_CURRENT_SOURCE_DIR}/mc/hostfile_bugged1_liveness
${CMAKE_CURRENT_SOURCE_DIR}/mc/hostfile_bugged1
${CMAKE_CURRENT_SOURCE_DIR}/mc/hostfile_bugged2
${CMAKE_CURRENT_SOURCE_DIR}/mc/hostfile_only_send_deterministic
${CMAKE_CURRENT_SOURCE_DIR}/mc/hostfile_mutual_exclusion
- ${CMAKE_CURRENT_SOURCE_DIR}/mc/hostfile_non_termination
${CMAKE_CURRENT_SOURCE_DIR}/simple-execute/hostfile_griffon PARENT_SCOPE)
set(txt_files ${txt_files} ${CMAKE_CURRENT_SOURCE_DIR}/replay/actions0.txt
${CMAKE_CURRENT_SOURCE_DIR}/replay/actions1.txt
if(enable_smpi)
# MC is currently broken with threads (deadlock => timeout)
- if(SIMGRID_HAVE_STATEFUL_MC)
+ if(SIMGRID_HAVE_MC)
add_dependencies(tests-mc smpimain)
add_dependencies(tests-mc smpi_only_send_deterministic)
ADD_TESH(smpi-mc-only-send-determinism --setenv srcdir=${CMAKE_HOME_DIRECTORY}/examples/smpi/mc --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --cd ${CMAKE_BINARY_DIR}/examples/smpi/mc ${CMAKE_HOME_DIRECTORY}/examples/smpi/mc/only_send_deterministic.tesh)
+++ /dev/null
-/* Copyright (c) 2013-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/***************** Centralized Mutual Exclusion Algorithm *********************/
-/* This example implements a centralized mutual exclusion algorithm. */
-/* Bug : CS requests of process 1 not satisfied */
-/* LTL property checked : G(r->F(cs)); (r=request of CS, cs=CS ok) */
-/******************************************************************************/
-
-/* Run :
- /usr/bin/time -f "clock:%e user:%U sys:%S swapped:%W exitval:%x max:%Mk" "$@" \
- ../../../smpi_script/bin/smpirun -hostfile hostfile_bugged1_liveness -platform ../../platforms/cluster_backbone.xml \
- --cfg=contexts/factory:ucontext --cfg=model-check/reduction:none \
- --cfg=model-check/property:promela_bugged1_liveness --cfg=smpi/send-is-detached-thresh:0 \
- --cfg=contexts/stack-size:128 --cfg=model-check/visited:100000 --cfg=model-check/max-depth:100000 ./bugged1_liveness
-*/
-
-#include <stdio.h>
-#include <mpi.h>
-#include <simgrid/modelchecker.h>
-#include <xbt/dynar.h>
-
-#define GRANT_TAG 0
-#define REQUEST_TAG 1
-#define RELEASE_TAG 2
-
-int r;
-int cs;
-
-int main(int argc, char **argv){
- int size;
- int rank;
- int recv_buff;
- MPI_Status status;
- xbt_dynar_t requests = xbt_dynar_new(sizeof(int), NULL);
-
- /* Initialize MPI */
- int err = MPI_Init(&argc, &argv);
- if(err != MPI_SUCCESS){
- printf("MPI initialization failed !\n");
- exit(1);
- }
-
- MC_automaton_new_propositional_symbol_pointer("r", &r);
- MC_automaton_new_propositional_symbol_pointer("cs", &cs);
-
- MC_ignore(&status.count, sizeof status.count);
-
- /* Get number of processes */
- MPI_Comm_size(MPI_COMM_WORLD, &size);
- /* Get id of this process */
- MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-
- if(rank == 0){ /* Coordinator */
- int CS_used = 0;
- while(1){
- MPI_Recv(&recv_buff, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- if(status.MPI_TAG == REQUEST_TAG){
- if(CS_used){
- printf("CS already used.\n");
- xbt_dynar_push(requests, &recv_buff);
- }else{
- if(recv_buff != size - 1){
- printf("CS idle. Grant immediately.\n");
- MPI_Send(&rank, 1, MPI_INT, recv_buff, GRANT_TAG, MPI_COMM_WORLD);
- CS_used = 1;
- }
- }
- }else{
- if(!xbt_dynar_is_empty(requests)){
- printf("CS release. Grant to queued requests (queue size: %lu)", xbt_dynar_length(requests));
- xbt_dynar_shift(requests, &recv_buff);
- if(recv_buff != size - 1){
- MPI_Send(&rank, 1, MPI_INT, recv_buff, GRANT_TAG, MPI_COMM_WORLD);
- CS_used = 1;
- }else{
- xbt_dynar_push(requests, &recv_buff);
- CS_used = 0;
- }
- }else{
- printf("CS release. Resource now idle.\n");
- CS_used = 0;
- }
- }
- }
- }else{ /* Client */
- while(1){
- printf("%d asks the request.\n", rank);
- MPI_Send(&rank, 1, MPI_INT, 0, REQUEST_TAG, MPI_COMM_WORLD);
- if(rank == size - 1){
- r = 1;
- cs = 0;
- }
- MPI_Recv(&recv_buff, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- if(status.MPI_TAG == GRANT_TAG && rank == size - 1){
- cs = 1;
- r = 0;
- }
- printf("%d got the answer. Release it.\n", rank);
- MPI_Send(&rank, 1, MPI_INT, 0, RELEASE_TAG, MPI_COMM_WORLD);
- if(rank == size - 1){
- r = 0;
- cs = 0;
- }
- }
- }
-
- MPI_Finalize();
-
- return 0;
-}
+++ /dev/null
-node-1.simgrid.org
-node-2.simgrid.org
-node-3.simgrid.org
+++ /dev/null
-node-1.simgrid.org
-node-2.simgrid.org
\ No newline at end of file
exit(1);
}
- MC_ignore(&status.count, sizeof status.count);
-
/* Get number of processes */
MPI_Comm_size(MPI_COMM_WORLD, &size);
/* Get id of this process */
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <stdio.h>
-#include <mpi.h>
-#include <simgrid/modelchecker.h>
-
-int x = 5;
-int y = 8;
-
-int main(int argc, char **argv) {
- int recv_buff;
- int size;
- int rank;
- MPI_Status status;
-
- MPI_Init(&argc, &argv);
-
- MPI_Comm_size(MPI_COMM_WORLD, &size); /* Get nr of tasks */
- MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* Get id of this process */
-
- MC_ignore(&status.count, sizeof status.count);
-
- if (rank == 0) {
- while (1) {
- MPI_Recv(&recv_buff, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- }
- } else {
- while (1) {
- int old_x = x;
- x = -y;
- y = old_x;
- printf("x = %d, y = %d\n", x, y);
- MPI_Send(&rank, 1, MPI_INT, 0, 42, MPI_COMM_WORLD);
- }
- }
-
- MPI_Finalize();
-
- return 0;
-}
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <stdio.h>
-#include <mpi.h>
-#include <simgrid/modelchecker.h>
-
-int x;
-
-int main(int argc, char **argv) {
- int recv_buff;
- int size;
- int rank;
- MPI_Status status;
-
- MPI_Init(&argc, &argv);
-
- MPI_Comm_size(MPI_COMM_WORLD, &size); /* Get nr of tasks */
- MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* Get id of this process */
-
- MC_ignore(&status.count, sizeof status.count);
-
- if (rank == 0) {
- while (1) {
- MPI_Recv(&recv_buff, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- }
- } else {
- while (1) {
- x = 2;
- MPI_Send(&rank, 1, MPI_INT, 0, 42, MPI_COMM_WORLD);
- }
- }
-
- MPI_Finalize();
-
- return 0;
-}
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <stdio.h>
-#include <mpi.h>
-#include <simgrid/modelchecker.h>
-
-int x = 0;
-int y = 0;
-
-int main(int argc, char **argv) {
- int recv_x;
- int recv_y;
- int size;
- int rank;
- MPI_Status status;
-
- MPI_Init(&argc, &argv);
-
- MPI_Comm_size(MPI_COMM_WORLD, &size); /* Get nr of tasks */
- MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* Get id of this process */
-
- MC_ignore(&status.count, sizeof status.count);
-
- if (rank == 0) {
- while (x<5) {
- MPI_Recv(&recv_x, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- MPI_Recv(&recv_y, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- }
- } else {
- while (x<5) {
- int old_x = x;
- x = old_x - y;
- MPI_Send(&x, 1, MPI_INT, 0, 42, MPI_COMM_WORLD);
- y = old_x + y;
- MPI_Send(&y, 1, MPI_INT, 0, 42, MPI_COMM_WORLD);
- }
- }
-
- MPI_Finalize();
-
- return 0;
-}
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <stdio.h>
-#include <mpi.h>
-#include <simgrid/modelchecker.h>
-
-int x = 20;
-
-int main(int argc, char **argv) {
- int recv_x = 1;
- int size;
- int rank;
- MPI_Status status;
-
- MPI_Init(&argc, &argv);
-
- MPI_Comm_size(MPI_COMM_WORLD, &size); /* Get nr of tasks */
- MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* Get id of this process */
-
- MC_ignore(&status.count, sizeof status.count);
-
- if(rank==0){
- while (recv_x>=0) {
- MPI_Recv(&recv_x, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
- }
- }else{
- while (x >= 0) {
- if (MC_random(0,1) == 0) {
- x -= 1;
- } else {
- x += 1;
- }
- printf("x=%d\n", x);
- MPI_Send(&x, 1, MPI_INT, 0, 42, MPI_COMM_WORLD);
- }
- }
-
- MPI_Finalize();
-
- return 0;
-}
+++ /dev/null
-never { /* !G(r->Fcs) */
-T0_init : /* init */
- if
- :: (1) -> goto T0_init
- :: (!cs && r) -> goto accept_S2
- fi;
-accept_S2 : /* 1 */
- if
- :: (!cs) -> goto accept_S2
- fi;
-}
add_executable (pthread-${x} EXCLUDE_FROM_ALL pthread-${x}.c)
set_target_properties(pthread-${x} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(pthread-${x} PRIVATE Threads::Threads)
- if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
- target_link_libraries(pthread-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
- endif()
add_dependencies(tests pthread-${x})
ADD_TESH_FACTORIES(pthread-${x} "^thread" --setenv libdir=${CMAKE_BINARY_DIR}/lib --cd ${CMAKE_BINARY_DIR}/examples/sthread ${CMAKE_CURRENT_SOURCE_DIR}/pthread-${x}.tesh)
add_executable (pthread-${x} EXCLUDE_FROM_ALL pthread-${x}.c)
set_target_properties(pthread-${x} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(pthread-${x} PRIVATE Threads::Threads)
- if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
- target_link_libraries(pthread-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
- endif()
if(SIMGRID_HAVE_MC)
add_dependencies(tests-mc pthread-${x})
add_executable (${example} EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/${example}/${example}.cpp)
set_target_properties(${example} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${example} PRIVATE Threads::Threads)
- if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
- target_link_libraries(${example} PUBLIC "-fPIC -Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
- endif()
set_target_properties(${example} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${example})
if(SIMGRID_HAVE_MC)
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-recursive
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-recursive
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> Got the lock on the default mutex.
> Failed to relock the default mutex.
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simple
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simple
> All threads are started.
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> The thread 0 is terminating.
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simpledeadlock
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/pthread-mutex-simpledeadlock
> All threads are started.
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> The thread 0 is terminating.
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -C 1 -P 1
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -C 1 -P 1
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_dfs/INFO] DFS exploration ended. 786 unique states visited; 97 backtracks (2049 transition replays, 2932 states visited overall)
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/reduction:sdpor --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -C 1 -P 1
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/reduction:sdpor --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -C 1 -P 1
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/reduction' to 'sdpor'
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: sdpor.
> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1186 unique states visited; 157 backtracks (3403 transition replays, 4746 states visited overall)
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/reduction:odpor --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -C 1 -P 1
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/reduction:odpor --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q -C 1 -P 1
> [0.000000] [xbt_cfg/INFO] Configuration change: Set 'model-check/reduction' to 'odpor'
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: odpor.
> [0.000000] [mc_dfs/INFO] DFS exploration ended. 39 unique states visited; 0 backtracks (0 transition replays, 39 states visited overall)
#cmakedefine01 SIMGRID_HAVE_MALLOCATOR
/* Was the model-checking compiled in? */
#cmakedefine01 SIMGRID_HAVE_MC
-/* Was the stateful model-checking compiled in? */
-#cmakedefine01 SIMGRID_HAVE_STATEFUL_MC
/* Was the ns-3 support compiled in? */
#cmakedefine01 SIMGRID_HAVE_NS3
#cmakedefine NS3_MINOR_VERSION @NS3_MINOR_VERSION@
* It is off in simulation or when replaying MC traces (see MC_record_replay_is_active()) */
XBT_PUBLIC int MC_is_active();
-XBT_PUBLIC void MC_automaton_new_propositional_symbol_pointer(const char* id, int* value);
-
-XBT_PUBLIC void MC_ignore(void* addr, size_t size);
-XBT_PUBLIC void MC_unignore(void* addr, size_t size);
-XBT_PUBLIC void MC_ignore_heap(void* address, size_t size);
-XBT_PUBLIC void MC_unignore_heap(void* address, size_t size);
-
SG_END_DECL
#endif /* SIMGRID_MODELCHECKER_H */
#define MPI_Ineighbor_alltoallw(...) (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Ineighbor_alltoallw"), MPI_Ineighbor_alltoallw(__VA_ARGS__))
#define MPI_Status_f2c(...) (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Status_f2c"), MPI_Status_f2c(__VA_ARGS__))
#define MPI_Status_c2f(...) (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Status_c2f"), MPI_Status_c2f(__VA_ARGS__))
+#define MPI_Parrived(...) (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Parrived"), MPI_Parrived(__VA_ARGS__))
+#define MPI_Pready(...) (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Pready"), MPI_Pready(__VA_ARGS__))
+#define MPI_Pready_range(...) \
+ (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Pready_range"), MPI_Pready_range(__VA_ARGS__))
+#define MPI_Pready_list(...) \
+ (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Pready_list"), MPI_Pready_list(__VA_ARGS__))
+#define MPI_Precv_init(...) \
+ (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Precv_init"), MPI_Precv_init(__VA_ARGS__))
+#define MPI_Psend_init(...) \
+ (smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_Psend_init"), MPI_Psend_init(__VA_ARGS__))
#define MPI_STATUS_F2C smpi_trace_set_call_location(__FILE__,__LINE__,"MPI_STATUS_F2C"); call MPI_Status_f2c
#define mpi_status_c2f smpi_trace_set_call_location(__FILE__,__LINE__,"mpi_status_c2f"); call MPI_Status_c2f
#define MPI_STATUS_C2F smpi_trace_set_call_location(__FILE__,__LINE__,"MPI_STATUS_C2F"); call MPI_Status_c2f
+#define mpi_parrived \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "mpi_parrived"); \
+ call MPI_Parrived
+#define MPI_PARRIVED \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_PARRIVED"); \
+ call MPI_Parrived
+#define mpi_pready \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "mpi_pready"); \
+ call MPI_Pready
+#define MPI_PREADY \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_PREADY"); \
+ call MPI_Pready
+#define mpi_pready_range \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "mpi_pready_range"); \
+ call MPI_Pready_range
+#define MPI_PREADY_RANGE \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_PREADY_RANGE"); \
+ call MPI_Pready_range
+#define mpi_pready_list \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "mpi_pready_list"); \
+ call MPI_Pready_list
+#define MPI_PREADY_LIST \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_PREADY_LIST"); \
+ call MPI_Pready_list
+#define mpi_precv_init \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "mpi_precv_init"); \
+ call MPI_Precv_init
+#define MPI_PRECV_INIT \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_PRECV_INIT"); \
+ call MPI_Precv_init
+#define mpi_psend_init \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "mpi_psend_init"); \
+ call MPI_Psend_init
+#define MPI_PSEND_INIT \
+ smpi_trace_set_call_location(__FILE__, __LINE__, "MPI_PSEND_INIT"); \
+ call MPI_Psend_init
#define PTH_STACKGROWTH @PTH_STACKGROWTH@
/* MC variables */
-/* Did we compile mmalloc in? */
-#cmakedefine01 HAVE_MMALLOC
/* process_vm_readv: transfer data between process address spaces */
#cmakedefine01 HAVE_PROCESS_VM_READV
/* Other function checks */
/* Function dlfunc */
#cmakedefine01 HAVE_DLFUNC
-/* Function mmap */
-#cmakedefine01 HAVE_MMAP
/* Function mremap */
#cmakedefine01 HAVE_MREMAP
/* Function vasprintf */
unsigned CommImpl::next_id_ = 0;
-/* In stateful MC, we need to ignore some private memory that is not relevant to the application state */
-void CommImpl::setup_mc()
-{
- MC_ignore(&CommImpl::next_id_, sizeof(CommImpl::next_id_));
-}
-
-CommImpl::CommImpl()
-{
- MC_ignore((void*)&id_, sizeof(id_));
-}
-
std::function<void(CommImpl*, void*, size_t)> CommImpl::copy_data_callback_ = [](kernel::activity::CommImpl* comm,
void* buff, size_t buff_size) {
xbt_assert((buff_size == sizeof(void*)), "Cannot copy %zu bytes: must be sizeof(void*)", buff_size);
} else if (mbox_) {
mbox_->remove(this);
}
-
- MC_unignore((void*)&id_, sizeof(id_));
}
/** @brief Starts the simulation of a communication synchro. */
const unsigned id_ = ++next_id_; // ID of this comm (for MC) -- 0 as an ID denotes "invalid/unknown comm"
public:
- CommImpl();
+ CommImpl() = default;
static void set_copy_data_callback(const std::function<void(CommImpl*, void*, size_t)>& callback);
expectations of the other side, too. See */
std::function<void(CommImpl*, void*, size_t)> copy_data_fun;
- /* In stateful MC, we need to ignore some private memory that is not relevant to the application state */
- static void setup_mc();
-
/* Model actions */
timeout_action_type src_timeout_{nullptr, [](resource::Action* a) { a->unref(); }}; /* timeout set by the sender */
timeout_action_type dst_timeout_{nullptr, [](resource::Action* a) { a->unref(); }}; /* timeout set by the receiver */
s4u::Mailbox piface_;
std::string name_;
actor::ActorImplPtr permanent_receiver_; // actor to which the mailbox is attached
-#if SIMGRID_HAVE_STATEFUL_MC
- /* Using deque here is faster in benchmarks, but break the state equality heuristic of Liveness checking on Debian
- * testing. This would desserve a proper investiguation, but simply use a single-sided list for the time being. HACK.
- */
- std::list<CommImplPtr> comm_queue_;
- // messages already received in the permanent receive mode
- std::list<CommImplPtr> done_comm_queue_;
-#else
+
std::deque<CommImplPtr> comm_queue_;
// messages already received in the permanent receive mode
std::deque<CommImplPtr> done_comm_queue_;
-#endif
friend s4u::Engine;
friend s4u::Mailbox;
current_context_ = self;
}
-void Context::declare_context(std::size_t size)
-{
- /* Store the address of the stack in heap to compare it apart of heap comparison */
- MC_ignore_heap(this, size);
-}
-
Context* ContextFactory::attach(actor::ActorImpl*)
{
xbt_die("Cannot attach with this ContextFactory.\n"
template <class T, class... Args> T* new_context(Args&&... args)
{
auto* context = new T(std::forward<Args>(args)...);
- context->declare_context(sizeof(T));
return context;
}
};
std::function<void()> code_;
actor::ActorImpl* actor_ = nullptr;
bool is_maestro_;
- void declare_context(std::size_t size);
public:
static e_xbt_parmap_mode_t parallel_mode;
XBT_VERB("Creating a context of stack %uMb", actor->get_stacksize() / 1024 / 1024);
if (has_code()) {
this->stack_top_ = raw_makecontext(get_stack(), actor->get_stacksize(), smx_ctx_wrapper, this);
- } else {
- MC_ignore_heap(&stack_top_, sizeof stack_top_);
}
}
#endif
size_t size = actor->get_stacksize() + guard_size;
-#if SIMGRID_HAVE_STATEFUL_MC
- /* Cannot use posix_memalign when SIMGRID_HAVE_STATEFUL_MC. Align stack by hand, and save the
- * pointer returned by xbt_malloc0. */
- auto* alloc = static_cast<unsigned char*>(xbt_malloc0(size + xbt_pagesize));
- stack_ = alloc - (reinterpret_cast<uintptr_t>(alloc) & (xbt_pagesize - 1)) + xbt_pagesize;
- reinterpret_cast<unsigned char**>(stack_)[-1] = alloc;
-#else
void* alloc;
xbt_assert(posix_memalign(&alloc, xbt_pagesize, size) == 0, "Failed to allocate stack.");
this->stack_ = static_cast<unsigned char*>(alloc);
-#endif
/* This is fatal. We are going to fail at some point when we try reusing this. */
xbt_assert(
XBT_WARN("Failed to remove page protection: %s", strerror(errno));
/* try to pursue anyway */
}
-#if SIMGRID_HAVE_STATEFUL_MC
- /* Retrieve the saved pointer. See the initialization above. */
- stack_ = reinterpret_cast<unsigned char**>(stack_)[-1];
-#endif
}
xbt_free(stack_);
UContext* arg = this;
memcpy(ctx_addr, &arg, sizeof arg);
makecontext(&this->uc_, (void (*)())sysv_ctx_wrapper, 2, ctx_addr[0], ctx_addr[1]);
-
-#if SIMGRID_HAVE_STATEFUL_MC
- if (MC_is_active())
- simgrid::mc::AppSide::get()->declare_stack(get_stack(), stack_size, &uc_);
-#endif
}
}
+++ /dev/null
-/* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_ADDRESS_SPACE_H
-#define SIMGRID_MC_ADDRESS_SPACE_H
-
-#include "src/mc/mc_forward.hpp"
-#include "src/mc/remote/RemotePtr.hpp"
-
-namespace simgrid::mc {
-
-/** Options for read operations
- *
- * This is a set of flags managed with bitwise operators. Only the
- * meaningful operations are defined: addition, conversions to/from
- * integers are not allowed.
- */
-class ReadOptions {
- std::uint32_t value_ = 0;
- constexpr explicit ReadOptions(std::uint32_t value) : value_(value) {}
-
-public:
- constexpr ReadOptions() = default;
-
- explicit constexpr operator bool() const { return value_ != 0; }
- constexpr bool operator!() const { return value_ == 0; }
-
- constexpr ReadOptions operator|(ReadOptions const& that) const
- {
- return ReadOptions(value_ | that.value_);
- }
- constexpr ReadOptions operator&(ReadOptions const& that) const
- {
- return ReadOptions(value_ & that.value_);
- }
- constexpr ReadOptions operator^(ReadOptions const& that) const
- {
- return ReadOptions(value_ ^ that.value_);
- }
- constexpr ReadOptions operator~() const
- {
- return ReadOptions(~value_);
- }
-
- ReadOptions& operator|=(ReadOptions const& that)
- {
- value_ |= that.value_;
- return *this;
- }
- ReadOptions& operator&=(ReadOptions const& that)
- {
- value_ &= that.value_;
- return *this;
- }
- ReadOptions& operator^=(ReadOptions const& that)
- {
- value_ ^= that.value_;
- return *this;
- }
-
- /** Copy the data to the given buffer */
- static constexpr ReadOptions none() { return ReadOptions(0); }
-
- /** Allows to return a pointer to another buffer where the data is
- * available instead of copying the data into the buffer
- */
- static constexpr ReadOptions lazy() { return ReadOptions(1); }
-};
-
-/** A given state of a given process (abstract base class)
- *
- * Currently, this might either be:
- *
- * * the current state of an existing process;
- *
- * * a snapshot.
- */
-class AddressSpace {
-private:
- RemoteProcessMemory* remote_process_memory_;
-
-public:
- explicit AddressSpace(RemoteProcessMemory* process) : remote_process_memory_(process) {}
- virtual ~AddressSpace() = default;
-
- /** The process of this address space
- *
- * This is where we can get debug information, memory layout, etc.
- */
- simgrid::mc::RemoteProcessMemory* get_remote_process_memory() const { return remote_process_memory_; }
-
- /** Read data from the address space
- *
- * @param buffer target buffer for the data
- * @param size number of bytes to read
- * @param address remote source address of the data
- * @param options
- */
- virtual void* read_bytes(void* buffer, std::size_t size, RemotePtr<void> address,
- ReadOptions options = ReadOptions::none()) const = 0;
-
- /** Read a given data structure from the address space */
- template <class T> inline void read(T* buffer, RemotePtr<T> ptr) const { this->read_bytes(buffer, sizeof(T), ptr); }
-
- template <class T> inline void read(Remote<T>& buffer, RemotePtr<T> ptr) const
- {
- this->read_bytes(buffer.get_buffer(), sizeof(T), ptr);
- }
-
- /** Read a given data structure from the address space
- *
- * This version returns by value.
- */
- template <class T> inline Remote<T> read(RemotePtr<T> ptr) const
- {
- Remote<T> res;
- this->read_bytes(&res, sizeof(T), ptr);
- return res;
- }
-
- /** Read a string of known size */
- std::string read_string(RemotePtr<char> address, std::size_t len) const
- {
- std::string res;
- res.resize(len);
- this->read_bytes(&res[0], len, address);
- return res;
- }
-};
-
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2011-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/VisitedState.hpp"
-#include "src/mc/explo/Exploration.hpp"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_private.hpp"
-
-#include <unistd.h>
-#include <sys/wait.h>
-#include <memory>
-#include <boost/range/algorithm.hpp>
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_VisitedState, mc, "Logging specific to state equality detection mechanisms");
-
-namespace simgrid::mc {
-
-/** @brief Save the current state */
-VisitedState::VisitedState(unsigned long state_number, unsigned int actor_count, RemoteApp& remote_app)
- : heap_bytes_used_(remote_app.get_remote_process_memory()->get_remote_heap_bytes())
- , actor_count_(actor_count)
- , num_(state_number)
-{
- this->system_state_ = std::make_shared<simgrid::mc::Snapshot>(state_number, remote_app.get_page_store(),
- *remote_app.get_remote_process_memory());
-}
-
-void VisitedStates::prune()
-{
- while (states_.size() > (std::size_t)_sg_mc_max_visited_states) {
- XBT_DEBUG("Try to remove visited state (maximum number of stored states reached)");
- auto min_element = boost::range::min_element(
- states_, [](const std::unique_ptr<simgrid::mc::VisitedState>& a,
- const std::unique_ptr<simgrid::mc::VisitedState>& b) { return a->num_ < b->num_; });
- xbt_assert(min_element != states_.end());
- // and drop it:
- states_.erase(min_element);
- XBT_DEBUG("Remove visited state (maximum number of stored states reached)");
- }
-}
-
-/** @brief Checks whether a given state has already been visited by the algorithm. */
-std::unique_ptr<simgrid::mc::VisitedState>
-VisitedStates::addVisitedState(unsigned long state_number, simgrid::mc::State* graph_state, RemoteApp& remote_app)
-{
- auto new_state =
- std::make_unique<simgrid::mc::VisitedState>(state_number, graph_state->get_actor_count(), remote_app);
-
- graph_state->set_system_state(new_state->system_state_);
- XBT_DEBUG("Snapshot %p of visited state %ld (exploration stack state %ld)", new_state->system_state_.get(),
- new_state->num_, graph_state->get_num());
-
- auto [range_begin, range_end] = boost::range::equal_range(states_, new_state.get(), [](auto const& a, auto const& b) {
- return std::make_pair(a->actor_count_, a->heap_bytes_used_) < std::make_pair(b->actor_count_, b->heap_bytes_used_);
- });
-
- for (auto i = range_begin; i != range_end; ++i) {
- auto& visited_state = *i;
- if (visited_state->system_state_->equals_to(*new_state->system_state_.get(),
- *remote_app.get_remote_process_memory())) {
- // The state has been visited:
-
- std::unique_ptr<simgrid::mc::VisitedState> old_state = std::move(visited_state);
-
- if (old_state->original_num_ == -1) // I'm the copy of an original process
- new_state->original_num_ = old_state->num_;
- else // I'm the copy of a copy
- new_state->original_num_ = old_state->original_num_;
-
- XBT_DEBUG("State %ld already visited ! (equal to state %ld (state %ld in dot_output))", new_state->num_,
- old_state->num_, new_state->original_num_);
-
- /* Replace the old state with the new one (with a bigger num)
- (when the max number of visited states is reached, the oldest
- one is removed according to its number (= with the min number) */
- XBT_DEBUG("Replace visited state %ld with the new visited state %ld", old_state->num_, new_state->num_);
-
- visited_state = std::move(new_state);
- return old_state;
- }
- }
-
- XBT_DEBUG("Insert new visited state %ld (total : %lu)", new_state->num_, (unsigned long)states_.size());
- states_.insert(range_begin, std::move(new_state));
- this->prune();
- return nullptr;
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_VISITED_STATE_HPP
-#define SIMGRID_MC_VISITED_STATE_HPP
-
-#include "src/mc/api/State.hpp"
-#include "src/mc/sosp/Snapshot.hpp"
-
-#include <cstddef>
-#include <memory>
-
-namespace simgrid::mc {
-
-class XBT_PRIVATE VisitedState {
-public:
- std::shared_ptr<simgrid::mc::Snapshot> system_state_ = nullptr;
- std::size_t heap_bytes_used_ = 0;
- int actor_count_;
- long num_; // unique id of that state in the storage of all stored IDs
- long original_num_ = -1; // num field of the VisitedState to which I was declared equal to (used for dot_output)
-
- explicit VisitedState(unsigned long state_number, unsigned int actor_count, RemoteApp& remote_app);
-};
-
-class XBT_PRIVATE VisitedStates {
- std::vector<std::unique_ptr<simgrid::mc::VisitedState>> states_;
-public:
- void clear() { states_.clear(); }
- std::unique_ptr<simgrid::mc::VisitedState> addVisitedState(unsigned long state_number,
- simgrid::mc::State* graph_state, RemoteApp& remote_app);
-
-private:
- void prune();
-};
-
-} // namespace simgrid::mc
-
-#endif
static std::string master_socket_name;
-RemoteApp::RemoteApp(const std::vector<char*>& args, bool need_memory_introspection) : app_args_(args)
+RemoteApp::RemoteApp(const std::vector<char*>& args) : app_args_(args)
{
- if (need_memory_introspection) {
-#if SIMGRID_HAVE_STATEFUL_MC
- checker_side_ = std::make_unique<simgrid::mc::CheckerSide>(app_args_, need_memory_introspection);
- initial_snapshot_ = std::make_shared<simgrid::mc::Snapshot>(0, page_store_, *checker_side_->get_remote_memory());
-#else
- xbt_die("SimGrid MC was compiled without memory introspection support.");
-#endif
- } else {
- master_socket_ = socket(AF_UNIX,
+ master_socket_ = socket(AF_UNIX,
#ifdef __APPLE__
- SOCK_STREAM, /* Mac OSX does not have AF_UNIX + SOCK_SEQPACKET, even if that's faster */
+ SOCK_STREAM, /* Mac OSX does not have AF_UNIX + SOCK_SEQPACKET, even if that's faster */
#else
- SOCK_SEQPACKET,
+ SOCK_SEQPACKET,
#endif
- 0);
+ 0);
xbt_assert(master_socket_ != -1, "Cannot create the master socket: %s", strerror(errno));
master_socket_name = "/tmp/simgrid-mc-" + std::to_string(getpid());
xbt_assert(listen(master_socket_, SOMAXCONN) >= 0, "Cannot listen to the master socket: %s.", strerror(errno));
- application_factory_ = std::make_unique<simgrid::mc::CheckerSide>(app_args_, need_memory_introspection);
+ application_factory_ = std::make_unique<simgrid::mc::CheckerSide>(app_args_);
checker_side_ = application_factory_->clone(master_socket_, master_socket_name);
- }
}
void RemoteApp::restore_initial_state()
{
- if (initial_snapshot_ == nullptr) // No memory introspection
checker_side_ = application_factory_->clone(master_socket_, master_socket_name);
-#if SIMGRID_HAVE_STATEFUL_MC
- else
- initial_snapshot_->restore(*checker_side_->get_remote_memory());
-#endif
}
unsigned long RemoteApp::get_maxpid() const
m.times_considered_ = times_considered;
checker_side_->get_channel().send(m);
-#if SIMGRID_HAVE_STATEFUL_MC
- if (auto* memory = get_remote_process_memory(); memory != nullptr)
- memory->clear_cache();
-#endif
if (checker_side_->running())
checker_side_->dispatch_events(); // The app may send messages while processing the transition
#include "src/mc/api/ActorState.hpp"
#include "src/mc/remote/CheckerSide.hpp"
#include "src/mc/remote/RemotePtr.hpp"
-#include "src/mc/sosp/PageStore.hpp"
#include <functional>
*/
class XBT_PUBLIC RemoteApp {
private:
-#if SIMGRID_HAVE_STATEFUL_MC
- PageStore page_store_{500};
- std::shared_ptr<simgrid::mc::Snapshot> initial_snapshot_;
-#else
- void* initial_snapshot_ = nullptr; // The code tests it to decide whether to use the refork exec path
-#endif
std::unique_ptr<CheckerSide> checker_side_;
std::unique_ptr<CheckerSide> application_factory_; // when no meminfo, create checker_side_ by cloning this one
int master_socket_ = -1;
*
* The code is expected to `exec` the model-checked application.
*/
- explicit RemoteApp(const std::vector<char*>& args, bool need_memory_introspection);
+ explicit RemoteApp(const std::vector<char*>& args);
void restore_initial_state();
void wait_for_requests();
/** Take a transition. A new Transition is created iff the last parameter is true */
Transition* handle_simcall(aid_t aid, int times_considered, bool new_transition);
-
-#if SIMGRID_HAVE_STATEFUL_MC
- /* Get the memory of the remote process */
- RemoteProcessMemory* get_remote_process_memory() { return checker_side_->get_remote_memory(); }
-
- PageStore& get_page_store() { return page_store_; }
-#endif
};
} // namespace simgrid::mc
THROW_IMPOSSIBLE;
remote_app.get_actors_status(strategy_->actors_to_run_);
-
-#if SIMGRID_HAVE_STATEFUL_MC
- /* Stateful model checking */
- if ((_sg_mc_checkpoint > 0 && (num_ % _sg_mc_checkpoint == 0)) || _sg_mc_termination)
- system_state_ = std::make_shared<simgrid::mc::Snapshot>(num_, remote_app.get_page_store(),
- *remote_app.get_remote_process_memory());
-#endif
}
State::State(RemoteApp& remote_app, std::shared_ptr<State> parent_state)
remote_app.get_actors_status(strategy_->actors_to_run_);
-#if SIMGRID_HAVE_STATEFUL_MC /* Stateful model checking */
- if ((_sg_mc_checkpoint > 0 && (num_ % _sg_mc_checkpoint == 0)) || _sg_mc_termination)
- system_state_ = std::make_shared<simgrid::mc::Snapshot>(num_, remote_app.get_page_store(),
- *remote_app.get_remote_process_memory());
-#endif
-
/* Copy the sleep set and eventually removes things from it: */
/* For each actor in the previous sleep set, keep it if it is not dependent with current transition.
* And if we kept it and the actor is enabled in this state, mark the actor as already done, so that
#include "src/mc/explo/odpor/WakeupTree.hpp"
#include "src/mc/transition/Transition.hpp"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/sosp/Snapshot.hpp"
-#endif
-
namespace simgrid::mc {
/* A node in the exploration graph (kind-of) */
/** Sequential state ID (used for debugging) */
long num_ = 0;
- /** Snapshot of system state (if needed) */
- std::shared_ptr<Snapshot> system_state_;
-
/** Unique parent of this state. Required both for sleep set computation
and for guided model-checking */
std::shared_ptr<State> parent_state_ = nullptr;
unsigned long get_actor_count() const { return strategy_->actors_to_run_.size(); }
bool is_actor_enabled(aid_t actor) const { return strategy_->actors_to_run_.at(actor).is_enabled(); }
- Snapshot* get_system_state() const { return system_state_.get(); }
- void set_system_state(std::shared_ptr<Snapshot> state) { system_state_ = std::move(state); }
-
/**
* @brief Computes the backtrack set for this state
* according to its definition in SimGrid.
+++ /dev/null
-/* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/** \file compare.cpp Memory snapshotting and comparison */
-
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_private.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-#include "src/mc/sosp/Snapshot.hpp"
-#include "xbt/ex.h"
-
-#include <algorithm>
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_compare, mc, "Logging specific to mc_compare in mc");
-
-namespace simgrid::mc {
-
-/*********************************** Heap comparison ***********************************/
-/***************************************************************************************/
-
-class HeapLocation {
-public:
- int block_ = 0;
- int fragment_ = 0;
-
- HeapLocation() = default;
- explicit HeapLocation(int block, int fragment = 0) : block_(block), fragment_(fragment) {}
-
- bool operator==(HeapLocation const& that) const
- {
- return block_ == that.block_ && fragment_ == that.fragment_;
- }
- bool operator<(HeapLocation const& that) const
- {
- return std::make_pair(block_, fragment_) < std::make_pair(that.block_, that.fragment_);
- }
-};
-
-using HeapLocationPair = std::array<HeapLocation, 2>;
-using HeapLocationPairs = std::set<HeapLocationPair>;
-
-class HeapArea : public HeapLocation {
-public:
- bool valid_ = false;
- HeapArea() = default;
- explicit HeapArea(int block) : valid_(true) { block_ = block; }
- HeapArea(int block, int fragment) : valid_(true)
- {
- block_ = block;
- fragment_ = fragment;
- }
-};
-
-class ProcessComparisonState {
-public:
- const std::vector<IgnoredHeapRegion>* to_ignore = nullptr;
- std::vector<HeapArea> equals_to;
- std::vector<Type*> types;
- std::size_t heapsize = 0;
-
- void initHeapInformation(const s_xbt_mheap_t* heap, const std::vector<IgnoredHeapRegion>& i);
-};
-
-class StateComparator {
-public:
- s_xbt_mheap_t std_heap_copy;
- std::size_t heaplimit;
- std::array<ProcessComparisonState, 2> processStates;
-
- std::unordered_set<std::pair<const void*, const void*>, simgrid::xbt::hash<std::pair<const void*, const void*>>>
- compared_pointers;
-
- void clear()
- {
- compared_pointers.clear();
- }
-
- int initHeapInformation(RemoteProcessMemory& appli, const s_xbt_mheap_t* heap1, const s_xbt_mheap_t* heap2,
- const std::vector<IgnoredHeapRegion>& i1, const std::vector<IgnoredHeapRegion>& i2);
-
- template <int rank> HeapArea& equals_to_(std::size_t i, std::size_t j)
- {
- return processStates[rank - 1].equals_to[MAX_FRAGMENT_PER_BLOCK * i + j];
- }
- template <int rank> Type*& types_(std::size_t i, std::size_t j)
- {
- return processStates[rank - 1].types[MAX_FRAGMENT_PER_BLOCK * i + j];
- }
-
- template <int rank> HeapArea const& equals_to_(std::size_t i, std::size_t j) const
- {
- return processStates[rank - 1].equals_to[MAX_FRAGMENT_PER_BLOCK * i + j];
- }
- template <int rank> Type* const& types_(std::size_t i, std::size_t j) const
- {
- return processStates[rank - 1].types[MAX_FRAGMENT_PER_BLOCK * i + j];
- }
-
- /** Check whether two blocks are known to be matching
- *
- * @param b1 Block of state 1
- * @param b2 Block of state 2
- * @return if the blocks are known to be matching
- */
- bool blocksEqual(int b1, int b2) const
- {
- return this->equals_to_<1>(b1, 0).block_ == b2 && this->equals_to_<2>(b2, 0).block_ == b1;
- }
-
- /** Check whether two fragments are known to be matching
- *
- * @param b1 Block of state 1
- * @param f1 Fragment of state 1
- * @param b2 Block of state 2
- * @param f2 Fragment of state 2
- * @return if the fragments are known to be matching
- */
- int fragmentsEqual(int b1, int f1, int b2, int f2) const
- {
- return this->equals_to_<1>(b1, f1).block_ == b2 && this->equals_to_<1>(b1, f1).fragment_ == f2 &&
- this->equals_to_<2>(b2, f2).block_ == b1 && this->equals_to_<2>(b2, f2).fragment_ == f1;
- }
-
- void match_equals(const HeapLocationPairs* list);
-};
-
-} // namespace simgrid::mc
-
-/************************************************************************************/
-
-static ssize_t heap_comparison_ignore_size(const std::vector<simgrid::mc::IgnoredHeapRegion>* ignore_list,
- const void* address)
-{
- auto pos = std::lower_bound(ignore_list->begin(), ignore_list->end(), address,
- [](auto const& reg, auto const* addr) { return reg.address < addr; });
- return (pos != ignore_list->end() && pos->address == address) ? pos->size : -1;
-}
-
-static bool is_stack(const simgrid::mc::RemoteProcessMemory& process, const void* address)
-{
- auto const& stack_areas = process.stack_areas();
- return std::any_of(stack_areas.begin(), stack_areas.end(),
- [address](auto const& stack) { return stack.address == address; });
-}
-
-// TODO, this should depend on the snapshot?
-static bool is_block_stack(const simgrid::mc::RemoteProcessMemory& process, int block)
-{
- auto const& stack_areas = process.stack_areas();
- return std::any_of(stack_areas.begin(), stack_areas.end(),
- [block](auto const& stack) { return stack.block == block; });
-}
-
-namespace simgrid::mc {
-
-void StateComparator::match_equals(const HeapLocationPairs* list)
-{
- for (auto const& pair : *list) {
- if (pair[0].fragment_ != -1) {
- this->equals_to_<1>(pair[0].block_, pair[0].fragment_) = HeapArea(pair[1].block_, pair[1].fragment_);
- this->equals_to_<2>(pair[1].block_, pair[1].fragment_) = HeapArea(pair[0].block_, pair[0].fragment_);
- } else {
- this->equals_to_<1>(pair[0].block_, 0) = HeapArea(pair[1].block_, pair[1].fragment_);
- this->equals_to_<2>(pair[1].block_, 0) = HeapArea(pair[0].block_, pair[0].fragment_);
- }
- }
-}
-
-void ProcessComparisonState::initHeapInformation(const s_xbt_mheap_t* heap, const std::vector<IgnoredHeapRegion>& i)
-{
- auto heaplimit = heap->heaplimit;
- this->heapsize = heap->heapsize;
- this->to_ignore = &i;
- this->equals_to.assign(heaplimit * MAX_FRAGMENT_PER_BLOCK, HeapArea());
- this->types.assign(heaplimit * MAX_FRAGMENT_PER_BLOCK, nullptr);
-}
-
-int StateComparator::initHeapInformation(simgrid::mc::RemoteProcessMemory& memory, const s_xbt_mheap_t* heap1,
- const s_xbt_mheap_t* heap2, const std::vector<IgnoredHeapRegion>& i1,
- const std::vector<IgnoredHeapRegion>& i2)
-{
- if ((heap1->heaplimit != heap2->heaplimit) || (heap1->heapsize != heap2->heapsize))
- return -1;
- this->heaplimit = heap1->heaplimit;
- this->std_heap_copy = *memory.get_heap();
- this->processStates[0].initHeapInformation(heap1, i1);
- this->processStates[1].initHeapInformation(heap2, i2);
- return 0;
-}
-
-// TODO, have a robust way to find it in O(1)
-static inline Region* MC_get_heap_region(const Snapshot& snapshot)
-{
- for (auto const& region : snapshot.snapshot_regions_)
- if (region->region_type() == RegionType::Heap)
- return region.get();
- xbt_die("No heap region");
-}
-
-static bool heap_area_differ(const RemoteProcessMemory& process, StateComparator& state, const void* area1,
- const void* area2, const Snapshot& snapshot1, const Snapshot& snapshot2,
- HeapLocationPairs* previous, Type* type, int pointer_level);
-
-/* Compares the content of each heap fragment between the two states, at the bit level.
- *
- * This operation is costly (about 5 seconds per snapshots' pair to compare on a small program),
- * but hard to optimize because our algorithm is too hackish.
- *
- * Going at bit level can trigger syntaxtic differences on states that are semantically equivalent.
- *
- * Padding bytes constitute the first source of such syntaxtic difference: Any malloced memory contains spaces that
- * are not used to enforce the memory alignment constraints of the CPU. So, cruft of irrelevant changes could get
- * added on these bits. But this case is handled properly, as any memory block is zeroed by mmalloc before being handled
- * back, not only for calloc but also for malloc. So the memory interstices due to padding bytes are properly zeroed.
- *
- * Another source of such change comes from the order of mallocs, that may well change from one execution path to
- * another. This will change the malloc fragment in which the data is stored and the pointer values (syntaxtic
- * difference) while the semantic of the state remains the same.
- *
- * To fix this, this code relies on a hugly hack. When we see a difference during the bit-level comparison,
- * we first check if it could be explained by a pointer-to-block difference. Ie, if when interpreting the memory
- * area containing that difference as a pointer, I get the pointer to a valid fragment in the heap (in both snapshots).
- *
- * This is why we cannot pre-compute a bit-level hash of the heap content: we discover the pointers to other memory
- * fragment when a difference is found during the bit-level exploration. Fixing this would require to save typing
- * information about the memory fragments, which is something that could be done with https://github.com/tudasc/TypeART
- * This would give us all pointers in the mallocated memory, allowing the graph traversal needed to precompute the hash.
- *
- * Using a hash without paying attention to malloc fragment reordering would lead to false negatives:
- * semantically equivalent states would be detected as [syntaxically] different. It's of no importance for the
- * state-equality reduction (we would re-explore semantically equivalent states), but it would endanger the soundness
- * of the liveness model-checker, as state-equality is used to detect the loops that constitute the accepting states of
- * the verified property. So we could miss counter-examples to the verified property. Not good. Not good at all.
- */
-static bool mmalloc_heap_differ(const RemoteProcessMemory& process, StateComparator& state, const Snapshot& snapshot1,
- const Snapshot& snapshot2)
-{
- /* Check busy blocks */
- size_t i1 = 1;
-
- malloc_info heapinfo_temp1;
- malloc_info heapinfo_temp2;
- malloc_info heapinfo_temp2b;
-
- const Region* heap_region1 = MC_get_heap_region(snapshot1);
- const Region* heap_region2 = MC_get_heap_region(snapshot2);
-
- // This is the address of std_heap->heapinfo in the application process:
- uint64_t heapinfo_address = process.heap_address.address() + offsetof(s_xbt_mheap_t, heapinfo);
-
- // This is in snapshot do not use them directly:
- const malloc_info* heapinfos1 = snapshot1.read(remote<malloc_info*>(heapinfo_address));
- const malloc_info* heapinfos2 = snapshot2.read(remote<malloc_info*>(heapinfo_address));
-
- while (i1 < state.heaplimit) {
- const auto* heapinfo1 =
- static_cast<malloc_info*>(heap_region1->read(&heapinfo_temp1, &heapinfos1[i1], sizeof(malloc_info)));
- const auto* heapinfo2 =
- static_cast<malloc_info*>(heap_region2->read(&heapinfo_temp2, &heapinfos2[i1], sizeof(malloc_info)));
-
- if (heapinfo1->type == MMALLOC_TYPE_FREE || heapinfo1->type == MMALLOC_TYPE_HEAPINFO) { /* Free block */
- i1 ++;
- continue;
- }
-
- xbt_assert(heapinfo1->type >= 0, "Unknown mmalloc block type: %d", heapinfo1->type);
-
- void* addr_block1 = (ADDR2UINT(i1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
-
- if (heapinfo1->type == MMALLOC_TYPE_UNFRAGMENTED) { /* Large block */
- if (is_stack(process, addr_block1)) {
- for (size_t k = 0; k < heapinfo1->busy_block.size; k++)
- state.equals_to_<1>(i1 + k, 0) = HeapArea(i1, -1);
- for (size_t k = 0; k < heapinfo2->busy_block.size; k++)
- state.equals_to_<2>(i1 + k, 0) = HeapArea(i1, -1);
- i1 += heapinfo1->busy_block.size;
- continue;
- }
-
- if (state.equals_to_<1>(i1, 0).valid_) {
- i1++;
- continue;
- }
-
- size_t i2 = 1;
- bool equal = false;
-
- /* Try first to associate to same block in the other heap */
- if (heapinfo2->type == heapinfo1->type && state.equals_to_<2>(i1, 0).valid_ == 0) {
- const void* addr_block2 = (ADDR2UINT(i1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
- if (not heap_area_differ(process, state, addr_block1, addr_block2, snapshot1, snapshot2, nullptr, nullptr, 0)) {
- for (size_t k = 1; k < heapinfo2->busy_block.size; k++)
- state.equals_to_<2>(i1 + k, 0) = HeapArea(i1, -1);
- for (size_t k = 1; k < heapinfo1->busy_block.size; k++)
- state.equals_to_<1>(i1 + k, 0) = HeapArea(i1, -1);
- equal = true;
- i1 += heapinfo1->busy_block.size;
- }
- }
-
- while (i2 < state.heaplimit && not equal) {
- const void* addr_block2 = (ADDR2UINT(i2) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
-
- if (i2 == i1) {
- i2++;
- continue;
- }
-
- const auto* heapinfo2b =
- static_cast<malloc_info*>(heap_region2->read(&heapinfo_temp2b, &heapinfos2[i2], sizeof(malloc_info)));
-
- if (heapinfo2b->type != MMALLOC_TYPE_UNFRAGMENTED) {
- i2++;
- continue;
- }
-
- if (state.equals_to_<2>(i2, 0).valid_) {
- i2++;
- continue;
- }
-
- if (not heap_area_differ(process, state, addr_block1, addr_block2, snapshot1, snapshot2, nullptr, nullptr, 0)) {
- for (size_t k = 1; k < heapinfo2b->busy_block.size; k++)
- state.equals_to_<2>(i2 + k, 0) = HeapArea(i1, -1);
- for (size_t k = 1; k < heapinfo1->busy_block.size; k++)
- state.equals_to_<1>(i1 + k, 0) = HeapArea(i2, -1);
- equal = true;
- i1 += heapinfo1->busy_block.size;
- }
- i2++;
- }
-
- if (not equal) {
- XBT_DEBUG("Block %zu not found (size_used = %zu, addr = %p)", i1, heapinfo1->busy_block.busy_size, addr_block1);
- return true;
- }
- } else { /* Fragmented block */
- for (size_t j1 = 0; j1 < (size_t)(BLOCKSIZE >> heapinfo1->type); j1++) {
- if (heapinfo1->busy_frag.frag_size[j1] == -1) /* Free fragment_ */
- continue;
-
- if (state.equals_to_<1>(i1, j1).valid_)
- continue;
-
- void* addr_frag1 = (char*)addr_block1 + (j1 << heapinfo1->type);
-
- size_t i2 = 1;
- bool equal = false;
-
- /* Try first to associate to same fragment_ in the other heap */
- if (heapinfo2->type == heapinfo1->type && not state.equals_to_<2>(i1, j1).valid_) {
- const void* addr_block2 = (ADDR2UINT(i1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
- const void* addr_frag2 = (const char*)addr_block2 + (j1 << heapinfo2->type);
- if (not heap_area_differ(process, state, addr_frag1, addr_frag2, snapshot1, snapshot2, nullptr, nullptr, 0))
- equal = true;
- }
-
- while (i2 < state.heaplimit && not equal) {
- const auto* heapinfo2b =
- static_cast<malloc_info*>(heap_region2->read(&heapinfo_temp2b, &heapinfos2[i2], sizeof(malloc_info)));
-
- if (heapinfo2b->type == MMALLOC_TYPE_FREE || heapinfo2b->type == MMALLOC_TYPE_HEAPINFO) {
- i2 ++;
- continue;
- }
-
- // We currently do not match fragments with unfragmented blocks (maybe we should).
- if (heapinfo2b->type == MMALLOC_TYPE_UNFRAGMENTED) {
- i2++;
- continue;
- }
-
- xbt_assert(heapinfo2b->type >= 0, "Unknown mmalloc block type: %d", heapinfo2b->type);
-
- for (size_t j2 = 0; j2 < (size_t)(BLOCKSIZE >> heapinfo2b->type); j2++) {
- if (i2 == i1 && j2 == j1)
- continue;
-
- if (state.equals_to_<2>(i2, j2).valid_)
- continue;
-
- const void* addr_block2 = (ADDR2UINT(i2) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
- const void* addr_frag2 = (const char*)addr_block2 + (j2 << heapinfo2b->type);
-
- if (not heap_area_differ(process, state, addr_frag1, addr_frag2, snapshot1, snapshot2, nullptr, nullptr,
- 0)) {
- equal = true;
- break;
- }
- }
- i2++;
- }
-
- if (not equal) {
- XBT_DEBUG("Block %zu, fragment_ %zu not found (size_used = %zd, address = %p)\n", i1, j1,
- heapinfo1->busy_frag.frag_size[j1], addr_frag1);
- return true;
- }
- }
- i1++;
- }
- }
-
- /* All blocks/fragments are equal to another block/fragment_ ? */
- for (size_t i = 1; i < state.heaplimit; i++) {
- const auto* heapinfo1 =
- static_cast<malloc_info*>(heap_region1->read(&heapinfo_temp1, &heapinfos1[i], sizeof(malloc_info)));
-
- if (heapinfo1->type == MMALLOC_TYPE_UNFRAGMENTED && i1 == state.heaplimit && heapinfo1->busy_block.busy_size > 0 &&
- not state.equals_to_<1>(i, 0).valid_) {
- XBT_DEBUG("Block %zu not found (size used = %zu)", i, heapinfo1->busy_block.busy_size);
- return true;
- }
-
- if (heapinfo1->type <= 0)
- continue;
- for (size_t j = 0; j < (size_t)(BLOCKSIZE >> heapinfo1->type); j++)
- if (i1 == state.heaplimit && heapinfo1->busy_frag.frag_size[j] > 0 && not state.equals_to_<1>(i, j).valid_) {
- XBT_DEBUG("Block %zu, Fragment %zu not found (size used = %zd)", i, j, heapinfo1->busy_frag.frag_size[j]);
- return true;
- }
- }
-
- for (size_t i = 1; i < state.heaplimit; i++) {
- const auto* heapinfo2 =
- static_cast<malloc_info*>(heap_region2->read(&heapinfo_temp2, &heapinfos2[i], sizeof(malloc_info)));
- if (heapinfo2->type == MMALLOC_TYPE_UNFRAGMENTED && i1 == state.heaplimit && heapinfo2->busy_block.busy_size > 0 &&
- not state.equals_to_<2>(i, 0).valid_) {
- XBT_DEBUG("Block %zu not found (size used = %zu)", i,
- heapinfo2->busy_block.busy_size);
- return true;
- }
-
- if (heapinfo2->type <= 0)
- continue;
-
- for (size_t j = 0; j < (size_t)(BLOCKSIZE >> heapinfo2->type); j++)
- if (i1 == state.heaplimit && heapinfo2->busy_frag.frag_size[j] > 0 && not state.equals_to_<2>(i, j).valid_) {
- XBT_DEBUG("Block %zu, Fragment %zu not found (size used = %zd)",
- i, j, heapinfo2->busy_frag.frag_size[j]);
- return true;
- }
- }
- return false;
-}
-
-/**
- *
- * @param state
- * @param real_area1 Process address for state 1
- * @param real_area2 Process address for state 2
- * @param snapshot1 Snapshot of state 1
- * @param snapshot2 Snapshot of state 2
- * @param previous
- * @param size
- * @param check_ignore
- * @return true when different, false otherwise (same or unknown)
- */
-static bool heap_area_differ_without_type(const RemoteProcessMemory& process, StateComparator& state,
- const void* real_area1, const void* real_area2, const Snapshot& snapshot1,
- const Snapshot& snapshot2, HeapLocationPairs* previous, int size,
- int check_ignore)
-{
- const Region* heap_region1 = MC_get_heap_region(snapshot1);
- const Region* heap_region2 = MC_get_heap_region(snapshot2);
-
- for (int i = 0; i < size; ) {
- if (check_ignore > 0) {
- ssize_t ignore1 = heap_comparison_ignore_size(state.processStates[0].to_ignore, (const char*)real_area1 + i);
- if (ignore1 != -1) {
- ssize_t ignore2 = heap_comparison_ignore_size(state.processStates[1].to_ignore, (const char*)real_area2 + i);
- if (ignore2 == ignore1) {
- if (ignore1 == 0) {
- return false;
- } else {
- i = i + ignore2;
- check_ignore--;
- continue;
- }
- }
- }
- }
-
- if (MC_snapshot_region_memcmp((const char*)real_area1 + i, heap_region1, (const char*)real_area2 + i, heap_region2,
- 1) != 0) {
- int pointer_align = (i / sizeof(void *)) * sizeof(void *);
- const void* addr_pointed1 = snapshot1.read(remote((void* const*)((const char*)real_area1 + pointer_align)));
- const void* addr_pointed2 = snapshot2.read(remote((void* const*)((const char*)real_area2 + pointer_align)));
-
- if (process.in_maestro_stack(remote(addr_pointed1)) && process.in_maestro_stack(remote(addr_pointed2))) {
- i = pointer_align + sizeof(void *);
- continue;
- }
-
- if (snapshot1.on_heap(addr_pointed1) && snapshot2.on_heap(addr_pointed2)) {
- // Both addresses are in the heap:
- if (heap_area_differ(process, state, addr_pointed1, addr_pointed2, snapshot1, snapshot2, previous, nullptr, 0))
- return true;
- i = pointer_align + sizeof(void *);
- continue;
- }
- return true;
- }
- i++;
- }
- return false;
-}
-
-/**
- *
- * @param state
- * @param real_area1 Process address for state 1
- * @param real_area2 Process address for state 2
- * @param snapshot1 Snapshot of state 1
- * @param snapshot2 Snapshot of state 2
- * @param previous
- * @param type
- * @param area_size either a byte_size or an elements_count (?)
- * @param check_ignore
- * @param pointer_level
- * @return true when different, false otherwise (same or unknown)
- */
-static bool heap_area_differ_with_type(const simgrid::mc::RemoteProcessMemory& process, StateComparator& state,
- const void* real_area1, const void* real_area2, const Snapshot& snapshot1,
- const Snapshot& snapshot2, HeapLocationPairs* previous, const Type* type,
- int area_size, int check_ignore, int pointer_level)
-{
- // HACK: This should not happen but in practice, there are some
- // DW_TAG_typedef without an associated DW_AT_type:
- //<1><538832>: Abbrev Number: 111 (DW_TAG_typedef)
- // <538833> DW_AT_name : (indirect string, offset: 0x2292f3): gregset_t
- // <538837> DW_AT_decl_file : 98
- // <538838> DW_AT_decl_line : 37
- if (type == nullptr)
- return false;
-
- if (is_stack(process, real_area1) && is_stack(process, real_area2))
- return false;
-
- if (check_ignore > 0) {
- ssize_t ignore1 = heap_comparison_ignore_size(state.processStates[0].to_ignore, real_area1);
- if (ignore1 > 0 && heap_comparison_ignore_size(state.processStates[1].to_ignore, real_area2) == ignore1)
- return false;
- }
-
- const Type* subtype;
- const Type* subsubtype;
- int elm_size;
- const void* addr_pointed1;
- const void* addr_pointed2;
-
- const Region* heap_region1 = MC_get_heap_region(snapshot1);
- const Region* heap_region2 = MC_get_heap_region(snapshot2);
-
- switch (type->type) {
- case DW_TAG_unspecified_type:
- return true;
-
- case DW_TAG_base_type:
- if (not type->name.empty() && type->name == "char") { /* String, hence random (arbitrary ?) size */
- if (real_area1 == real_area2)
- return false;
- else
- return MC_snapshot_region_memcmp(real_area1, heap_region1, real_area2, heap_region2, area_size) != 0;
- } else {
- if (area_size != -1 && type->byte_size != area_size)
- return false;
- else
- return MC_snapshot_region_memcmp(real_area1, heap_region1, real_area2, heap_region2, type->byte_size) != 0;
- }
-
- case DW_TAG_enumeration_type:
- if (area_size != -1 && type->byte_size != area_size)
- return false;
- return MC_snapshot_region_memcmp(real_area1, heap_region1, real_area2, heap_region2, type->byte_size) != 0;
-
- case DW_TAG_typedef:
- case DW_TAG_const_type:
- case DW_TAG_volatile_type:
- return heap_area_differ_with_type(process, state, real_area1, real_area2, snapshot1, snapshot2, previous,
- type->subtype, area_size, check_ignore, pointer_level);
-
- case DW_TAG_array_type:
- subtype = type->subtype;
- switch (subtype->type) {
- case DW_TAG_unspecified_type:
- return true;
-
- case DW_TAG_base_type:
- case DW_TAG_enumeration_type:
- case DW_TAG_pointer_type:
- case DW_TAG_reference_type:
- case DW_TAG_rvalue_reference_type:
- case DW_TAG_structure_type:
- case DW_TAG_class_type:
- case DW_TAG_union_type:
- if (subtype->full_type)
- subtype = subtype->full_type;
- elm_size = subtype->byte_size;
- break;
- // TODO, just remove the type indirection?
- case DW_TAG_const_type:
- case DW_TAG_typedef:
- case DW_TAG_volatile_type:
- subsubtype = subtype->subtype;
- if (subsubtype->full_type)
- subsubtype = subsubtype->full_type;
- elm_size = subsubtype->byte_size;
- break;
- default:
- return false;
- }
- for (int i = 0; i < type->element_count; i++) {
- // TODO, add support for variable stride (DW_AT_byte_stride)
- if (heap_area_differ_with_type(process, state, (const char*)real_area1 + (i * elm_size),
- (const char*)real_area2 + (i * elm_size), snapshot1, snapshot2, previous,
- type->subtype, subtype->byte_size, check_ignore, pointer_level))
- return true;
- }
- return false;
-
- case DW_TAG_reference_type:
- case DW_TAG_rvalue_reference_type:
- case DW_TAG_pointer_type:
- if (type->subtype && type->subtype->type == DW_TAG_subroutine_type) {
- addr_pointed1 = snapshot1.read(remote((void* const*)real_area1));
- addr_pointed2 = snapshot2.read(remote((void* const*)real_area2));
- return (addr_pointed1 != addr_pointed2);
- }
- pointer_level++;
- if (pointer_level <= 1) {
- addr_pointed1 = snapshot1.read(remote((void* const*)real_area1));
- addr_pointed2 = snapshot2.read(remote((void* const*)real_area2));
- if (snapshot1.on_heap(addr_pointed1) && snapshot2.on_heap(addr_pointed2))
- return heap_area_differ(process, state, addr_pointed1, addr_pointed2, snapshot1, snapshot2, previous,
- type->subtype, pointer_level);
- else
- return (addr_pointed1 != addr_pointed2);
- }
- for (size_t i = 0; i < (area_size / sizeof(void*)); i++) {
- addr_pointed1 = snapshot1.read(remote((void* const*)((const char*)real_area1 + i * sizeof(void*))));
- addr_pointed2 = snapshot2.read(remote((void* const*)((const char*)real_area2 + i * sizeof(void*))));
- bool differ = snapshot1.on_heap(addr_pointed1) && snapshot2.on_heap(addr_pointed2)
- ? heap_area_differ(process, state, addr_pointed1, addr_pointed2, snapshot1, snapshot2,
- previous, type->subtype, pointer_level)
- : addr_pointed1 != addr_pointed2;
- if (differ)
- return true;
- }
- return false;
-
- case DW_TAG_structure_type:
- case DW_TAG_class_type:
- if (type->full_type)
- type = type->full_type;
- if (type->byte_size == 0)
- return false;
- if (area_size != -1 && type->byte_size != area_size) {
- if (area_size <= type->byte_size || area_size % type->byte_size != 0)
- return false;
- for (size_t i = 0; i < (size_t)(area_size / type->byte_size); i++) {
- if (heap_area_differ_with_type(process, state, (const char*)real_area1 + i * type->byte_size,
- (const char*)real_area2 + i * type->byte_size, snapshot1, snapshot2, previous,
- type, -1, check_ignore, 0))
- return true;
- }
- } else {
- for (const simgrid::mc::Member& member : type->members) {
- // TODO, optimize this? (for the offset case)
- const void* real_member1 = dwarf::resolve_member(real_area1, type, &member, &snapshot1);
- const void* real_member2 = dwarf::resolve_member(real_area2, type, &member, &snapshot2);
- if (heap_area_differ_with_type(process, state, real_member1, real_member2, snapshot1, snapshot2, previous,
- member.type, -1, check_ignore, 0))
- return true;
- }
- }
- return false;
-
- case DW_TAG_union_type:
- return heap_area_differ_without_type(process, state, real_area1, real_area2, snapshot1, snapshot2, previous,
- type->byte_size, check_ignore);
-
- default:
- THROW_IMPOSSIBLE;
- }
-}
-
-/** Infer the type of a part of the block from the type of the block
- *
- * TODO, handle DW_TAG_array_type as well as arrays of the object ((*p)[5], p[5])
- *
- * TODO, handle subfields ((*p).bar.foo, (*p)[5].bar…)
- *
- * @param type DWARF type ID of the root address
- * @param area_size
- * @return DWARF type ID for given offset
- */
-static Type* get_offset_type(void* real_base_address, Type* type, int offset, int area_size, const Snapshot& snapshot)
-{
- // Beginning of the block, the inferred variable type if the type of the block:
- if (offset == 0)
- return type;
-
- switch (type->type) {
- case DW_TAG_structure_type:
- case DW_TAG_class_type:
- if (type->full_type)
- type = type->full_type;
- if (area_size != -1 && type->byte_size != area_size) {
- if (area_size > type->byte_size && area_size % type->byte_size == 0)
- return type;
- else
- return nullptr;
- }
-
- for (const simgrid::mc::Member& member : type->members) {
- if (member.has_offset_location()) {
- // We have the offset, use it directly (shortcut):
- if (member.offset() == offset)
- return member.type;
- } else {
- void* real_member = dwarf::resolve_member(real_base_address, type, &member, &snapshot);
- if ((char*)real_member - (char*)real_base_address == offset)
- return member.type;
- }
- }
- return nullptr;
-
- default:
- /* FIXME: other cases ? */
- return nullptr;
- }
-}
-
-/**
- *
- * @param area1 Process address for state 1
- * @param area2 Process address for state 2
- * @param snapshot1 Snapshot of state 1
- * @param snapshot2 Snapshot of state 2
- * @param previous Pairs of blocks already compared on the current path (or nullptr)
- * @param type_id Type of variable
- * @param pointer_level
- * @return true when different, false otherwise (same or unknown)
- */
-static bool heap_area_differ(const RemoteProcessMemory& process, StateComparator& state, const void* area1,
- const void* area2, const Snapshot& snapshot1, const Snapshot& snapshot2,
- HeapLocationPairs* previous, Type* type, int pointer_level)
-{
- ssize_t block1;
- ssize_t block2;
- ssize_t size;
- int check_ignore = 0;
-
- int type_size = -1;
- int offset1 = 0;
- int offset2 = 0;
- int new_size1 = -1;
- int new_size2 = -1;
-
- Type* new_type1 = nullptr;
-
- bool match_pairs = false;
-
- // This is the address of std_heap->heapinfo in the application process:
- uint64_t heapinfo_address = process.heap_address.address() + offsetof(s_xbt_mheap_t, heapinfo);
-
- const malloc_info* heapinfos1 = snapshot1.read(remote<malloc_info*>(heapinfo_address));
- const malloc_info* heapinfos2 = snapshot2.read(remote<malloc_info*>(heapinfo_address));
-
- malloc_info heapinfo_temp1;
- malloc_info heapinfo_temp2;
-
- simgrid::mc::HeapLocationPairs current;
- if (previous == nullptr) {
- previous = ¤t;
- match_pairs = true;
- }
-
- // Get block number:
- block1 = ((const char*)area1 - (const char*)state.std_heap_copy.heapbase) / BLOCKSIZE + 1;
- block2 = ((const char*)area2 - (const char*)state.std_heap_copy.heapbase) / BLOCKSIZE + 1;
-
- // If either block is a stack block:
- if (is_block_stack(process, (int)block1) && is_block_stack(process, (int)block2)) {
- previous->insert(HeapLocationPair{{HeapLocation(block1, -1), HeapLocation(block2, -1)}});
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- // If either block is not in the expected area of memory:
- if (((const char*)area1 < (const char*)state.std_heap_copy.heapbase) ||
- (block1 > (ssize_t)state.processStates[0].heapsize) ||
- ((const char*)area2 < (const char*)state.std_heap_copy.heapbase) ||
- (block2 > (ssize_t)state.processStates[1].heapsize)) {
- return true;
- }
-
- // Process address of the block:
- void* real_addr_block1 = (ADDR2UINT(block1) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
- void* real_addr_block2 = (ADDR2UINT(block2) - 1) * BLOCKSIZE + (char*)state.std_heap_copy.heapbase;
-
- if (type) {
- if (type->full_type)
- type = type->full_type;
-
- // This assume that for "boring" types (volatile ...) byte_size is absent:
- while (type->byte_size == 0 && type->subtype != nullptr)
- type = type->subtype;
-
- // Find type_size:
- if (type->type == DW_TAG_pointer_type ||
- (type->type == DW_TAG_base_type && not type->name.empty() && type->name == "char"))
- type_size = -1;
- else
- type_size = type->byte_size;
- }
-
- const Region* heap_region1 = MC_get_heap_region(snapshot1);
- const Region* heap_region2 = MC_get_heap_region(snapshot2);
-
- const auto* heapinfo1 =
- static_cast<malloc_info*>(heap_region1->read(&heapinfo_temp1, &heapinfos1[block1], sizeof(malloc_info)));
- const auto* heapinfo2 =
- static_cast<malloc_info*>(heap_region2->read(&heapinfo_temp2, &heapinfos2[block2], sizeof(malloc_info)));
-
- if ((heapinfo1->type == MMALLOC_TYPE_FREE || heapinfo1->type==MMALLOC_TYPE_HEAPINFO)
- && (heapinfo2->type == MMALLOC_TYPE_FREE || heapinfo2->type ==MMALLOC_TYPE_HEAPINFO)) {
- /* Free block */
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if (heapinfo1->type == MMALLOC_TYPE_UNFRAGMENTED && heapinfo2->type == MMALLOC_TYPE_UNFRAGMENTED) {
- /* Complete block */
-
- // TODO, lookup variable type from block type as done for fragmented blocks
-
- if (state.equals_to_<1>(block1, 0).valid_ && state.equals_to_<2>(block2, 0).valid_ &&
- state.blocksEqual(block1, block2)) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if (type_size != -1 && type_size != (ssize_t)heapinfo1->busy_block.busy_size &&
- type_size != (ssize_t)heapinfo2->busy_block.busy_size && type->name.empty()) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if (heapinfo1->busy_block.size != heapinfo2->busy_block.size ||
- heapinfo1->busy_block.busy_size != heapinfo2->busy_block.busy_size)
- return true;
-
- if (not previous->insert(HeapLocationPair{{HeapLocation(block1, -1), HeapLocation(block2, -1)}}).second) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- size = heapinfo1->busy_block.busy_size;
-
- // Remember (basic) type inference.
- // The current data structure only allows us to do this for the whole block.
- if (type != nullptr && area1 == real_addr_block1)
- state.types_<1>(block1, 0) = type;
- if (type != nullptr && area2 == real_addr_block2)
- state.types_<2>(block2, 0) = type;
-
- if (size <= 0) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if (heapinfo1->busy_block.ignore > 0 && heapinfo2->busy_block.ignore == heapinfo1->busy_block.ignore)
- check_ignore = heapinfo1->busy_block.ignore;
-
- } else if ((heapinfo1->type > 0) && (heapinfo2->type > 0)) { /* Fragmented block */
- // Fragment number:
- ssize_t frag1 = (ADDR2UINT(area1) % BLOCKSIZE) >> heapinfo1->type;
- ssize_t frag2 = (ADDR2UINT(area2) % BLOCKSIZE) >> heapinfo2->type;
-
- // Process address of the fragment_:
- void* real_addr_frag1 = (char*)real_addr_block1 + (frag1 << heapinfo1->type);
- void* real_addr_frag2 = (char*)real_addr_block2 + (frag2 << heapinfo2->type);
-
- // Check the size of the fragments against the size of the type:
- if (type_size != -1) {
- if (heapinfo1->busy_frag.frag_size[frag1] == -1 || heapinfo2->busy_frag.frag_size[frag2] == -1) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
- // ?
- if (type_size != heapinfo1->busy_frag.frag_size[frag1]
- || type_size != heapinfo2->busy_frag.frag_size[frag2]) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
- }
-
- // Check if the blocks are already matched together:
- if (state.equals_to_<1>(block1, frag1).valid_ && state.equals_to_<2>(block2, frag2).valid_ &&
- state.fragmentsEqual(block1, frag1, block2, frag2)) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
- // Compare the size of both fragments:
- if (heapinfo1->busy_frag.frag_size[frag1] != heapinfo2->busy_frag.frag_size[frag2]) {
- if (type_size == -1) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- } else
- return true;
- }
-
- // Size of the fragment_:
- size = heapinfo1->busy_frag.frag_size[frag1];
-
- // Remember (basic) type inference.
- // The current data structure only allows us to do this for the whole fragment_.
- if (type != nullptr && area1 == real_addr_frag1)
- state.types_<1>(block1, frag1) = type;
- if (type != nullptr && area2 == real_addr_frag2)
- state.types_<2>(block2, frag2) = type;
-
- // The type of the variable is already known:
- if (type) {
- new_type1 = type;
- }
- // Type inference from the block type.
- else if (state.types_<1>(block1, frag1) != nullptr || state.types_<2>(block2, frag2) != nullptr) {
- Type* new_type2 = nullptr;
-
- offset1 = (const char*)area1 - (const char*)real_addr_frag1;
- offset2 = (const char*)area2 - (const char*)real_addr_frag2;
-
- if (state.types_<1>(block1, frag1) != nullptr && state.types_<2>(block2, frag2) != nullptr) {
- new_type1 = get_offset_type(real_addr_frag1, state.types_<1>(block1, frag1), offset1, size, snapshot1);
- new_type2 = get_offset_type(real_addr_frag2, state.types_<2>(block2, frag2), offset1, size, snapshot2);
- } else if (state.types_<1>(block1, frag1) != nullptr) {
- new_type1 = get_offset_type(real_addr_frag1, state.types_<1>(block1, frag1), offset1, size, snapshot1);
- new_type2 = get_offset_type(real_addr_frag2, state.types_<1>(block1, frag1), offset2, size, snapshot2);
- } else if (state.types_<2>(block2, frag2) != nullptr) {
- new_type1 = get_offset_type(real_addr_frag1, state.types_<2>(block2, frag2), offset1, size, snapshot1);
- new_type2 = get_offset_type(real_addr_frag2, state.types_<2>(block2, frag2), offset2, size, snapshot2);
- } else {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if (new_type1 != nullptr && new_type2 != nullptr && new_type1 != new_type2) {
- type = new_type1;
- while (type->byte_size == 0 && type->subtype != nullptr)
- type = type->subtype;
- new_size1 = type->byte_size;
-
- type = new_type2;
- while (type->byte_size == 0 && type->subtype != nullptr)
- type = type->subtype;
- new_size2 = type->byte_size;
-
- } else {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
- }
-
- if (new_size1 > 0 && new_size1 == new_size2) {
- type = new_type1;
- size = new_size1;
- }
-
- if (offset1 == 0 && offset2 == 0 &&
- not previous->insert(HeapLocationPair{{HeapLocation(block1, frag1), HeapLocation(block2, frag2)}}).second) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if (size <= 0) {
- if (match_pairs)
- state.match_equals(previous);
- return false;
- }
-
- if ((heapinfo1->busy_frag.ignore[frag1] > 0) &&
- (heapinfo2->busy_frag.ignore[frag2] == heapinfo1->busy_frag.ignore[frag1]))
- check_ignore = heapinfo1->busy_frag.ignore[frag1];
- } else
- return true;
-
- /* Start comparison */
- if (type ? heap_area_differ_with_type(process, state, area1, area2, snapshot1, snapshot2, previous, type, size,
- check_ignore, pointer_level)
- : heap_area_differ_without_type(process, state, area1, area2, snapshot1, snapshot2, previous, size,
- check_ignore))
- return true;
-
- if (match_pairs)
- state.match_equals(previous);
- return false;
-}
-} // namespace simgrid::mc
-
-/************************** Snapshot comparison *******************************/
-/******************************************************************************/
-
-static bool areas_differ_with_type(const simgrid::mc::RemoteProcessMemory& process, simgrid::mc::StateComparator& state,
- const void* real_area1, const simgrid::mc::Snapshot& snapshot1,
- simgrid::mc::Region* region1, const void* real_area2,
- const simgrid::mc::Snapshot& snapshot2, simgrid::mc::Region* region2,
- const simgrid::mc::Type* type, int pointer_level)
-{
- const simgrid::mc::Type* subtype;
- const simgrid::mc::Type* subsubtype;
- int elm_size;
-
- xbt_assert(type != nullptr);
- switch (type->type) {
- case DW_TAG_unspecified_type:
- return true;
-
- case DW_TAG_base_type:
- case DW_TAG_enumeration_type:
- case DW_TAG_union_type:
- return MC_snapshot_region_memcmp(real_area1, region1, real_area2, region2, type->byte_size) != 0;
- case DW_TAG_typedef:
- case DW_TAG_volatile_type:
- case DW_TAG_const_type:
- return areas_differ_with_type(process, state, real_area1, snapshot1, region1, real_area2, snapshot2, region2,
- type->subtype, pointer_level);
- case DW_TAG_array_type:
- subtype = type->subtype;
- switch (subtype->type) {
- case DW_TAG_unspecified_type:
- return true;
-
- case DW_TAG_base_type:
- case DW_TAG_enumeration_type:
- case DW_TAG_pointer_type:
- case DW_TAG_reference_type:
- case DW_TAG_rvalue_reference_type:
- case DW_TAG_structure_type:
- case DW_TAG_class_type:
- case DW_TAG_union_type:
- if (subtype->full_type)
- subtype = subtype->full_type;
- elm_size = subtype->byte_size;
- break;
- case DW_TAG_const_type:
- case DW_TAG_typedef:
- case DW_TAG_volatile_type:
- subsubtype = subtype->subtype;
- if (subsubtype->full_type)
- subsubtype = subsubtype->full_type;
- elm_size = subsubtype->byte_size;
- break;
- default:
- return false;
- }
- for (int i = 0; i < type->element_count; i++) {
- size_t off = i * elm_size;
- if (areas_differ_with_type(process, state, (const char*)real_area1 + off, snapshot1, region1,
- (const char*)real_area2 + off, snapshot2, region2, type->subtype, pointer_level))
- return true;
- }
- break;
- case DW_TAG_pointer_type:
- case DW_TAG_reference_type:
- case DW_TAG_rvalue_reference_type: {
- const void* addr_pointed1 = MC_region_read_pointer(region1, real_area1);
- const void* addr_pointed2 = MC_region_read_pointer(region2, real_area2);
-
- if (type->subtype && type->subtype->type == DW_TAG_subroutine_type)
- return (addr_pointed1 != addr_pointed2);
- if (addr_pointed1 == nullptr && addr_pointed2 == nullptr)
- return false;
- if (addr_pointed1 == nullptr || addr_pointed2 == nullptr)
- return true;
- if (not state.compared_pointers.insert(std::make_pair(addr_pointed1, addr_pointed2)).second)
- return false;
-
- pointer_level++;
-
- // Some cases are not handled here:
- // * the pointers lead to different areas (one to the heap, the other to the RW segment ...)
- // * a pointer leads to the read-only segment of the current object
- // * a pointer lead to a different ELF object
-
- if (snapshot1.on_heap(addr_pointed1)) {
- if (not snapshot2.on_heap(addr_pointed2))
- return true;
- // The pointers are both in the heap:
- return simgrid::mc::heap_area_differ(process, state, addr_pointed1, addr_pointed2, snapshot1, snapshot2,
- nullptr, type->subtype, pointer_level);
-
- } else if (region1->contain(simgrid::mc::remote(addr_pointed1))) {
- // The pointers are both in the current object R/W segment:
- if (not region2->contain(simgrid::mc::remote(addr_pointed2)))
- return true;
- if (not type->type_id)
- return (addr_pointed1 != addr_pointed2);
- else
- return areas_differ_with_type(process, state, addr_pointed1, snapshot1, region1, addr_pointed2, snapshot2,
- region2, type->subtype, pointer_level);
- } else {
- // TODO, We do not handle very well the case where
- // it belongs to a different (non-heap) region from the current one.
-
- return (addr_pointed1 != addr_pointed2);
- }
- }
- case DW_TAG_structure_type:
- case DW_TAG_class_type:
- for (const simgrid::mc::Member& member : type->members) {
- const void* member1 = simgrid::dwarf::resolve_member(real_area1, type, &member, &snapshot1);
- const void* member2 = simgrid::dwarf::resolve_member(real_area2, type, &member, &snapshot2);
- simgrid::mc::Region* subregion1 = snapshot1.get_region(member1, region1); // region1 is hinted
- simgrid::mc::Region* subregion2 = snapshot2.get_region(member2, region2); // region2 is hinted
- if (areas_differ_with_type(process, state, member1, snapshot1, subregion1, member2, snapshot2, subregion2,
- member.type, pointer_level))
- return true;
- }
- break;
- case DW_TAG_subroutine_type:
- return false;
- default:
- XBT_VERB("Unknown case: %d", type->type);
- break;
- }
-
- return false;
-}
-
-static bool global_variables_differ(const simgrid::mc::RemoteProcessMemory& process,
- simgrid::mc::StateComparator& state,
- const simgrid::mc::ObjectInformation* object_info, simgrid::mc::Region* r1,
- simgrid::mc::Region* r2, const simgrid::mc::Snapshot& snapshot1,
- const simgrid::mc::Snapshot& snapshot2)
-{
- xbt_assert(r1 && r2, "Missing region.");
-
- const std::vector<simgrid::mc::Variable>& variables = object_info->global_variables;
-
- for (simgrid::mc::Variable const& current_var : variables) {
- // If the variable is not in this object, skip it:
- // We do not expect to find a pointer to something which is not reachable
- // by the global variables.
- if ((char*)current_var.address < object_info->start_rw || (char*)current_var.address > object_info->end_rw)
- continue;
-
- const simgrid::mc::Type* bvariable_type = current_var.type;
- if (areas_differ_with_type(process, state, current_var.address, snapshot1, r1, current_var.address, snapshot2, r2,
- bvariable_type, 0)) {
- XBT_VERB("Global variable %s (%p) is different between snapshots", current_var.name.c_str(), current_var.address);
- return true;
- }
- }
-
- return false;
-}
-
-static bool local_variables_differ(const simgrid::mc::RemoteProcessMemory& process, simgrid::mc::StateComparator& state,
- const simgrid::mc::Snapshot& snapshot1, const simgrid::mc::Snapshot& snapshot2,
- const_mc_snapshot_stack_t stack1, const_mc_snapshot_stack_t stack2)
-{
- if (stack1->local_variables.size() != stack2->local_variables.size()) {
- XBT_VERB("Different number of local variables");
- return true;
- }
-
- for (unsigned int cursor = 0; cursor < stack1->local_variables.size(); cursor++) {
- const_local_variable_t current_var1 = &stack1->local_variables[cursor];
- const_local_variable_t current_var2 = &stack2->local_variables[cursor];
- if (current_var1->name != current_var2->name || current_var1->subprogram != current_var2->subprogram ||
- current_var1->ip != current_var2->ip) {
- // TODO, fix current_varX->subprogram->name to include name if DW_TAG_inlined_subprogram
- XBT_VERB("Different name of variable (%s - %s) or frame (%s - %s) or ip (%lu - %lu)", current_var1->name.c_str(),
- current_var2->name.c_str(), current_var1->subprogram->name.c_str(),
- current_var2->subprogram->name.c_str(), current_var1->ip, current_var2->ip);
- return true;
- }
-
- if (areas_differ_with_type(process, state, current_var1->address, snapshot1,
- snapshot1.get_region(current_var1->address), current_var2->address, snapshot2,
- snapshot2.get_region(current_var2->address), current_var1->type, 0)) {
- XBT_VERB("Local variable %s (%p - %p) in frame %s is different between snapshots", current_var1->name.c_str(),
- current_var1->address, current_var2->address, current_var1->subprogram->name.c_str());
- return true;
- }
- }
- return false;
-}
-
-namespace simgrid::mc {
-bool Snapshot::equals_to(const Snapshot& other, RemoteProcessMemory& memory)
-{
- /* TODO: the memory parameter should be eventually removed. It seems to be there because each snapshot lacks some sort
- of metadata. That's OK for now (letting appart the fact that we cannot have a nice operator== because we need that
- extra parameter), but it will fall short when we want to have parallel explorations, with more than one
- RemoteProcess. At the very least, snapshots will need to know the remote process they are corresponding to, and more
- probably they will need to embeed all their metadata to let the remoteprocesses die before the end of the
- exploration. */
-
- /* TODO: This method should moved to Snapshot.cpp, but it needs the StateComparator that is declared locally to this
- * file only. */
-
- static StateComparator state_comparator; // TODO, make this a field of a persistant state object
-
- if (hash_ != other.hash_) {
- XBT_VERB("(%ld - %ld) Different hash: 0x%" PRIx64 "--0x%" PRIx64, this->num_state_, other.num_state_, this->hash_,
- other.hash_);
- return false;
- }
- XBT_VERB("(%ld - %ld) Same hash: 0x%" PRIx64, this->num_state_, other.num_state_, this->hash_);
-
- /* TODO: re-enable the quick filter of counting enabled processes in each snapshots */
-
- /* Compare size of stacks */
- for (unsigned long i = 0; i < this->stacks_.size(); i++) {
- size_t size_used1 = this->stack_sizes_[i];
- size_t size_used2 = other.stack_sizes_[i];
- if (size_used1 != size_used2) {
- XBT_VERB("(%ld - %ld) Different size used in stacks: %zu - %zu", num_state_, other.num_state_, size_used1,
- size_used2);
- return false;
- }
- }
-
- /* Init heap information used in heap comparison algorithm */
- const s_xbt_mheap_t* heap1 = static_cast<xbt_mheap_t>(
- this->read_bytes(alloca(sizeof(s_xbt_mheap_t)), sizeof(s_xbt_mheap_t), memory.heap_address, ReadOptions::lazy()));
- const s_xbt_mheap_t* heap2 = static_cast<xbt_mheap_t>(
- other.read_bytes(alloca(sizeof(s_xbt_mheap_t)), sizeof(s_xbt_mheap_t), memory.heap_address, ReadOptions::lazy()));
- if (state_comparator.initHeapInformation(memory, heap1, heap2, this->to_ignore_, other.to_ignore_) == -1) {
- XBT_VERB("(%ld - %ld) Different heap information", this->num_state_, other.num_state_);
- return false;
- }
-
- /* Stacks comparison */
- for (unsigned int cursor = 0; cursor < this->stacks_.size(); cursor++) {
- const_mc_snapshot_stack_t stack1 = &this->stacks_[cursor];
- const_mc_snapshot_stack_t stack2 = &other.stacks_[cursor];
-
- if (local_variables_differ(memory, state_comparator, *this, other, stack1, stack2)) {
- XBT_VERB("(%ld - %ld) Different local variables between stacks %u", this->num_state_, other.num_state_,
- cursor + 1);
- return false;
- }
- }
-
- size_t regions_count = this->snapshot_regions_.size();
- if (regions_count != other.snapshot_regions_.size())
- return false;
-
- for (size_t k = 0; k != regions_count; ++k) {
- Region* region1 = this->snapshot_regions_[k].get();
- Region* region2 = other.snapshot_regions_[k].get();
-
- // Preconditions:
- if (region1->region_type() != RegionType::Data)
- continue;
-
- xbt_assert(region1->region_type() == region2->region_type());
- xbt_assert(region1->object_info() == region2->object_info());
- xbt_assert(region1->object_info());
-
- /* Compare global variables */
- if (global_variables_differ(memory, state_comparator, region1->object_info(), region1, region2, *this, other)) {
- std::string const& name = region1->object_info()->file_name;
- XBT_VERB("(%ld - %ld) Different global variables in %s", this->num_state_, other.num_state_, name.c_str());
- return false;
- }
- }
-
- XBT_VERB(" Compare heap...");
- /* Compare heap */
- if (mmalloc_heap_differ(memory, state_comparator, *this, other)) {
- XBT_VERB("(%ld - %ld) Different heap (mmalloc_heap_differ)", this->num_state_, other.num_state_);
- return false;
- }
-
- XBT_VERB("(%ld - %ld) No difference found", this->num_state_, other.num_state_);
-
- return true;
-}
-} // namespace simgrid::mc
#include "xbt/string.hpp"
#include <cstdint>
+#include <inttypes.h>
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_comm_determinism, mc, "Logging specific to MC communication determinism detection");
XBT_DEBUG("********* Start communication determinism verification *********");
- auto* base = new DFSExplorer(args, mode, true);
+ auto* base = new DFSExplorer(args, mode);
auto* extension = new CommDetExtension(*base);
DFSExplorer::on_exploration_start([extension](RemoteApp const&) {
#include "src/mc/mc_record.hpp"
#include "src/mc/transition/Transition.hpp"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/VisitedState.hpp"
-#endif
-
-#include "src/xbt/mmalloc/mmprivate.h"
#include "xbt/log.h"
#include "xbt/string.hpp"
#include "xbt/sysdep.h"
xbt::signal<void(RemoteApp&)> DFSExplorer::on_log_state_signal;
-void DFSExplorer::check_non_termination(const State* current_state)
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- for (auto const& state : stack_) {
- if (state->get_system_state()->equals_to(*current_state->get_system_state(),
- *get_remote_app().get_remote_process_memory())) {
- XBT_INFO("Non-progressive cycle: state %ld -> state %ld", state->get_num(), current_state->get_num());
- XBT_INFO("******************************************");
- XBT_INFO("*** NON-PROGRESSIVE CYCLE DETECTED ***");
- XBT_INFO("******************************************");
- XBT_INFO("Counter-example execution trace:");
- for (auto const& s : get_textual_trace())
- XBT_INFO(" %s", s.c_str());
- XBT_INFO("You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with "
- "--cfg=model-check/replay:'%s'",
- get_record_trace().to_string().c_str());
- log_state();
-
- throw McError(ExitStatus::NON_TERMINATION);
- }
- }
-#endif
-}
-
RecordTrace DFSExplorer::get_record_trace() // override
{
RecordTrace res;
continue;
}
-#if SIMGRID_HAVE_STATEFUL_MC
- // Backtrack if we are revisiting a state we saw previously while applying state-equality reduction
- if (visited_state_ != nullptr) {
- XBT_DEBUG("State already visited (equal to state %ld), exploration stopped on this path.",
- visited_state_->original_num_ == -1 ? visited_state_->num_ : visited_state_->original_num_);
-
- visited_state_ = nullptr;
- this->backtrack();
- continue;
- }
-#endif
-
if (reduction_mode_ == ReductionMode::odpor) {
// In the case of ODPOR, the wakeup tree for this
// state may be empty if we're exploring new territory
if (stack_.back()->count_todo_multiples() > 0)
opened_states_.emplace_back(stack_.back());
- if (_sg_mc_termination)
- this->check_non_termination(next_state.get());
-
-#if SIMGRID_HAVE_STATEFUL_MC
- /* Check whether we already explored next_state in the past (but only if interested in state-equality reduction)
- */
- if (_sg_mc_max_visited_states > 0)
- visited_state_ = visited_states_.addVisitedState(next_state->get_num(), next_state.get(), get_remote_app());
-#endif
-
stack_.emplace_back(std::move(next_state));
/* If this is a new state (or if we don't care about state-equality reduction) */
- if (visited_state_ == nullptr) {
- /* Get an enabled process and insert it in the interleave set of the next state */
- if (reduction_mode_ == ReductionMode::dpor)
- stack_.back()->consider_best(); // Take only one transition if DPOR: others may be considered later if required
- else {
- stack_.back()->consider_all();
- }
-
- dot_output("\"%ld\" -> \"%ld\" [%s];\n", state->get_num(), stack_.back()->get_num(),
- state->get_transition_out()->dot_string().c_str());
-#if SIMGRID_HAVE_STATEFUL_MC
- } else {
- dot_output("\"%ld\" -> \"%ld\" [%s];\n", state->get_num(),
- visited_state_->original_num_ == -1 ? visited_state_->num_ : visited_state_->original_num_,
- state->get_transition_out()->dot_string().c_str());
-#endif
+ /* Get an enabled process and insert it in the interleave set of the next state */
+ if (reduction_mode_ == ReductionMode::dpor)
+ stack_.back()->consider_best(); // Take only one transition if DPOR: others may be considered later if required
+ else {
+ stack_.back()->consider_all();
}
+
+ dot_output("\"%ld\" -> \"%ld\" [%s];\n", state->get_num(), stack_.back()->get_num(),
+ state->get_transition_out()->dot_string().c_str());
}
log_state();
}
backtrack_count_++;
XBT_DEBUG("Backtracking to state#%ld", backtracking_point->get_num());
-#if SIMGRID_HAVE_STATEFUL_MC
- /* If asked to rollback on a state that has a snapshot, restore it */
- if (const auto* system_state = backtracking_point->get_system_state()) {
- system_state->restore(*get_remote_app().get_remote_process_memory());
- on_restore_system_state_signal(backtracking_point.get(), get_remote_app());
- this->restore_stack(backtracking_point);
- return;
- }
-#endif
-
// Search how to restore the backtracking point
- State* init_state = nullptr;
std::deque<Transition*> replay_recipe;
for (auto* s = backtracking_point.get(); s != nullptr; s = s->get_parent_state().get()) {
-#if SIMGRID_HAVE_STATEFUL_MC
- if (s->get_system_state() != nullptr) { // Found a state that I can restore
- init_state = s;
- break;
- }
-#endif
if (s->get_transition_in() != nullptr) // The root has no transition_in
replay_recipe.push_front(s->get_transition_in().get());
}
- // Restore the init_state, if any
- if (init_state != nullptr) {
-#if SIMGRID_HAVE_STATEFUL_MC
- const auto* system_state = init_state->get_system_state();
- system_state->restore(*get_remote_app().get_remote_process_memory());
- on_restore_system_state_signal(init_state, get_remote_app());
-#endif
- } else { // Restore the initial state if no intermediate state was found
- get_remote_app().restore_initial_state();
- on_restore_initial_state_signal(get_remote_app());
- }
+ // Restore the initial state if no intermediate state was found
+ get_remote_app().restore_initial_state();
+ on_restore_initial_state_signal(get_remote_app());
/* if no snapshot, we need to restore the initial state and replay the transitions */
/* Traverse the stack from the state at position start and re-execute the transitions */
this->restore_stack(backtracking_point);
}
-DFSExplorer::DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info)
- : Exploration(args, need_memory_info || _sg_mc_termination
-#if SIMGRID_HAVE_STATEFUL_MC
- || _sg_mc_checkpoint > 0
-#endif
- )
- , reduction_mode_(mode)
+DFSExplorer::DFSExplorer(const std::vector<char*>& args, ReductionMode mode) : Exploration(args), reduction_mode_(mode)
{
- if (_sg_mc_termination) {
- if (mode != ReductionMode::none) {
- XBT_INFO("Check non progressive cycles (turning DPOR off)");
- reduction_mode_ = ReductionMode::none;
- } else {
- XBT_INFO("Check non progressive cycles");
- }
- } else
- XBT_INFO("Start a DFS exploration. Reduction is: %s.", to_c_str(reduction_mode_));
+ XBT_INFO("Start a DFS exploration. Reduction is: %s.", to_c_str(reduction_mode_));
auto initial_state = std::make_shared<State>(get_remote_app());
#include "src/mc/explo/odpor/Execution.hpp"
#include "src/mc/mc_config.hpp"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/VisitedState.hpp"
-#endif
-
#include <deque>
#include <list>
#include <memory>
static xbt::signal<void(RemoteApp&)> on_log_state_signal;
public:
- explicit DFSExplorer(const std::vector<char*>& args, ReductionMode mode, bool need_memory_info = false);
+ explicit DFSExplorer(const std::vector<char*>& args, ReductionMode mode);
void run() override;
RecordTrace get_record_trace() override;
void log_state() override;
static void on_log_state(std::function<void(RemoteApp&)> const& f) { on_log_state_signal.connect(f); }
private:
- void check_non_termination(const State* current_state);
void backtrack();
/** Stack representing the position in the exploration graph */
/** Per-actor clock vectors used to compute the "happens-before" relation */
std::unordered_map<aid_t, ClockVector> per_actor_clocks_;
-#if SIMGRID_HAVE_STATEFUL_MC
- VisitedStates visited_states_;
- std::unique_ptr<VisitedState> visited_state_;
-#else
- void* visited_state_ = nullptr; /* The code uses it to detect whether we are doing stateful MC */
-#endif
-
/** Opened states are states that still contains todo actors.
* When backtracking, we pick a state from it*/
std::vector<std::shared_ptr<State>> opened_states_;
#include "src/mc/mc_private.hpp"
#include "xbt/string.hpp"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-#endif
-
#include <sys/wait.h>
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_explo, mc, "Generic exploration algorithm of the model-checker");
Exploration* Exploration::instance_ = nullptr; // singleton instance
-Exploration::Exploration(const std::vector<char*>& args, bool need_memory_introspection)
- : remote_app_(std::make_unique<RemoteApp>(args, need_memory_introspection))
+Exploration::Exploration(const std::vector<char*>& args) : remote_app_(std::make_unique<RemoteApp>(args))
{
xbt_assert(instance_ == nullptr, "Cannot have more than one exploration instance");
instance_ = this;
"--cfg=model-check/replay:'%s'",
get_record_trace().to_string().c_str());
log_state();
- if (xbt_log_no_loc) {
- XBT_INFO("Stack trace not displayed because you passed --log=no_loc");
- } else {
-#if SIMGRID_HAVE_STATEFUL_MC
- const auto* memory = get_remote_app().get_remote_process_memory();
- if (memory) {
- XBT_INFO("Stack trace:");
- memory->dump_stack();
- } else
-#endif
- XBT_INFO("Stack trace not shown because there is no memory introspection.");
- }
throw McError(ExitStatus::PROGRAM_CRASH);
}
FILE* dot_output_ = nullptr;
public:
- explicit Exploration(const std::vector<char*>& args, bool need_memory_introspection);
+ explicit Exploration(const std::vector<char*>& args);
virtual ~Exploration();
static Exploration* get_instance() { return instance_; }
};
// External constructors so that the types (and the types of their content) remain hidden
-XBT_PUBLIC Exploration* create_liveness_checker(const std::vector<char*>& args);
XBT_PUBLIC Exploration* create_dfs_exploration(const std::vector<char*>& args, ReductionMode mode);
XBT_PUBLIC Exploration* create_communication_determinism_checker(const std::vector<char*>& args, ReductionMode mode);
XBT_PUBLIC Exploration* create_udpor_checker(const std::vector<char*>& args);
+++ /dev/null
-/* Copyright (c) 2011-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/explo/LivenessChecker.hpp"
-#include "src/mc/api/RemoteApp.hpp"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_exit.hpp"
-#include "src/mc/mc_private.hpp"
-
-#include <boost/range/algorithm.hpp>
-#include <cstring>
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_liveness, mc, "Logging specific to algorithms for liveness properties verification");
-
-/********* Static functions *********/
-
-namespace simgrid::mc {
-
-VisitedPair::VisitedPair(int pair_num, xbt_automaton_state_t prop_state,
- std::shared_ptr<const std::vector<int>> atomic_propositions, std::shared_ptr<State> app_state,
- RemoteApp& remote_app)
- : num(pair_num), prop_state_(prop_state)
-{
- auto* memory = remote_app.get_remote_process_memory();
- this->app_state_ = std::move(app_state);
- if (not this->app_state_->get_system_state())
- this->app_state_->set_system_state(std::make_shared<Snapshot>(pair_num, remote_app.get_page_store(), *memory));
- this->heap_bytes_used = memory->get_remote_heap_bytes();
- this->actor_count_ = app_state_->get_actor_count();
- this->other_num = -1;
- this->atomic_propositions = std::move(atomic_propositions);
-}
-
-bool LivenessChecker::evaluate_label(const xbt_automaton_exp_label* l, std::vector<int> const& values)
-{
- switch (l->type) {
- case xbt_automaton_exp_label::AUT_OR:
- return evaluate_label(l->u.or_and.left_exp, values) || evaluate_label(l->u.or_and.right_exp, values);
- case xbt_automaton_exp_label::AUT_AND:
- return evaluate_label(l->u.or_and.left_exp, values) && evaluate_label(l->u.or_and.right_exp, values);
- case xbt_automaton_exp_label::AUT_NOT:
- return not evaluate_label(l->u.exp_not, values);
- case xbt_automaton_exp_label::AUT_PREDICAT:
- return values.at(compare_automaton_exp_label(l)) != 0;
- case xbt_automaton_exp_label::AUT_ONE:
- return true;
- default:
- xbt_die("Unexpected value for automaton");
- }
-}
-
-Pair::Pair(unsigned long expanded_pairs) : num(expanded_pairs) {}
-
-std::shared_ptr<const std::vector<int>> LivenessChecker::get_proposition_values() const
-{
- auto values = automaton_propositional_symbol_evaluate();
- return std::make_shared<const std::vector<int>>(std::move(values));
-}
-
-std::shared_ptr<VisitedPair> LivenessChecker::insert_acceptance_pair(simgrid::mc::Pair* pair)
-{
- auto new_pair = std::make_shared<VisitedPair>(pair->num, pair->prop_state_, pair->atomic_propositions,
- pair->app_state_, get_remote_app());
-
- auto [res_begin,
- res_end] = boost::range::equal_range(acceptance_pairs_, new_pair.get(), [](auto const& a, auto const& b) {
- return std::make_pair(a->actor_count_, a->heap_bytes_used) < std::make_pair(b->actor_count_, b->heap_bytes_used);
- });
-
- if (pair->search_cycle)
- for (auto i = res_begin; i != res_end; ++i) {
- std::shared_ptr<simgrid::mc::VisitedPair> const& pair_test = *i;
- if (xbt_automaton_state_compare(pair_test->prop_state_, new_pair->prop_state_) != 0 ||
- *(pair_test->atomic_propositions) != *(new_pair->atomic_propositions) ||
- (not pair_test->app_state_->get_system_state()->equals_to(*new_pair->app_state_->get_system_state(),
- *get_remote_app().get_remote_process_memory())))
- continue;
- XBT_INFO("Pair %d already reached (equal to pair %d) !", new_pair->num, pair_test->num);
- exploration_stack_.pop_back();
- dot_output("\"%d\" -> \"%d\" [%s];\n", this->previous_pair_, pair_test->num, this->previous_request_.c_str());
- return nullptr;
- }
-
- acceptance_pairs_.insert(res_begin, new_pair);
- return new_pair;
-}
-
-void LivenessChecker::remove_acceptance_pair(int pair_num)
-{
- for (auto i = acceptance_pairs_.begin(); i != acceptance_pairs_.end(); ++i)
- if ((*i)->num == pair_num) {
- acceptance_pairs_.erase(i);
- break;
- }
-}
-
-void LivenessChecker::replay()
-{
- XBT_DEBUG("**** Begin Replay ****");
-
- /* Intermediate backtracking */
- if (_sg_mc_checkpoint > 0) {
- const Pair* pair = exploration_stack_.back().get();
- if (const auto* system_state = pair->app_state_->get_system_state()) {
- system_state->restore(*get_remote_app().get_remote_process_memory());
- return;
- }
- }
-
- get_remote_app().restore_initial_state();
-
- /* Traverse the stack from the initial state and re-execute the transitions */
- int depth = 1;
- for (std::shared_ptr<Pair> const& pair : exploration_stack_) {
- if (pair == exploration_stack_.back())
- break;
-
- std::shared_ptr<State> state = pair->app_state_;
-
- if (pair->exploration_started) {
- state->get_transition_out()->replay(get_remote_app());
- XBT_DEBUG("Replay (depth = %d) : %s (%p)", depth, state->get_transition_out()->to_string().c_str(), state.get());
- }
-
- /* Update statistics */
- visited_pairs_count_++;
- depth++;
- }
- XBT_DEBUG("**** End Replay ****");
-}
-
-/**
- * @brief Checks whether a given pair has already been visited by the algorithm.
- */
-int LivenessChecker::insert_visited_pair(std::shared_ptr<VisitedPair> visited_pair, simgrid::mc::Pair* pair)
-{
- if (_sg_mc_max_visited_states == 0)
- return -1;
-
- if (visited_pair == nullptr)
- visited_pair = std::make_shared<VisitedPair>(pair->num, pair->prop_state_, pair->atomic_propositions,
- pair->app_state_, get_remote_app());
-
- auto [range_begin,
- range_end] = boost::range::equal_range(visited_pairs_, visited_pair.get(), [](auto const& a, auto const& b) {
- return std::make_pair(a->actor_count_, a->heap_bytes_used) < std::make_pair(b->actor_count_, b->heap_bytes_used);
- });
-
- for (auto i = range_begin; i != range_end; ++i) {
- const VisitedPair* pair_test = i->get();
- if (xbt_automaton_state_compare(pair_test->prop_state_, visited_pair->prop_state_) != 0 ||
- *(pair_test->atomic_propositions) != *(visited_pair->atomic_propositions) ||
- (not pair_test->app_state_->get_system_state()->equals_to(*visited_pair->app_state_->get_system_state(),
- *get_remote_app().get_remote_process_memory())))
- continue;
- if (pair_test->other_num == -1)
- visited_pair->other_num = pair_test->num;
- else
- visited_pair->other_num = pair_test->other_num;
- XBT_DEBUG("Pair %d already visited ! (equal to pair %d (pair %d in dot_output))", visited_pair->num, pair_test->num,
- visited_pair->other_num);
- (*i) = std::move(visited_pair);
- return (*i)->other_num;
- }
-
- visited_pairs_.insert(range_begin, std::move(visited_pair));
- this->purge_visited_pairs();
- return -1;
-}
-
-void LivenessChecker::purge_visited_pairs()
-{
- if (_sg_mc_max_visited_states != 0 && visited_pairs_.size() > (std::size_t)_sg_mc_max_visited_states) {
- // Remove the oldest entry with a linear search:
- visited_pairs_.erase(
- boost::min_element(visited_pairs_, [](std::shared_ptr<VisitedPair> const a,
- std::shared_ptr<VisitedPair> const& b) { return a->num < b->num; }));
- }
-}
-
-LivenessChecker::LivenessChecker(const std::vector<char*>& args) : Exploration(args, true) {}
-LivenessChecker::~LivenessChecker()
-{
- xbt_automaton_free(property_automaton_);
-}
-
-xbt_automaton_t LivenessChecker::property_automaton_ = nullptr;
-
-void LivenessChecker::automaton_load(const char* file) const
-{
- if (property_automaton_ == nullptr)
- property_automaton_ = xbt_automaton_new();
-
- xbt_automaton_load(property_automaton_, file);
-}
-
-std::vector<int> LivenessChecker::automaton_propositional_symbol_evaluate() const
-{
- unsigned int cursor = 0;
- std::vector<int> values;
- xbt_automaton_propositional_symbol_t ps = nullptr;
- xbt_dynar_foreach (property_automaton_->propositional_symbols, cursor, ps)
- values.push_back(xbt_automaton_propositional_symbol_evaluate(ps));
- return values;
-}
-
-std::vector<xbt_automaton_state_t> LivenessChecker::get_automaton_state() const
-{
- std::vector<xbt_automaton_state_t> automaton_stack;
- unsigned int cursor = 0;
- xbt_automaton_state_t automaton_state;
- xbt_dynar_foreach (property_automaton_->states, cursor, automaton_state)
- if (automaton_state->type == -1)
- automaton_stack.push_back(automaton_state);
- return automaton_stack;
-}
-
-int LivenessChecker::compare_automaton_exp_label(const xbt_automaton_exp_label* l) const
-{
- unsigned int cursor = 0;
- xbt_automaton_propositional_symbol_t p = nullptr;
- xbt_dynar_foreach (property_automaton_->propositional_symbols, cursor, p) {
- if (std::strcmp(xbt_automaton_propositional_symbol_get_name(p), l->u.predicat) == 0)
- return cursor;
- }
- return -1;
-}
-
-void LivenessChecker::set_property_automaton(xbt_automaton_state_t const& automaton_state) const
-{
- property_automaton_->current_state = automaton_state;
-}
-
-xbt_automaton_exp_label_t LivenessChecker::get_automaton_transition_label(xbt_dynar_t const& dynar, int index) const
-{
- const xbt_automaton_transition* transition = xbt_dynar_get_as(dynar, index, xbt_automaton_transition_t);
- return transition->label;
-}
-
-xbt_automaton_state_t LivenessChecker::get_automaton_transition_dst(xbt_dynar_t const& dynar, int index) const
-{
- const xbt_automaton_transition* transition = xbt_dynar_get_as(dynar, index, xbt_automaton_transition_t);
- return transition->dst;
-}
-void LivenessChecker::automaton_register_symbol(RemoteProcessMemory const& remote_process, const char* name,
- RemotePtr<int> address)
-{
- if (property_automaton_ == nullptr)
- property_automaton_ = xbt_automaton_new();
-
- xbt::add_proposition(property_automaton_, name,
- [&remote_process, address]() { return remote_process.read(address); });
-}
-
-RecordTrace LivenessChecker::get_record_trace() // override
-{
- RecordTrace res;
- for (std::shared_ptr<Pair> const& pair : exploration_stack_)
- if (pair->app_state_->get_transition_out() != nullptr)
- res.push_back(pair->app_state_->get_transition_out().get());
- return res;
-}
-
-void LivenessChecker::log_state() // override
-{
- XBT_INFO("Expanded pairs = %lu", expanded_pairs_count_);
- XBT_INFO("Visited pairs = %lu", visited_pairs_count_);
- XBT_INFO("Executed transitions = %lu", Transition::get_executed_transitions());
- Exploration::log_state();
-}
-
-void LivenessChecker::show_acceptance_cycle(std::size_t depth)
-{
- XBT_INFO("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
- XBT_INFO("| ACCEPTANCE CYCLE |");
- XBT_INFO("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
- XBT_INFO("Counter-example that violates formula:");
- for (auto const& s : this->get_textual_trace())
- XBT_INFO(" %s", s.c_str());
- XBT_INFO("You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with "
- "--cfg=model-check/replay:'%s'",
- get_record_trace().to_string().c_str());
- log_state();
- XBT_INFO("Counter-example depth: %zu", depth);
-}
-
-std::shared_ptr<Pair> LivenessChecker::create_pair(const Pair* current_pair, xbt_automaton_state_t state,
- std::shared_ptr<const std::vector<int>> propositions)
-{
- ++expanded_pairs_count_;
- auto next_pair = std::make_shared<Pair>(expanded_pairs_count_);
- next_pair->prop_state_ = state;
- next_pair->app_state_ = std::make_shared<State>(get_remote_app());
- next_pair->atomic_propositions = std::move(propositions);
- if (current_pair)
- next_pair->depth = current_pair->depth + 1;
- else
- next_pair->depth = 1;
- /* Add all enabled actors to the interleave set of the initial state */
- for (auto const& [aid, _] : next_pair->app_state_->get_actors_list())
- if (next_pair->app_state_->is_actor_enabled(aid))
- next_pair->app_state_->consider_one(aid);
-
- next_pair->requests = next_pair->app_state_->count_todo();
- /* FIXME : get search_cycle value for each accepting state */
- if (next_pair->prop_state_->type == 1 || (current_pair && current_pair->search_cycle))
- next_pair->search_cycle = true;
- else
- next_pair->search_cycle = false;
- return next_pair;
-}
-
-void LivenessChecker::backtrack()
-{
- /* Traverse the stack backwards until a pair with a non empty interleave
- set is found, deleting all the pairs that have it empty in the way. */
- while (not exploration_stack_.empty()) {
- std::shared_ptr<simgrid::mc::Pair> current_pair = exploration_stack_.back();
- exploration_stack_.pop_back();
- if (current_pair->requests > 0) {
- /* We found a backtracking point */
- XBT_DEBUG("Backtracking to depth %d", current_pair->depth);
- exploration_stack_.push_back(std::move(current_pair));
- this->replay();
- XBT_DEBUG("Backtracking done");
- break;
- } else {
- XBT_DEBUG("Delete pair %d at depth %d", current_pair->num, current_pair->depth);
- if (current_pair->prop_state_->type == 1)
- this->remove_acceptance_pair(current_pair->num);
- }
- }
-}
-
-void LivenessChecker::run()
-{
- XBT_INFO("Check the liveness property %s", _sg_mc_property_file.get().c_str());
- automaton_load(_sg_mc_property_file.get().c_str());
-
- XBT_DEBUG("Starting the liveness algorithm");
-
- /* Initialize */
- this->previous_pair_ = 0;
-
- std::shared_ptr<const std::vector<int>> propos = this->get_proposition_values();
-
- // For each initial state of the property automaton, push a
- // (application_state, automaton_state) pair to the exploration stack:
- auto automaton_stack = get_automaton_state();
- for (auto* automaton_state : automaton_stack) {
- if (automaton_state->type == -1)
- exploration_stack_.push_back(this->create_pair(nullptr, automaton_state, propos));
- }
-
- /* Actually run the double DFS search for counter-examples */
- while (not exploration_stack_.empty()) {
- std::shared_ptr<Pair> current_pair = exploration_stack_.back();
-
- /* Update current state in buchi automaton */
- set_property_automaton(current_pair->prop_state_);
-
- XBT_DEBUG(
- "********************* ( Depth = %d, search_cycle = %d, interleave size = %zu, pair_num = %d, requests = %d)",
- current_pair->depth, current_pair->search_cycle, current_pair->app_state_->count_todo(), current_pair->num,
- current_pair->requests);
-
- if (current_pair->requests == 0) {
- this->backtrack();
- continue;
- }
-
- std::shared_ptr<VisitedPair> reached_pair;
- if (current_pair->prop_state_->type == 1 && not current_pair->exploration_started) {
- reached_pair = this->insert_acceptance_pair(current_pair.get());
- if (reached_pair == nullptr) {
- this->show_acceptance_cycle(current_pair->depth);
- throw McError(ExitStatus::LIVENESS);
- }
- }
-
- /* Pair already visited ? stop the exploration on the current path */
- if (not current_pair->exploration_started) {
- int visited_num = this->insert_visited_pair(reached_pair, current_pair.get());
- if (visited_num != -1) {
- dot_output("\"%d\" -> \"%d\" [%s];\n", this->previous_pair_, visited_num, this->previous_request_.c_str());
-
- XBT_DEBUG("Pair already visited (equal to pair %d), exploration on the current path stopped.", visited_num);
- current_pair->requests = 0;
- this->backtrack();
- continue;
- }
- }
-
- current_pair->app_state_->execute_next(current_pair->app_state_->next_transition(), get_remote_app());
- XBT_DEBUG("Execute: %s", current_pair->app_state_->get_transition_out()->to_string().c_str());
-
- /* Update the dot output */
- if (this->previous_pair_ != 0 && this->previous_pair_ != current_pair->num) {
- dot_output("\"%d\" -> \"%d\" [%s];\n", this->previous_pair_, current_pair->num, this->previous_request_.c_str());
- this->previous_request_.clear();
- }
- this->previous_pair_ = current_pair->num;
- this->previous_request_ = current_pair->app_state_->get_transition_out()->dot_string();
- if (current_pair->search_cycle)
- dot_output("%d [shape=doublecircle];\n", current_pair->num);
-
- if (not current_pair->exploration_started)
- visited_pairs_count_++;
-
- current_pair->requests--;
- current_pair->exploration_started = true;
-
- /* Get values of atomic propositions (variables used in the property formula) */
- std::shared_ptr<const std::vector<int>> prop_values = this->get_proposition_values();
-
- // For each enabled transition in the property automaton, push a
- // (application_state, automaton_state) pair to the exploration stack:
- for (int i = xbt_dynar_length(current_pair->prop_state_->out) - 1; i >= 0; i--) {
- const auto* transition_succ_label = get_automaton_transition_label(current_pair->prop_state_->out, i);
- auto* transition_succ_dst = get_automaton_transition_dst(current_pair->prop_state_->out, i);
- if (evaluate_label(transition_succ_label, *prop_values))
- exploration_stack_.push_back(this->create_pair(current_pair.get(), transition_succ_dst, prop_values));
- }
- }
-
- XBT_INFO("No property violation found.");
- log_state();
-}
-
-Exploration* create_liveness_checker(const std::vector<char*>& args)
-{
- return new LivenessChecker(args);
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_LIVENESS_CHECKER_HPP
-#define SIMGRID_MC_LIVENESS_CHECKER_HPP
-
-#include "src/mc/api/State.hpp"
-#include "src/mc/explo/Exploration.hpp"
-#include "xbt/automaton.hpp"
-
-#include <list>
-#include <memory>
-#include <vector>
-
-namespace simgrid::mc {
-
-class XBT_PRIVATE Pair {
-public:
- int num = 0;
- bool search_cycle = false;
- std::shared_ptr<State> app_state_ = nullptr; /* State of the application (including system state) */
- xbt_automaton_state_t prop_state_ = nullptr; /* State of the property automaton */
- std::shared_ptr<const std::vector<int>> atomic_propositions;
- int requests = 0;
- int depth = 0;
- bool exploration_started = false;
-
- explicit Pair(unsigned long expanded_pairs);
-
- Pair(Pair const&) = delete;
- Pair& operator=(Pair const&) = delete;
-};
-
-class XBT_PRIVATE VisitedPair {
-public:
- int num;
- int other_num = 0; /* Dot output for */
- std::shared_ptr<State> app_state_ = nullptr; /* State of the application (including system state) */
- xbt_automaton_state_t prop_state_; /* State of the property automaton */
- std::shared_ptr<const std::vector<int>> atomic_propositions;
- std::size_t heap_bytes_used = 0;
- int actor_count_;
-
- VisitedPair(int pair_num, xbt_automaton_state_t prop_state,
- std::shared_ptr<const std::vector<int>> atomic_propositions, std::shared_ptr<State> app_state,
- RemoteApp& remote_app);
-};
-
-class XBT_PRIVATE LivenessChecker : public Exploration {
-public:
- explicit LivenessChecker(const std::vector<char*>& args);
- ~LivenessChecker() override;
-
- void run() override;
- RecordTrace get_record_trace() override;
- void log_state() override;
-
-private:
- std::shared_ptr<const std::vector<int>> get_proposition_values() const;
- std::shared_ptr<VisitedPair> insert_acceptance_pair(Pair* pair);
- int insert_visited_pair(std::shared_ptr<VisitedPair> visited_pair, Pair* pair);
- void show_acceptance_cycle(std::size_t depth);
- void replay();
- void remove_acceptance_pair(int pair_num);
- void purge_visited_pairs();
- void backtrack();
- std::shared_ptr<Pair> create_pair(const Pair* pair, xbt_automaton_state_t state,
- std::shared_ptr<const std::vector<int>> propositions);
-
- // A stack of (application_state, automaton_state) pairs for DFS exploration:
- std::list<std::shared_ptr<Pair>> exploration_stack_;
- std::list<std::shared_ptr<VisitedPair>> acceptance_pairs_;
- std::list<std::shared_ptr<VisitedPair>> visited_pairs_;
- unsigned long visited_pairs_count_ = 0;
- unsigned long expanded_pairs_count_ = 0;
- int previous_pair_ = 0;
- std::string previous_request_;
-
- /* The property automaton must be a static because it's sometimes used before the explorer is even created.
- *
- * This can happen if some symbols are created during the application's initialization process, before the first
- * decision point for the model-checker. Since the first snapshot is taken at the first decision point and since the
- * explorer is created after the first snapshot, this may result in some symbols being registered even before the
- * model-checker notices that this is a LivenessChecker to create.
- *
- * This situation is unfortunate, but I guess that it's the best I can achieve given the state of our initialization
- * code.
- */
- static xbt_automaton_t property_automaton_;
- bool evaluate_label(const xbt_automaton_exp_label* l, std::vector<int> const& values);
-
-public:
- void automaton_load(const char* file) const;
- std::vector<int> automaton_propositional_symbol_evaluate() const;
- std::vector<xbt_automaton_state_t> get_automaton_state() const;
- int compare_automaton_exp_label(const xbt_automaton_exp_label* l) const;
- void set_property_automaton(xbt_automaton_state_t const& automaton_state) const;
- xbt_automaton_exp_label_t get_automaton_transition_label(xbt_dynar_t const& dynar, int index) const;
- xbt_automaton_state_t get_automaton_transition_dst(xbt_dynar_t const& dynar, int index) const;
- static void automaton_register_symbol(RemoteProcessMemory const& remote_process, const char* name,
- RemotePtr<int> addr);
-};
-
-} // namespace simgrid::mc
-
-#endif
namespace simgrid::mc::udpor {
-UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args, true) {}
+UdporChecker::UdporChecker(const std::vector<char*>& args) : Exploration(args) {}
void UdporChecker::run()
{
std::unique_ptr<Exploration> explo;
-#if SIMGRID_HAVE_STATEFUL_MC
if (_sg_mc_comms_determinism || _sg_mc_send_determinism)
explo = std::unique_ptr<Exploration>(
create_communication_determinism_checker(argv_copy, get_model_checking_reduction()));
else if (_sg_mc_unfolding_checker)
explo = std::unique_ptr<Exploration>(create_udpor_checker(argv_copy));
- else if (not _sg_mc_property_file.get().empty())
- explo = std::unique_ptr<Exploration>(create_liveness_checker(argv_copy));
else
-#endif
explo = std::unique_ptr<Exploration>(create_dfs_exploration(argv_copy, get_model_checking_reduction()));
ExitStatus status;
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <cstddef>
-#include <cstdint>
-#include <unordered_set>
-
-#include "src/mc/AddressSpace.hpp"
-#include "src/mc/inspect/DwarfExpression.hpp"
-#include "src/mc/inspect/Frame.hpp"
-#include "src/mc/inspect/LocationList.hpp"
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/inspect/mc_dwarf.hpp"
-#include "src/mc/mc_private.hpp"
-
-namespace simgrid::dwarf {
-
-void execute(const Dwarf_Op* ops, std::size_t n, const ExpressionContext& context, ExpressionStack& stack)
-{
- for (size_t i = 0; i != n; ++i) {
- const Dwarf_Op* op = ops + i;
- std::uint8_t atom = op->atom;
- intptr_t first;
- intptr_t second;
-
- switch (atom) {
- // Push the CFA (Canonical Frame Address):
- case DW_OP_call_frame_cfa:
- /* See 6.4 of DWARF4 (http://dwarfstd.org/doc/DWARF4.pdf#page=140):
- *
- * > Typically, the CFA is defined to be the value of the stack
- * > pointer at the call site in the previous frame (which may be
- * > different from its value on entry to the current frame).
- *
- * We need to unwind the frame in order to get the SP of the parent
- * frame.
- *
- * Warning: the CFA returned by libunwind (UNW_X86_64_RSP, etc.)
- * is the SP of the *current* frame. */
- if (context.cursor) {
- // Get frame:
- unw_cursor_t cursor = *(context.cursor);
- unw_step(&cursor);
-
- unw_word_t res;
- unw_get_reg(&cursor, UNW_REG_SP, &res);
- stack.push(res);
- break;
- }
- throw evaluation_error("Missing cursor");
-
- // Frame base:
- case DW_OP_fbreg:
- stack.push((std::uintptr_t)context.frame_base + op->number);
- break;
-
- // Address from the base address of this ELF object.
- // Push the address on the stack (base_address + argument).
- case DW_OP_addr:
- if (context.object_info) {
- Dwarf_Off addr = (Dwarf_Off)(std::uintptr_t)context.object_info->base_address() + op->number;
- stack.push(addr);
- break;
- }
- throw evaluation_error("No base address");
-
- // ***** Stack manipulation:
-
- // Push another copy/duplicate the value at the top of the stack:
- case DW_OP_dup:
- stack.dup();
- break;
-
- // Pop/drop the top of the stack:
- case DW_OP_drop:
- (void)stack.pop();
- break;
-
- case DW_OP_swap:
- stack.swap();
- break;
-
- // Duplicate the value under the top of the stack:
- case DW_OP_over:
- stack.push(stack.top(1));
- break;
-
- // ***** Operations:
- // Those usually take the top of the stack and the next value as argument
- // and replace the top of the stack with the computed value
- // (stack.top() += stack.before_top()).
-
- case DW_OP_plus:
- first = stack.pop();
- second = stack.pop();
- stack.push(first + second);
- break;
-
- case DW_OP_mul:
- first = stack.pop();
- second = stack.pop();
- stack.push(first * second);
- break;
-
- case DW_OP_plus_uconst:
- stack.top() += op->number;
- break;
-
- case DW_OP_not:
- stack.top() = ~stack.top();
- break;
-
- case DW_OP_neg:
- stack.top() = -(intptr_t)stack.top();
- break;
-
- case DW_OP_minus:
- first = stack.pop();
- second = stack.pop();
- stack.push(second - first);
- break;
-
- case DW_OP_and:
- first = stack.pop();
- second = stack.pop();
- stack.push(first & second);
- break;
-
- case DW_OP_or:
- first = stack.pop();
- second = stack.pop();
- stack.push(first | second);
- break;
-
- case DW_OP_xor:
- first = stack.pop();
- second = stack.pop();
- stack.push(first ^ second);
- break;
-
- case DW_OP_nop:
- break;
-
- // ***** Deference (memory fetch)
-
- case DW_OP_deref_size:
- throw evaluation_error("Unsupported operation");
-
- case DW_OP_deref:
- // Computed address:
- if (not context.address_space)
- throw evaluation_error("Missing address space");
- context.address_space->read_bytes(&stack.top(), sizeof(uintptr_t), mc::remote(stack.top()));
- break;
-
- default:
-
- // Registers:
- if (static const std::unordered_set<uint8_t> registers =
- {DW_OP_breg0, DW_OP_breg1, DW_OP_breg2, DW_OP_breg3, DW_OP_breg4, DW_OP_breg5, DW_OP_breg6,
- DW_OP_breg7, DW_OP_breg8, DW_OP_breg9, DW_OP_breg10, DW_OP_breg11, DW_OP_breg12, DW_OP_breg13,
- DW_OP_breg14, DW_OP_breg15, DW_OP_breg16, DW_OP_breg17, DW_OP_breg18, DW_OP_breg19, DW_OP_breg20,
- DW_OP_breg21, DW_OP_breg22, DW_OP_breg23, DW_OP_breg24, DW_OP_breg25, DW_OP_breg26, DW_OP_breg27,
- DW_OP_breg28, DW_OP_breg29, DW_OP_breg30, DW_OP_breg31};
- registers.count(atom) > 0) {
- // Push register + constant:
- int register_id = dwarf_register_to_libunwind(op->atom - DW_OP_breg0);
- unw_word_t res;
- if (not context.cursor)
- throw evaluation_error("Missing stack context");
- unw_get_reg(context.cursor, register_id, &res);
- stack.push(res + op->number);
- break;
- }
-
- // ***** Constants:
-
- // Short constant literals:
- if (static const std::unordered_set<uint8_t> literals = {DW_OP_lit0, DW_OP_lit1, DW_OP_lit2, DW_OP_lit3,
- DW_OP_lit4, DW_OP_lit5, DW_OP_lit6, DW_OP_lit7,
- DW_OP_lit8, DW_OP_lit9, DW_OP_lit10, DW_OP_lit11,
- DW_OP_lit12, DW_OP_lit13, DW_OP_lit14, DW_OP_lit15,
- DW_OP_lit16, DW_OP_lit17, DW_OP_lit18, DW_OP_lit19,
- DW_OP_lit20, DW_OP_lit21, DW_OP_lit22, DW_OP_lit23,
- DW_OP_lit24, DW_OP_lit25, DW_OP_lit26, DW_OP_lit27,
- DW_OP_lit28, DW_OP_lit29, DW_OP_lit30, DW_OP_lit31};
- literals.count(atom) > 0) {
- // Push a literal/constant on the stack:
- stack.push(atom - DW_OP_lit0);
- break;
- }
-
- // General constants:
- if (static const std::unordered_set<uint8_t> constants = {DW_OP_const1u, DW_OP_const2u, DW_OP_const4u,
- DW_OP_const8u, DW_OP_const1s, DW_OP_const2s,
- DW_OP_const4s, DW_OP_const8s, DW_OP_constu,
- DW_OP_consts};
- constants.count(atom) > 0) {
- // Push the constant argument on the stack.
- stack.push(op->number);
- break;
- }
-
- // Not handled:
- throw evaluation_error("Unsupported operation");
- }
- }
-}
-
-} // namespace simgrid::dwarf
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_DWARF_EXPRESSION_HPP
-#define SIMGRID_MC_DWARF_EXPRESSION_HPP
-
-#include <cstdint>
-#include <cstdlib>
-
-#include <array>
-#include <stdexcept> // runtime_error
-#include <utility>
-#include <vector>
-
-#include <elfutils/libdw.h>
-#include <libunwind.h>
-
-#include "src/mc/inspect/mc_dwarf.hpp"
-#include "src/mc/mc_forward.hpp"
-
-/** @file DwarfExpression.hpp
- *
- * Evaluation of DWARF location expressions.
- */
-
-namespace simgrid::dwarf {
-
-/** A DWARF expression
- *
- * DWARF defines a simple stack-based VM for evaluating expressions
- * (such as locations of variables, etc.): a DWARF expression is
- * just a sequence of dwarf instructions. We currently directly use
- * `Dwarf_Op` from `dwarf.h` for dwarf instructions.
- */
-using DwarfExpression = std::vector<Dwarf_Op>;
-
-/** Context of evaluation of a DWARF expression
- *
- * Some DWARF instructions need to read the CPU registers,
- * the process memory, etc. All those information are gathered in
- * the evaluation context.
- */
-struct ExpressionContext {
- /** CPU state (registers) */
- unw_cursor_t* cursor = nullptr;
- void* frame_base = nullptr;
- const mc::AddressSpace* address_space = nullptr; /** Address space used to read memory */
- mc::ObjectInformation* object_info = nullptr;
-};
-
-/** When an error happens in the execution of a DWARF expression */
-class evaluation_error : public std::runtime_error {
-public:
- using std::runtime_error::runtime_error;
-};
-
-/** A stack for evaluating a DWARF expression
- *
- * DWARF expressions work by manipulating a stack of integer values.
- */
-class ExpressionStack {
-public:
- using value_type = std::uintptr_t;
- static constexpr std::size_t MAX_SIZE = 64;
-
-private:
- // Values of the stack (the top is stack_[size_ - 1]):
- std::array<uintptr_t, MAX_SIZE> stack_{{0}};
- size_t size_ = 0;
-
-public:
- // Access:
- std::size_t size() const { return size_; }
- bool empty() const { return size_ == 0; }
- void clear() { size_ = 0; }
- uintptr_t& operator[](int i) { return stack_[i]; }
- uintptr_t const& operator[](int i) const { return stack_[i]; }
-
- /** Top of the stack */
- value_type& top()
- {
- if (size_ == 0)
- throw evaluation_error("Empty stack");
- return stack_[size_ - 1];
- }
-
- /** Access the i-th element from the top of the stack */
- value_type& top(unsigned i)
- {
- if (size_ < i)
- throw evaluation_error("Invalid element");
- return stack_[size_ - 1 - i];
- }
-
- /** Push a value on the top of the stack */
- void push(value_type value)
- {
- if (size_ == stack_.size())
- throw evaluation_error("DWARF stack overflow");
- stack_[size_] = value;
- size_++;
- }
-
- /* Pop a value from the top of the stack */
- value_type pop()
- {
- if (size_ == 0)
- throw evaluation_error("DWARF stack underflow");
- --size_;
- return stack_[size_];
- }
-
- // These are DWARF operations (DW_OP_foo):
-
- /* Push a copy of the top-value (DW_OP_dup) */
- void dup() { push(top()); }
-
- /* Swap the two top-most values */
- void swap() { std::swap(top(), top(1)); }
-};
-
-/** Executes a DWARF expression
- *
- * @param ops DWARF expression instructions
- * @param n number of instructions
- * @param context evaluation context (registers, memory, etc.)
- * @param stack DWARf stack where the operations are executed
- */
-void execute(const Dwarf_Op* ops, std::size_t n, ExpressionContext const& context, ExpressionStack& stack);
-
-/** Executes/evaluates a DWARF expression
- *
- * @param expression DWARF expression to execute
- * @param context evaluation context (registers, memory, etc.)
- * @param stack DWARf stack where the operations are executed
- */
-inline void execute(simgrid::dwarf::DwarfExpression const& expression, ExpressionContext const& context,
- ExpressionStack& stack)
-{
- execute(expression.data(), expression.size(), context, stack);
-}
-
-} // namespace simgrid::dwarf
-
-#endif
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <libunwind.h>
-
-#include "xbt/sysdep.h"
-
-#include "src/mc/inspect/Frame.hpp"
-
-namespace simgrid::mc {
-
-void* Frame::frame_base(unw_cursor_t& unw_cursor) const
-{
- simgrid::dwarf::Location location =
- simgrid::dwarf::resolve(frame_base_location, object_info, &unw_cursor, nullptr, nullptr);
- if (location.in_memory())
- return location.address();
- else if (location.in_register()) {
- // This is a special case.
- // The register is not the location of the frame base
- // (a frame base cannot be located in a register).
- // Instead, DWARF defines this to mean that the register
- // contains the address of the frame base.
- unw_word_t word;
- unw_get_reg(&unw_cursor, location.register_id(), &word);
- return (void*)word;
- } else
- xbt_die("Unexpected location type");
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_FRAME_HPP
-#define SIMGRID_MC_FRAME_HPP
-
-#include <cstdint>
-#include <string>
-
-#include "xbt/base.h"
-#include "xbt/range.hpp"
-
-#include "src/mc/inspect/LocationList.hpp"
-#include "src/mc/inspect/Variable.hpp"
-#include "src/mc/mc_forward.hpp"
-
-namespace simgrid::mc {
-
-/** Debug information about a given function or scope within a function */
-class Frame {
-public:
- /** Kind of scope (DW_TAG_subprogram, DW_TAG_inlined_subroutine, etc.) */
- int tag = DW_TAG_invalid;
-
- /** Name of the function (if it is a function) */
- std::string name;
-
- /** Range of instruction addresses for which this scope is valid */
- simgrid::xbt::Range<std::uint64_t> range{0, 0};
-
- simgrid::dwarf::LocationList frame_base_location;
-
- /** List of the variables (sorted by name) */
- std::vector<Variable> variables;
-
- /* Unique identifier for this scope (in the object_info)
- *
- * This is the global DWARF offset of the DIE. */
- unsigned long int id = 0;
-
- std::vector<Frame> scopes;
-
- /** Value of `DW_AT_abstract_origin`
- *
- * For inlined subprograms, this is the ID of the
- * parent function.
- */
- unsigned long int abstract_origin_id = 0;
-
- simgrid::mc::ObjectInformation* object_info = nullptr;
-
- void* frame_base(unw_cursor_t& unw_cursor) const;
- void remove_variable(char* name);
-};
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2004-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/inspect/LocationList.hpp"
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/inspect/mc_dwarf.hpp"
-
-#include "xbt/asserts.h"
-#include "xbt/log.h"
-#include "xbt/sysdep.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <libunwind.h>
-#include <utility>
-
-XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(mc_dwarf);
-
-namespace simgrid::dwarf {
-
-/** Resolve a location expression */
-Location resolve(simgrid::dwarf::DwarfExpression const& expression, simgrid::mc::ObjectInformation* object_info,
- unw_cursor_t* c, void* frame_pointer_address, const simgrid::mc::AddressSpace* address_space)
-{
- simgrid::dwarf::ExpressionContext context;
- context.frame_base = frame_pointer_address;
- context.cursor = c;
- context.address_space = address_space;
- context.object_info = object_info;
-
- if (not expression.empty() && expression[0].atom >= DW_OP_reg0 && expression[0].atom <= DW_OP_reg31) {
- int dwarf_register = expression[0].atom - DW_OP_reg0;
- xbt_assert(c, "Missing frame context for register operation DW_OP_reg%i", dwarf_register);
- return Location(dwarf_register_to_libunwind(dwarf_register));
- }
-
- simgrid::dwarf::ExpressionStack stack;
- simgrid::dwarf::execute(expression, context, stack);
- return Location((void*)stack.top());
-}
-
-// TODO, move this in a method of LocationList
-static simgrid::dwarf::DwarfExpression const* find_expression(simgrid::dwarf::LocationList const& locations,
- unw_word_t ip)
-{
- for (simgrid::dwarf::LocationListEntry const& entry : locations)
- if (entry.valid_for_ip(ip))
- return &entry.expression();
- return nullptr;
-}
-
-Location resolve(simgrid::dwarf::LocationList const& locations, simgrid::mc::ObjectInformation* object_info,
- unw_cursor_t* c, void* frame_pointer_address, const simgrid::mc::AddressSpace* address_space)
-{
- unw_word_t ip = 0;
- if (c)
- xbt_assert(unw_get_reg(c, UNW_REG_IP, &ip) == 0, "Could not resolve IP");
- simgrid::dwarf::DwarfExpression const* expression = find_expression(locations, ip);
- xbt_assert(expression != nullptr, "Could not resolve location");
- return simgrid::dwarf::resolve(*expression, object_info, c, frame_pointer_address, address_space);
-}
-
-LocationList location_list(const simgrid::mc::ObjectInformation& info, Dwarf_Attribute& attr)
-{
- LocationList locations;
- std::ptrdiff_t offset = 0;
- while (true) {
- Dwarf_Addr base;
- Dwarf_Addr start;
- Dwarf_Addr end;
- Dwarf_Op* ops;
- std::size_t len;
-
- offset = dwarf_getlocations(&attr, offset, &base, &start, &end, &ops, &len);
-
- if (offset == -1)
- XBT_WARN("Error while loading location list: %s", dwarf_errmsg(-1));
- if (offset <= 0)
- break;
-
- auto base_address = reinterpret_cast<std::uint64_t>(info.base_address());
-
- LocationListEntry::range_type range;
- if (start == 0)
- // If start == 0, this is not a location list:
- range = {0, UINT64_MAX};
- else
- range = {base_address + start, base_address + end};
-
- locations.emplace_back(DwarfExpression(ops, ops + len), range);
- }
-
- return locations;
-}
-} // namespace simgrid::dwarf
+++ /dev/null
-/* Copyright (c) 2004-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_OBJECT_LOCATION_H
-#define SIMGRID_MC_OBJECT_LOCATION_H
-
-#include "xbt/base.h"
-#include "xbt/range.hpp"
-
-#include "src/mc/inspect/DwarfExpression.hpp"
-#include "src/mc/mc_base.hpp"
-#include "src/mc/mc_forward.hpp"
-
-#include <cstdint>
-#include <vector>
-
-namespace simgrid::dwarf {
-
-/** A DWARF expression with optional validity constraints */
-class LocationListEntry {
-public:
- using range_type = simgrid::xbt::Range<std::uint64_t>;
-
-private:
- DwarfExpression expression_;
- // By default, the expression is always valid:
- range_type range_ = {0, UINT64_MAX};
-
-public:
- LocationListEntry() = default;
- LocationListEntry(DwarfExpression expression, range_type range) : expression_(std::move(expression)), range_(range) {}
- explicit LocationListEntry(DwarfExpression expression) : expression_(std::move(expression)) {}
-
- DwarfExpression& expression() { return expression_; }
- DwarfExpression const& expression() const { return expression_; }
- bool valid_for_ip(unw_word_t ip) const { return range_.contain(ip); }
-};
-
-using LocationList = std::vector<LocationListEntry>;
-
-/** Location of some variable in memory
- *
- * The variable is located either in memory of a register.
- */
-class Location {
-private:
- void* memory_ = nullptr;
- int register_id_ = 0;
-
-public:
- explicit Location(void* x) : memory_(x) {}
- explicit Location(int register_id) : register_id_(register_id) {}
- // Type of location:
- bool in_register() const { return memory_ == nullptr; }
- bool in_memory() const { return memory_ != nullptr; }
-
- // Get the location:
- void* address() const { return memory_; }
- int register_id() const { return register_id_; }
-};
-
-XBT_PRIVATE
-Location resolve(simgrid::dwarf::DwarfExpression const& expression, simgrid::mc::ObjectInformation* object_info,
- unw_cursor_t* c, void* frame_pointer_address, const simgrid::mc::AddressSpace* address_space);
-
-Location resolve(simgrid::dwarf::LocationList const& locations, simgrid::mc::ObjectInformation* object_info,
- unw_cursor_t* c, void* frame_pointer_address, const simgrid::mc::AddressSpace* address_space);
-
-XBT_PRIVATE
-simgrid::dwarf::LocationList location_list(const simgrid::mc::ObjectInformation& info, Dwarf_Attribute& attr);
-
-} // namespace simgrid::dwarf
-
-#endif
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <algorithm>
-#include <cstdint>
-#include <sys/mman.h> // PROT_READ and friends
-#include <vector>
-
-#include "src/mc/inspect/Frame.hpp"
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/inspect/Variable.hpp"
-#include "src/mc/mc_private.hpp"
-#include "xbt/file.hpp"
-
-namespace simgrid::mc {
-
-/* For an executable object, addresses are virtual address (there is no offset) i.e.
- * \f$\text{virtual address} = \{dwarf address}\f$
- *
- * For a shared object, the addresses are offset from the beginning of the shared object (the base address of the
- * mapped shared object must be used as offset
- * i.e. \f$\text{virtual address} = \text{shared object base address}
- * + \text{dwarf address}\f$.
- */
-void* ObjectInformation::base_address() const
-{
- // For an executable (more precisely for an ET_EXEC) the base it 0:
- if (this->executable())
- return nullptr;
-
- // For an a shared-object (ET_DYN, including position-independent executables) the base address is its lowest address:
- void* result = this->start_exec;
- if (this->start_rw != nullptr && result > (void*)this->start_rw)
- result = this->start_rw;
- if (this->start_ro != nullptr && result > (void*)this->start_ro)
- result = this->start_ro;
- return result;
-}
-
-Frame* ObjectInformation::find_function(const void* ip)
-{
- ensure_dwarf_loaded();
-
- /* This is implemented by binary search on a sorted array.
- *
- * We do quite a lot of those so we want this to be cache efficient.
- * We pack the only information we need in the index entries in order
- * to successfully do the binary search. We do not need the high_pc
- * during the binary search (only at the end) so it is not included
- * in the index entry. We could use parallel arrays as well.
- *
- * Note the usage of reverse iterators to match the correct interval.
- */
- auto pos = std::lower_bound(this->functions_index.rbegin(), this->functions_index.rend(), ip,
- [](auto const& func, auto const* addr) { return func.low_pc > addr; });
-
- /* At this point, the search is over.
- * Either we have found the correct function or we do not know
- * any function corresponding to this instruction address.
- * Only at the point do we dereference the function pointer. */
- return (pos != this->functions_index.rend() && reinterpret_cast<std::uint64_t>(ip) < pos->function->range.end())
- ? pos->function
- : nullptr;
-}
-
-const Variable* ObjectInformation::find_variable(const char* var_name)
-{
- ensure_dwarf_loaded();
-
- auto pos = std::lower_bound(this->global_variables.begin(), this->global_variables.end(), var_name,
- [](auto const& var, const char* name) { return var.name < name; });
- return (pos != this->global_variables.end() && pos->name == var_name) ? &(*pos) : nullptr;
-}
-
-void ObjectInformation::remove_global_variable(const char* var_name)
-{
- // Binary search:
- auto pos1 = std::lower_bound(this->global_variables.begin(), this->global_variables.end(), var_name,
- [](auto const& var, const char* name) { return var.name < name; });
- // Find the whole range:
- auto pos2 = std::upper_bound(pos1, this->global_variables.end(), var_name,
- [](const char* name, auto const& var) { return name < var.name; });
- // Remove the whole range:
- this->global_variables.erase(pos1, pos2);
-}
-
-/** Ignore a local variable in a scope
- *
- * Ignore all instances of variables with a given name in any (possibly inlined) subprogram with a given namespaced
- * name.
- *
- * @param var_name Name of the local variable to ignore
- * @param subprogram_name Name of the subprogram to ignore (nullptr for any)
- * @param subprogram (possibly inlined) Subprogram of the scope current scope
- * @param scope Current scope
- */
-static void remove_local_variable(Frame& scope, const char* var_name, const char* subprogram_name,
- Frame const& subprogram)
-{
- // If the current subprogram matches the given name:
- if (subprogram_name == nullptr || (not subprogram.name.empty() && subprogram.name == subprogram_name)) {
- // Try to find the variable and remove it:
-
- // Binary search:
- auto pos = std::lower_bound(scope.variables.begin(), scope.variables.end(), var_name,
- [](auto const& var, const char* name) { return var.name < name; });
- if (pos != scope.variables.end() && pos->name == var_name) {
- // Variable found, remove it:
- scope.variables.erase(pos);
- }
- }
-
- // And recursive processing in nested scopes:
- for (Frame& nested_scope : scope.scopes) {
- // The new scope may be an inlined subroutine, in this case we want to use its
- // namespaced name in recursive calls:
- Frame const& nested_subprogram = nested_scope.tag == DW_TAG_inlined_subroutine ? nested_scope : subprogram;
- remove_local_variable(nested_scope, var_name, subprogram_name, nested_subprogram);
- }
-}
-
-void ObjectInformation::remove_local_variable(const char* var_name, const char* subprogram_name)
-{
- for (auto& [_, entry] : this->subprograms)
- mc::remove_local_variable(entry, var_name, subprogram_name, entry);
-}
-
-/** @brief Fills the position of the segments (executable, read-only, read/write) */
-// TODO, use the ELF segment information for more robustness
-void find_object_address(std::vector<xbt::VmMap> const& maps, ObjectInformation* result)
-{
- const int PROT_RW = PROT_READ | PROT_WRITE;
- const int PROT_RX = PROT_READ | PROT_EXEC;
-
- std::string name = xbt::Path(result->file_name).get_base_name();
-
- for (size_t i = 0; i < maps.size(); ++i) {
- simgrid::xbt::VmMap const& reg = maps[i];
- if (reg.pathname.empty() || name != simgrid::xbt::Path(reg.pathname).get_base_name())
- continue;
-
- // This is the non-GNU_RELRO-part of the data segment:
- if (reg.prot == PROT_RW) {
- xbt_assert(not result->start_rw, "Multiple read-write segments for %s, not supported", maps[i].pathname.c_str());
- result->start_rw = (char*)reg.start_addr;
- result->end_rw = (char*)reg.end_addr;
-
- // The next VMA might be end of the data segment:
- if (i + 1 < maps.size() && maps[i + 1].pathname.empty() && maps[i + 1].prot == PROT_RW &&
- maps[i + 1].start_addr == reg.end_addr)
- result->end_rw = (char*)maps[i + 1].end_addr;
- }
-
- // This is the text segment:
- else if (reg.prot == PROT_RX) {
- xbt_assert(not result->start_exec, "Multiple executable segments for %s, not supported",
- maps[i].pathname.c_str());
- result->start_exec = (char*)reg.start_addr;
- result->end_exec = (char*)reg.end_addr;
-
- // The next VMA might be end of the data segment:
- if (i + 1 < maps.size() && maps[i + 1].pathname.empty() && maps[i + 1].prot == PROT_RW &&
- maps[i + 1].start_addr == reg.end_addr) {
- result->start_rw = (char*)maps[i + 1].start_addr;
- result->end_rw = (char*)maps[i + 1].end_addr;
- }
- }
-
- // This is the GNU_RELRO-part of the data segment:
- else if (reg.prot == PROT_READ) {
- xbt_assert(not result->start_ro,
- "Multiple read-only segments for %s, not supported. Compiling with the following may help: "
- "-g -Wl,-znorelro -Wl,-znoseparate-code",
- maps[i].pathname.c_str());
- result->start_ro = (char*)reg.start_addr;
- result->end_ro = (char*)reg.end_addr;
- }
- }
-
- result->start = result->start_rw;
- if ((const void*)result->start_ro < result->start)
- result->start = result->start_ro;
- if ((const void*)result->start_exec < result->start)
- result->start = result->start_exec;
-
- result->end = result->end_rw;
- if (result->end_ro && (const void*)result->end_ro > result->end)
- result->end = result->end_ro;
- if (result->end_exec && (const void*)result->end_exec > result->end)
- result->end = result->end_exec;
-
- xbt_assert(result->start_exec || result->start_rw || result->start_ro);
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_OBJECT_INFORMATION_HPP
-#define SIMGRID_MC_OBJECT_INFORMATION_HPP
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "src/mc/inspect/Frame.hpp"
-#include "src/mc/inspect/Type.hpp"
-#include "src/mc/mc_forward.hpp"
-#include "src/xbt/memory_map.hpp"
-
-#include "src/smpi/include/private.hpp"
-
-namespace simgrid::mc {
-
-/** An entry in the functions index
- *
- * See the code of ObjectInformation::find_function.
- */
-struct FunctionIndexEntry {
- void* low_pc;
- simgrid::mc::Frame* function;
-};
-
-/** Information about an ELF module (executable or shared object)
- *
- * This contains all the information we need about an executable or
- * shared-object in the model-checked process:
- *
- * - where it is located in the virtual address space;
- *
- * - where are located its different memory mappings in the the
- * virtual address space;
- *
- * - all the debugging (DWARF) information
- * - types,
- * - location of the functions and their local variables,
- * - global variables,
- *
- * - etc.
- */
-class ObjectInformation {
- bool dwarf_loaded = false; // Lazily loads the dwarf info
-
-public:
- void ensure_dwarf_loaded(); // Used by functions that need the dwarf
- ObjectInformation() = default;
-
- // Not copiable:
- ObjectInformation(ObjectInformation const&) = delete;
- ObjectInformation& operator=(ObjectInformation const&) = delete;
-
- // Flag:
- static const int Executable = 1;
-
- /** Bitfield of flags */
- int flags = 0;
- std::string file_name;
- const void* start = nullptr;
- const void* end = nullptr;
- // Location of its text segment:
- char* start_exec = nullptr;
- char* end_exec = nullptr;
- // Location of the read-only part of its data segment:
- char* start_rw = nullptr;
- char* end_rw = nullptr;
- // Location of the read/write part of its data segment:
- char* start_ro = nullptr;
- char* end_ro = nullptr;
-
- /** All of its subprograms indexed by their address */
- std::unordered_map<std::uint64_t, simgrid::mc::Frame> subprograms;
-
- /** Index of functions by instruction address
- *
- * We need to efficiently find the function from any given instruction
- * address inside its range. This index is sorted by low_pc
- *
- * The entries are sorted by low_pc and a binary search can be used to look
- * them up. In order to have a better cache locality, we only keep the
- * information we need for the lookup in this vector. We could probably
- * replace subprograms by an ordered vector of Frame and replace this one b
- * a parallel `std::vector<void*>`.
- */
- std::vector<FunctionIndexEntry> functions_index;
-
- std::vector<simgrid::mc::Variable> global_variables;
-
- /** Types indexed by DWARF ID */
- std::unordered_map<std::uint64_t, simgrid::mc::Type> types;
-
- /** Types indexed by name
- *
- * Different compilation units have their separate type definitions
- * (for the same type). When we find an opaque type in one compilation unit,
- * we use this in order to try to find its definition in another compilation
- * unit.
- */
- std::unordered_map<std::string, simgrid::mc::Type*> full_types_by_name;
-
- /** Whether this module is an executable
- *
- * More precisely we check if this is an ET_EXE ELF. These ELF files
- * use fixed addresses instead of base-address relative addresses.
- * Position independent executables are in fact ET_DYN.
- */
- bool executable() const { return this->flags & simgrid::mc::ObjectInformation::Executable; }
-
- /** Base address of the module
- *
- * All the location information in ELF and DWARF are expressed as an offsets
- * from this base address:
- *
- * - location of the functions and global variables
- *
- * - the DWARF instruction `OP_addr` pushes this on the DWARF stack.
- **/
- void* base_address() const;
-
- /** Find a function by instruction address
- *
- * Loads the dwarf information on need.
- *
- * @param ip instruction address
- * @return corresponding function (if any) or nullptr
- */
- simgrid::mc::Frame* find_function(const void* ip);
-
- /** Find a global variable by name
- *
- * Loads the dwarf information on need.
- *
- * This is used to ignore global variables and to find well-known variables
- * (`__mmalloc_default_mdp`).
- *
- * @param name scopes name of the global variable (`myproject::Foo::count`)
- * @return corresponding variable (if any) or nullptr
- */
- const simgrid::mc::Variable* find_variable(const char* name);
-
- /** Remove a global variable (in order to ignore it)
- *
- * This is used to ignore a global variable for the snapshot comparison.
- */
- void remove_global_variable(const char* name);
-
- /** Remove a local variables (in order to ignore it)
- *
- * @param name Name of the local variable
- * @param scope scopes name name of the function (myproject::Foo::count) or null for all functions
- */
- void remove_local_variable(const char* name, const char* scope);
-};
-
-XBT_PRIVATE std::shared_ptr<ObjectInformation> createObjectInformation(std::vector<simgrid::xbt::VmMap> const& maps,
- const char* name);
-
-/** Augment the current module with information about the other ones */
-XBT_PRIVATE void postProcessObjectInformation(const simgrid::mc::RemoteProcessMemory* process,
- simgrid::mc::ObjectInformation* info);
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_TYPE_HPP
-#define SIMGRID_MC_TYPE_HPP
-
-#include <cstddef>
-
-#include <string>
-#include <vector>
-
-#include <dwarf.h>
-
-#include "xbt/asserts.h"
-#include "xbt/base.h"
-
-#include "src/mc/inspect/LocationList.hpp"
-#include "src/mc/mc_forward.hpp"
-
-namespace simgrid::mc {
-
-/** A member of a structure, union
- *
- * Inheritance is seen as a special member as well.
- */
-class Member {
-public:
- using flags_type = int;
- static constexpr flags_type INHERITANCE_FLAG = 1;
- static constexpr flags_type VIRTUAL_POINTER_FLAG = 2;
-
- Member() = default;
-
- /** Whether this member represent some inherited part of the object */
- flags_type flags = 0;
-
- /** Name of the member (if any) */
- std::string name;
-
- /** DWARF location expression for locating the location of the member */
- simgrid::dwarf::DwarfExpression location_expression;
-
- std::size_t byte_size = 0; // Do we really need this?
-
- unsigned type_id = 0;
- simgrid::mc::Type* type = nullptr;
-
- bool isInheritance() const { return this->flags & INHERITANCE_FLAG; }
- bool isVirtualPointer() const { return this->flags & VIRTUAL_POINTER_FLAG; }
-
- /** Whether the member is at a fixed offset from the base address */
- bool has_offset_location() const
- {
- // Recognize the expression `DW_OP_plus_uconst(offset)`:
- return location_expression.size() == 1 && location_expression[0].atom == DW_OP_plus_uconst;
- }
-
- /** Get the offset of the member
- *
- * This is only valid is the member is at a fixed offset from the base.
- * This is often the case (for C types, C++ type without virtual
- * inheritance).
- *
- * If the location is more complex, the location expression has
- * to be evaluated (which might need accessing the memory).
- */
- int offset() const
- {
- xbt_assert(this->has_offset_location());
- return this->location_expression[0].number;
- }
-
- /** Set the location of the member as a fixed offset */
- void offset(int new_offset)
- {
- // Set the expression to be `DW_OP_plus_uconst(offset)`:
- Dwarf_Op op;
- op.atom = DW_OP_plus_uconst;
- op.number = new_offset;
- this->location_expression = {op};
- }
-};
-
-/** A type in the model-checked program */
-class Type {
-public:
- Type() = default;
-
- /** The DWARF TAG of the type (e.g. DW_TAG_array_type) */
- int type = 0;
- unsigned id = 0; /* Offset in the section (in hexadecimal form) */
- std::string name; /* Name of the type */
- int byte_size = 0; /* Size in bytes */
- int element_count = 0; /* Number of elements for array type */
- unsigned type_id = 0; /* DW_AT_type id */
- std::vector<Member> members; /* if DW_TAG_structure_type, DW_TAG_class_type, DW_TAG_union_type*/
-
- simgrid::mc::Type* subtype = nullptr; // DW_AT_type
- simgrid::mc::Type* full_type = nullptr; // The same (but more complete) type
-};
-
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_VARIABLE_HPP
-#define SIMGRID_MC_VARIABLE_HPP
-
-#include <cstddef>
-
-#include <string>
-
-#include "src/mc/inspect/LocationList.hpp"
-#include "src/mc/mc_forward.hpp"
-
-namespace simgrid::mc {
-
-/** A variable (global or local) in the model-checked program */
-class Variable {
-public:
- Variable() = default;
- std::uint32_t id = 0;
- bool global = false;
- std::string name;
- unsigned type_id = 0;
- simgrid::mc::Type* type = nullptr;
-
- /** Address of the variable (if it is fixed) */
- void* address = nullptr;
-
- /** Description of the location of the variable (if it's not fixed) */
- simgrid::dwarf::LocationList location_list;
-
- /** Offset of validity of the variable (DW_AT_start_scope)
- *
- * This is an offset from the variable scope beginning. This variable
- * is only valid starting from this offset.
- */
- std::size_t start_scope = 0;
-
- simgrid::mc::ObjectInformation* object_info = nullptr;
-};
-
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/simgrid/util.hpp"
-#include "xbt/log.h"
-#include "xbt/string.hpp"
-#include "xbt/sysdep.h"
-#include <simgrid/config.h>
-
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/inspect/Variable.hpp"
-#include "src/mc/inspect/mc_dwarf.hpp"
-#include "src/mc/mc_private.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include <algorithm>
-#include <array>
-#include <cerrno>
-#include <cinttypes>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <fcntl.h>
-#include <memory>
-#include <unordered_map>
-#include <utility>
-
-#include <boost/range/algorithm.hpp>
-
-#include <elfutils/libdw.h>
-#include <elfutils/version.h>
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_dwarf, mc, "DWARF processing");
-
-/** @brief The default DW_TAG_lower_bound for a given DW_AT_language.
- *
- * The default for a given language is defined in the DWARF spec.
- *
- * @param language constant as defined by the DWARf spec
- */
-static uint64_t MC_dwarf_default_lower_bound(int lang);
-
-/** @brief Computes the the element_count of a DW_TAG_enumeration_type DIE
- *
- * This is the number of elements in a given array dimension.
- *
- * A reference of the compilation unit (DW_TAG_compile_unit) is
- * needed because the default lower bound (when there is no DW_AT_lower_bound)
- * depends of the language of the compilation unit (DW_AT_language).
- *
- * @param die DIE for the DW_TAG_enumeration_type or DW_TAG_subrange_type
- * @param unit DIE of the DW_TAG_compile_unit
- */
-static uint64_t MC_dwarf_subrange_element_count(Dwarf_Die* die, Dwarf_Die* unit);
-
-/** @brief Computes the number of elements of a given DW_TAG_array_type.
- *
- * @param die DIE for the DW_TAG_array_type
- */
-static uint64_t MC_dwarf_array_element_count(Dwarf_Die* die, Dwarf_Die* unit);
-
-/** @brief Process a DIE
- *
- * @param info the resulting object for the library/binary file (output)
- * @param die the current DIE
- * @param unit the DIE of the compile unit of the current DIE
- * @param frame containing frame if any
- */
-static void MC_dwarf_handle_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns);
-
-/** @brief Process a type DIE
- */
-static void MC_dwarf_handle_type_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns);
-
-/** @brief Calls MC_dwarf_handle_die on all children of the given die
- *
- * @param info the resulting object for the library/binary file (output)
- * @param die the current DIE
- * @param unit the DIE of the compile unit of the current DIE
- * @param frame containing frame if any
- */
-static void MC_dwarf_handle_children(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns);
-
-/** @brief Handle a variable (DW_TAG_variable or other)
- *
- * @param info the resulting object for the library/binary file (output)
- * @param die the current DIE
- * @param unit the DIE of the compile unit of the current DIE
- * @param frame containing frame if any
- */
-static void MC_dwarf_handle_variable_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, const Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns);
-
-/** @brief Get the DW_TAG_type of the DIE
- *
- * @param die DIE
- * @return DW_TAG_type attribute as a new string (nullptr if none)
- */
-static std::uint64_t MC_dwarf_at_type(Dwarf_Die* die);
-
-namespace simgrid::dwarf {
-
-enum class TagClass { Unknown, Type, Subprogram, Variable, Scope, Namespace };
-
-/*** Class of forms defined in the DWARF standard */
-enum class FormClass {
- Unknown,
- Address, // Location in the program's address space
- Block, // Arbitrary block of bytes
- Constant,
- String,
- Flag, // Boolean value
- Reference, // Reference to another DIE
- ExprLoc, // DWARF expression/location description
- LinePtr,
- LocListPtr,
- MacPtr,
- RangeListPtr
-};
-
-static TagClass classify_tag(int tag)
-{
- static const std::unordered_map<int, TagClass> map = {
- {DW_TAG_array_type, TagClass::Type}, {DW_TAG_class_type, TagClass::Type},
- {DW_TAG_enumeration_type, TagClass::Type}, {DW_TAG_typedef, TagClass::Type},
- {DW_TAG_pointer_type, TagClass::Type}, {DW_TAG_reference_type, TagClass::Type},
- {DW_TAG_rvalue_reference_type, TagClass::Type}, {DW_TAG_string_type, TagClass::Type},
- {DW_TAG_structure_type, TagClass::Type}, {DW_TAG_subroutine_type, TagClass::Type},
- {DW_TAG_union_type, TagClass::Type}, {DW_TAG_ptr_to_member_type, TagClass::Type},
- {DW_TAG_set_type, TagClass::Type}, {DW_TAG_subrange_type, TagClass::Type},
- {DW_TAG_base_type, TagClass::Type}, {DW_TAG_const_type, TagClass::Type},
- {DW_TAG_file_type, TagClass::Type}, {DW_TAG_packed_type, TagClass::Type},
- {DW_TAG_volatile_type, TagClass::Type}, {DW_TAG_restrict_type, TagClass::Type},
- {DW_TAG_interface_type, TagClass::Type}, {DW_TAG_unspecified_type, TagClass::Type},
- {DW_TAG_shared_type, TagClass::Type},
-
- {DW_TAG_subprogram, TagClass::Subprogram},
-
- {DW_TAG_variable, TagClass::Variable}, {DW_TAG_formal_parameter, TagClass::Variable},
-
- {DW_TAG_lexical_block, TagClass::Scope}, {DW_TAG_try_block, TagClass::Scope},
- {DW_TAG_catch_block, TagClass::Scope}, {DW_TAG_inlined_subroutine, TagClass::Scope},
- {DW_TAG_with_stmt, TagClass::Scope},
-
- {DW_TAG_namespace, TagClass::Namespace}};
-
- auto res = map.find(tag);
- return res != map.end() ? res->second : TagClass::Unknown;
-}
-
-/** @brief Find the DWARF data class for a given DWARF data form
- *
- * This mapping is defined in the DWARF spec.
- *
- * @param form The form (values taken from the DWARF spec)
- * @return An internal representation for the corresponding class
- * */
-static FormClass classify_form(int form)
-{
- static const std::unordered_map<int, FormClass> map = {
- {DW_FORM_addr, FormClass::Address},
-
- {DW_FORM_block2, FormClass::Block}, {DW_FORM_block4, FormClass::Block},
- {DW_FORM_block, FormClass::Block}, {DW_FORM_block1, FormClass::Block},
-
- {DW_FORM_data1, FormClass::Constant}, {DW_FORM_data2, FormClass::Constant},
- {DW_FORM_data4, FormClass::Constant}, {DW_FORM_data8, FormClass::Constant},
- {DW_FORM_udata, FormClass::Constant}, {DW_FORM_sdata, FormClass::Constant},
-#if _ELFUTILS_PREREQ(0, 171)
- {DW_FORM_implicit_const, FormClass::Constant},
-#endif
-
- {DW_FORM_string, FormClass::String}, {DW_FORM_strp, FormClass::String},
-
- {DW_FORM_ref_addr, FormClass::Reference}, {DW_FORM_ref1, FormClass::Reference},
- {DW_FORM_ref2, FormClass::Reference}, {DW_FORM_ref4, FormClass::Reference},
- {DW_FORM_ref8, FormClass::Reference}, {DW_FORM_ref_udata, FormClass::Reference},
-
- {DW_FORM_flag, FormClass::Flag}, {DW_FORM_flag_present, FormClass::Flag},
-
- {DW_FORM_exprloc, FormClass::ExprLoc}
-
- // TODO sec offset
- // TODO indirect
- };
-
- auto res = map.find(form);
- return res != map.end() ? res->second : FormClass::Unknown;
-}
-
-/** @brief Get the name of the tag of a given DIE
- *
- * @param die DIE
- * @return name of the tag of this DIE
- */
-inline XBT_PRIVATE const char* tagname(Dwarf_Die* die)
-{
- return tagname(dwarf_tag(die));
-}
-
-} // namespace simgrid::dwarf
-
-// ***** Attributes
-
-/** @brief Get an attribute of a given DIE as a string
- *
- * @param die the DIE
- * @param attribute attribute
- * @return value of the given attribute of the given DIE
- */
-static const char* MC_dwarf_attr_integrate_string(Dwarf_Die* die, int attribute)
-{
- Dwarf_Attribute attr;
- if (not dwarf_attr_integrate(die, attribute, &attr))
- return nullptr;
- else
- return dwarf_formstring(&attr);
-}
-
-static Dwarf_Off MC_dwarf_attr_integrate_dieoffset(Dwarf_Die* die, int attribute)
-{
- Dwarf_Attribute attr;
- if (dwarf_hasattr_integrate(die, attribute) == 0)
- return 0;
- dwarf_attr_integrate(die, attribute, &attr);
- Dwarf_Die subtype_die;
- xbt_assert(dwarf_formref_die(&attr, &subtype_die) != nullptr, "Could not find DIE");
- return dwarf_dieoffset(&subtype_die);
-}
-
-/** @brief Find the type/subtype (DW_AT_type) for a DIE
- *
- * @param die the DIE
- * @return DW_AT_type reference as a global offset in hexadecimal (or nullptr)
- */
-static std::uint64_t MC_dwarf_at_type(Dwarf_Die* die)
-{
- return MC_dwarf_attr_integrate_dieoffset(die, DW_AT_type);
-}
-
-static uint64_t MC_dwarf_attr_integrate_addr(Dwarf_Die* die, int attribute)
-{
- Dwarf_Attribute attr;
- if (dwarf_attr_integrate(die, attribute, &attr) == nullptr)
- return 0;
- Dwarf_Addr value;
- if (dwarf_formaddr(&attr, &value) == 0)
- return (uint64_t)value;
- else
- return 0;
-}
-
-static uint64_t MC_dwarf_attr_integrate_uint(Dwarf_Die* die, int attribute, uint64_t default_value)
-{
- Dwarf_Attribute attr;
- if (dwarf_attr_integrate(die, attribute, &attr) == nullptr)
- return default_value;
- Dwarf_Word value;
- return dwarf_formudata(dwarf_attr_integrate(die, attribute, &attr), &value) == 0 ? (uint64_t)value : default_value;
-}
-
-static bool MC_dwarf_attr_flag(Dwarf_Die* die, int attribute, bool integrate)
-{
- Dwarf_Attribute attr;
- if ((integrate ? dwarf_attr_integrate(die, attribute, &attr) : dwarf_attr(die, attribute, &attr)) == nullptr)
- return false;
-
- bool result;
- xbt_assert(not dwarf_formflag(&attr, &result), "Unexpected form for attribute %s",
- simgrid::dwarf::attrname(attribute));
- return result;
-}
-
-/** @brief Find the default lower bound for a given language
- *
- * The default lower bound of an array (when DW_TAG_lower_bound
- * is missing) depends on the language of the compilation unit.
- *
- * @param lang Language of the compilation unit (values defined in the DWARF spec)
- * @return Default lower bound of an array in this compilation unit
- * */
-static uint64_t MC_dwarf_default_lower_bound(int lang)
-{
- const std::unordered_map<int, unsigned> map = {
- {DW_LANG_C, 0}, {DW_LANG_C89, 0}, {DW_LANG_C99, 0}, {DW_LANG_C11, 0},
- {DW_LANG_C_plus_plus, 0}, {DW_LANG_C_plus_plus_11, 0}, {DW_LANG_C_plus_plus_14, 0}, {DW_LANG_D, 0},
- {DW_LANG_Java, 0}, {DW_LANG_ObjC, 0}, {DW_LANG_ObjC_plus_plus, 0}, {DW_LANG_Python, 0},
- {DW_LANG_UPC, 0},
-
- {DW_LANG_Ada83, 1}, {DW_LANG_Ada95, 1}, {DW_LANG_Fortran77, 1}, {DW_LANG_Fortran90, 1},
- {DW_LANG_Fortran95, 1}, {DW_LANG_Fortran03, 1}, {DW_LANG_Fortran08, 1}, {DW_LANG_Modula2, 1},
- {DW_LANG_Pascal83, 1}, {DW_LANG_PL1, 1}, {DW_LANG_Cobol74, 1}, {DW_LANG_Cobol85, 1}};
-
- auto res = map.find(lang);
- xbt_assert(res != map.end(), "No default DW_TAG_lower_bound for language %i and none given", lang);
- return res->second;
-}
-
-/** @brief Finds the number of elements in a DW_TAG_subrange_type or DW_TAG_enumeration_type DIE
- *
- * @param die the DIE
- * @param unit DIE of the compilation unit
- * @return number of elements in the range
- * */
-static uint64_t MC_dwarf_subrange_element_count(Dwarf_Die* die, Dwarf_Die* unit)
-{
- xbt_assert(dwarf_tag(die) == DW_TAG_enumeration_type || dwarf_tag(die) == DW_TAG_subrange_type,
- "MC_dwarf_subrange_element_count called with DIE of type %s", simgrid::dwarf::tagname(die));
-
- // Use DW_TAG_count if present:
- if (dwarf_hasattr_integrate(die, DW_AT_count))
- return MC_dwarf_attr_integrate_uint(die, DW_AT_count, 0);
- // Otherwise compute DW_TAG_upper_bound-DW_TAG_lower_bound + 1:
-
- if (not dwarf_hasattr_integrate(die, DW_AT_upper_bound))
- // This is not really 0, but the code expects this (we do not know):
- return 0;
-
- uint64_t upper_bound = MC_dwarf_attr_integrate_uint(die, DW_AT_upper_bound, static_cast<uint64_t>(-1));
-
- uint64_t lower_bound = 0;
- if (dwarf_hasattr_integrate(die, DW_AT_lower_bound))
- lower_bound = MC_dwarf_attr_integrate_uint(die, DW_AT_lower_bound, static_cast<uint64_t>(-1));
- else
- lower_bound = MC_dwarf_default_lower_bound(dwarf_srclang(unit));
- return upper_bound - lower_bound + 1;
-}
-
-/** @brief Finds the number of elements in an array type (DW_TAG_array_type)
- *
- * The compilation unit might be needed because the default lower
- * bound depends on the language of the compilation unit.
- *
- * @param die the DIE of the DW_TAG_array_type
- * @param unit the DIE of the compilation unit
- * @return number of elements in this array type
- * */
-static uint64_t MC_dwarf_array_element_count(Dwarf_Die* die, Dwarf_Die* unit)
-{
- xbt_assert(dwarf_tag(die) == DW_TAG_array_type, "MC_dwarf_array_element_count called with DIE of type %s",
- simgrid::dwarf::tagname(die));
-
- int result = 1;
- Dwarf_Die child;
- for (int res = dwarf_child(die, &child); res == 0; res = dwarf_siblingof(&child, &child)) {
- int child_tag = dwarf_tag(&child);
- if (child_tag == DW_TAG_subrange_type || child_tag == DW_TAG_enumeration_type)
- result *= MC_dwarf_subrange_element_count(&child, unit);
- }
- return result;
-}
-
-// ***** Variable
-
-/** Sort the variable by name and address.
- *
- * We could use boost::container::flat_set instead.
- */
-static bool MC_compare_variable(simgrid::mc::Variable const& a, simgrid::mc::Variable const& b)
-{
- int cmp = a.name.compare(b.name);
- if (cmp < 0)
- return true;
- else if (cmp > 0)
- return false;
- else
- return a.address < b.address;
-}
-
-// ***** simgrid::mc::Type*
-
-/** @brief Initialize the location of a member of a type
- * (DW_AT_data_member_location of a DW_TAG_member).
- *
- * @param type a type (struct, class)
- * @param member the member of the type
- * @param child DIE of the member (DW_TAG_member)
- */
-static void MC_dwarf_fill_member_location(const simgrid::mc::Type* type, simgrid::mc::Member* member, Dwarf_Die* child)
-{
- xbt_assert(not dwarf_hasattr(child, DW_AT_data_bit_offset), "Can't groke DW_AT_data_bit_offset.");
-
- if (not dwarf_hasattr_integrate(child, DW_AT_data_member_location)) {
- xbt_assert(type->type == DW_TAG_union_type,
- "Missing DW_AT_data_member_location field in DW_TAG_member %s of type <%" PRIx64 ">%s",
- member->name.c_str(), (uint64_t)type->id, type->name.c_str());
- return;
- }
-
- Dwarf_Attribute attr;
- dwarf_attr_integrate(child, DW_AT_data_member_location, &attr);
- int form = dwarf_whatform(&attr);
- simgrid::dwarf::FormClass form_class = simgrid::dwarf::classify_form(form);
- switch (form_class) {
- case simgrid::dwarf::FormClass::ExprLoc:
- case simgrid::dwarf::FormClass::Block:
- // Location expression:
- {
- Dwarf_Op* expr;
- size_t len;
- xbt_assert(not dwarf_getlocation(&attr, &expr, &len),
- "Could not read location expression DW_AT_data_member_location in DW_TAG_member %s of type <%" PRIx64
- ">%s",
- MC_dwarf_attr_integrate_string(child, DW_AT_name), (uint64_t)type->id, type->name.c_str());
- member->location_expression = simgrid::dwarf::DwarfExpression(expr, expr + len);
- break;
- }
- case simgrid::dwarf::FormClass::Constant:
- // Offset from the base address of the object:
- {
- Dwarf_Word offset;
- xbt_assert(not dwarf_formudata(&attr, &offset), "Cannot get %s location <%" PRIx64 ">%s",
- MC_dwarf_attr_integrate_string(child, DW_AT_name), (uint64_t)type->id, type->name.c_str());
- member->offset(offset);
- break;
- }
-
- default:
- // includes FormClass::LocListPtr (reference to a location list: TODO) and FormClass::Reference (it's supposed to
- // be possible in DWARF2 but I couldn't find its semantic in the spec)
- xbt_die("Can't handle form class (%d) / form 0x%x as DW_AT_member_location", (int)form_class, (unsigned)form);
- }
-}
-
-/** @brief Populate the list of members of a type
- *
- * @param info ELF object containing the type DIE
- * @param die DIE of the type
- * @param unit DIE of the compilation unit containing the type DIE
- * @param type the type
- */
-static void MC_dwarf_add_members(const simgrid::mc::ObjectInformation* /*info*/, Dwarf_Die* die,
- const Dwarf_Die* /*unit*/, simgrid::mc::Type* type)
-{
- Dwarf_Die child;
- xbt_assert(type->members.empty());
- for (int res = dwarf_child(die, &child); res == 0; res = dwarf_siblingof(&child, &child)) {
- int tag = dwarf_tag(&child);
- if (tag == DW_TAG_member || tag == DW_TAG_inheritance) {
- // Skip declarations:
- if (MC_dwarf_attr_flag(&child, DW_AT_declaration, false))
- continue;
-
- // Skip compile time constants:
- if (dwarf_hasattr(&child, DW_AT_const_value))
- continue;
-
- // TODO, we should use another type (because is is not a type but a member)
- simgrid::mc::Member member;
- if (tag == DW_TAG_inheritance)
- member.flags |= simgrid::mc::Member::INHERITANCE_FLAG;
-
- const char* name = MC_dwarf_attr_integrate_string(&child, DW_AT_name);
- if (name)
- member.name = name;
- // Those base names are used by GCC and clang for virtual table pointers
- // respectively ("__vptr$ClassName", "__vptr.ClassName"):
- if (member.name.rfind("__vptr$", 0) == 0 || member.name.rfind("__vptr.", 0) == 0)
- member.flags |= simgrid::mc::Member::VIRTUAL_POINTER_FLAG;
- // A cleaner solution would be to check against the type:
- // ---
- // tag: DW_TAG_member
- // name: "_vptr$Foo"
- // type:
- // # Type for a pointer to a vtable
- // tag: DW_TAG_pointer_type
- // type:
- // # Type for a vtable:
- // tag: DW_TAG_pointer_type
- // name: "__vtbl_ptr_type"
- // type:
- // tag: DW_TAG_subroutine_type
- // type:
- // tag: DW_TAG_base_type
- // name: "int"
- // ---
-
- member.byte_size = MC_dwarf_attr_integrate_uint(&child, DW_AT_byte_size, 0);
- member.type_id = MC_dwarf_at_type(&child);
-
- if (dwarf_hasattr(&child, DW_AT_data_bit_offset)) {
- XBT_WARN("Can't groke DW_AT_data_bit_offset for %s", name);
- continue;
- }
-
- MC_dwarf_fill_member_location(type, &member, &child);
-
- xbt_assert(member.type_id, "Missing type for member %s of <%" PRIx64 ">%s", member.name.c_str(),
- (uint64_t)type->id, type->name.c_str());
-
- type->members.push_back(std::move(member));
- }
- }
-}
-
-/** @brief Create a MC type object from a DIE
- *
- * @param info current object info object
- * @param die DIE (for a given type)
- * @param unit compilation unit of the current DIE
- * @return MC representation of the type
- */
-static simgrid::mc::Type MC_dwarf_die_to_type(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns)
-{
- simgrid::mc::Type type;
- type.type = dwarf_tag(die);
- type.name = "";
- type.element_count = -1;
-
- // Global Offset
- type.id = dwarf_dieoffset(die);
-
- const char* prefix;
- switch (type.type) {
- case DW_TAG_structure_type:
- prefix = "struct ";
- break;
- case DW_TAG_union_type:
- prefix = "union ";
- break;
- case DW_TAG_class_type:
- prefix = "class ";
- break;
- default:
- prefix = "";
- }
-
- const char* name = MC_dwarf_attr_integrate_string(die, DW_AT_name);
- if (name != nullptr) {
- if (ns)
- type.name = simgrid::xbt::string_printf("%s%s::%s", prefix, ns, name);
- else
- type.name = simgrid::xbt::string_printf("%s%s", prefix, name);
- }
-
- type.type_id = MC_dwarf_at_type(die);
-
- // Some compilers do not emit DW_AT_byte_size for pointer_type,
- // so we fill this. We currently assume that the model-checked process is in
- // the same architecture..
- if (type.type == DW_TAG_pointer_type)
- type.byte_size = sizeof(void*);
-
- // Computation of the byte_size
- if (dwarf_hasattr_integrate(die, DW_AT_byte_size))
- type.byte_size = MC_dwarf_attr_integrate_uint(die, DW_AT_byte_size, 0);
- else if (type.type == DW_TAG_array_type || type.type == DW_TAG_structure_type || type.type == DW_TAG_class_type) {
- Dwarf_Word size;
- if (dwarf_aggregate_size(die, &size) == 0)
- type.byte_size = size;
- }
-
- switch (type.type) {
- case DW_TAG_array_type:
- type.element_count = MC_dwarf_array_element_count(die, unit);
- // TODO, handle DW_byte_stride and (not) DW_bit_stride
- break;
-
- case DW_TAG_pointer_type:
- case DW_TAG_reference_type:
- case DW_TAG_rvalue_reference_type:
- break;
-
- case DW_TAG_structure_type:
- case DW_TAG_union_type:
- case DW_TAG_class_type:
- MC_dwarf_add_members(info, die, unit, &type);
- MC_dwarf_handle_children(info, die, unit, frame,
- ns ? simgrid::xbt::string_printf("%s::%s", ns, name).c_str() : type.name.c_str());
- break;
-
- default:
- XBT_DEBUG("Unhandled type: %d (%s)", type.type, simgrid::dwarf::tagname(type.type));
- break;
- }
-
- return type;
-}
-
-static void MC_dwarf_handle_type_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns)
-{
- simgrid::mc::Type type = MC_dwarf_die_to_type(info, die, unit, frame, ns);
- auto& t = (info->types[type.id] = std::move(type));
- if (not t.name.empty() && type.byte_size != 0)
- info->full_types_by_name[t.name] = &t;
-}
-
-static std::unique_ptr<simgrid::mc::Variable> MC_die_to_variable(simgrid::mc::ObjectInformation* info, Dwarf_Die* die,
- const Dwarf_Die* /*unit*/,
- const simgrid::mc::Frame* frame, const char* ns)
-{
- // Skip declarations:
- if (MC_dwarf_attr_flag(die, DW_AT_declaration, false))
- return nullptr;
-
- // Skip compile time constants:
- if (dwarf_hasattr(die, DW_AT_const_value))
- return nullptr;
-
- Dwarf_Attribute attr_location;
- if (dwarf_attr(die, DW_AT_location, &attr_location) == nullptr)
- // No location: do not add it ?
- return nullptr;
-
- auto variable = std::make_unique<simgrid::mc::Variable>();
- variable->id = dwarf_dieoffset(die);
- variable->global = frame == nullptr; // Can be override base on DW_AT_location
- variable->object_info = info;
-
- const char* name = MC_dwarf_attr_integrate_string(die, DW_AT_name);
- if (name)
- variable->name = name;
- variable->type_id = MC_dwarf_at_type(die);
-
- int form = dwarf_whatform(&attr_location);
- simgrid::dwarf::FormClass form_class;
- if (form == DW_FORM_sec_offset)
- form_class = simgrid::dwarf::FormClass::Constant;
- else
- form_class = simgrid::dwarf::classify_form(form);
- switch (form_class) {
- case simgrid::dwarf::FormClass::ExprLoc:
- case simgrid::dwarf::FormClass::Block:
- // Location expression:
- {
- Dwarf_Op* expr;
- size_t len;
- xbt_assert(not dwarf_getlocation(&attr_location, &expr, &len),
- "Could not read location expression in DW_AT_location "
- "of variable <%" PRIx64 ">%s",
- (uint64_t)variable->id, variable->name.c_str());
-
- if (len == 1 && expr[0].atom == DW_OP_addr) {
- variable->global = true;
- auto offset = static_cast<uintptr_t>(expr[0].number);
- auto base = reinterpret_cast<uintptr_t>(info->base_address());
- variable->address = reinterpret_cast<void*>(base + offset);
- } else
- variable->location_list = {
- simgrid::dwarf::LocationListEntry(simgrid::dwarf::DwarfExpression(expr, expr + len))};
-
- break;
- }
-
- case simgrid::dwarf::FormClass::LocListPtr:
- case simgrid::dwarf::FormClass::Constant:
- // Reference to location list:
- variable->location_list = simgrid::dwarf::location_list(*info, attr_location);
- break;
-
- default:
- xbt_die("Unexpected form 0x%x (%i), class 0x%x (%i) list for location in <%" PRIx64 ">%s", (unsigned)form, form,
- (unsigned)form_class, (int)form_class, (uint64_t)variable->id, variable->name.c_str());
- }
-
- // Handle start_scope:
- if (dwarf_hasattr(die, DW_AT_start_scope)) {
- Dwarf_Attribute attr;
- dwarf_attr(die, DW_AT_start_scope, &attr);
- form = dwarf_whatform(&attr);
- form_class = simgrid::dwarf::classify_form(form);
- if (form_class == simgrid::dwarf::FormClass::Constant) {
- Dwarf_Word value;
- variable->start_scope = dwarf_formudata(&attr, &value) == 0 ? (size_t)value : 0;
- } else {
- // TODO: FormClass::RangeListPtr
- xbt_die("Unhandled form 0x%x, class 0x%X for DW_AT_start_scope of variable %s", (unsigned)form,
- (unsigned)form_class, name == nullptr ? "?" : name);
- }
- }
-
- if (ns && variable->global)
- variable->name.insert(0, std::string(ns) + "::");
-
- // The current code needs a variable name,
- // generate a fake one:
- static int mc_anonymous_variable_index = 0;
- if (variable->name.empty()) {
- variable->name = "@anonymous#" + std::to_string(mc_anonymous_variable_index);
- mc_anonymous_variable_index++;
- }
- return variable;
-}
-
-static void MC_dwarf_handle_variable_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, const Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns)
-{
- std::unique_ptr<simgrid::mc::Variable> variable = MC_die_to_variable(info, die, unit, frame, ns);
- if (not variable)
- return;
- // Those arrays are sorted later:
- if (variable->global)
- info->global_variables.push_back(std::move(*variable));
- else if (frame != nullptr)
- frame->variables.push_back(std::move(*variable));
- else
- xbt_die("No frame for this local variable");
-}
-
-static void MC_dwarf_handle_scope_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* parent_frame, const char* ns)
-{
- // TODO, handle DW_TAG_type/DW_TAG_location for DW_TAG_with_stmt
- int tag = dwarf_tag(die);
- simgrid::dwarf::TagClass klass = simgrid::dwarf::classify_tag(tag);
-
- // (Template) Subprogram declaration:
- if (klass == simgrid::dwarf::TagClass::Subprogram && MC_dwarf_attr_flag(die, DW_AT_declaration, false))
- return;
-
- if (klass == simgrid::dwarf::TagClass::Scope)
- xbt_assert(parent_frame, "No parent scope for this scope");
-
- simgrid::mc::Frame frame;
- frame.tag = tag;
- frame.id = dwarf_dieoffset(die);
- frame.object_info = info;
-
- if (klass == simgrid::dwarf::TagClass::Subprogram) {
- const char* name = MC_dwarf_attr_integrate_string(die, DW_AT_name);
- if (name && ns)
- frame.name = std::string(ns) + "::" + name;
- else if (name)
- frame.name = name;
- }
-
- frame.abstract_origin_id = MC_dwarf_attr_integrate_dieoffset(die, DW_AT_abstract_origin);
-
- // This is the base address for DWARF addresses.
- // Relocated addresses are offset from this base address.
- // See DWARF4 spec 7.5
- auto base = reinterpret_cast<std::uint64_t>(info->base_address());
-
- // TODO, support DW_AT_ranges
- uint64_t low_pc = MC_dwarf_attr_integrate_addr(die, DW_AT_low_pc);
- frame.range.begin() = low_pc ? base + low_pc : 0;
- if (low_pc) {
- // DW_AT_high_pc:
- Dwarf_Attribute attr;
- xbt_assert(dwarf_attr_integrate(die, DW_AT_high_pc, &attr), "Missing DW_AT_high_pc matching with DW_AT_low_pc");
-
- Dwarf_Sword offset;
- Dwarf_Addr high_pc;
-
- switch (simgrid::dwarf::classify_form(dwarf_whatform(&attr))) {
- // DW_AT_high_pc if an offset from the low_pc:
- case simgrid::dwarf::FormClass::Constant:
-
- xbt_assert(dwarf_formsdata(&attr, &offset) == 0, "Could not read constant");
- frame.range.end() = frame.range.begin() + offset;
- break;
-
- // DW_AT_high_pc is a relocatable address:
- case simgrid::dwarf::FormClass::Address:
- xbt_assert(dwarf_formaddr(&attr, &high_pc) == 0, "Could not read address");
- frame.range.end() = base + high_pc;
- break;
-
- default:
- xbt_die("Unexpected class for DW_AT_high_pc");
- }
- }
-
- if (klass == simgrid::dwarf::TagClass::Subprogram) {
- Dwarf_Attribute attr_frame_base;
- if (dwarf_attr_integrate(die, DW_AT_frame_base, &attr_frame_base))
- frame.frame_base_location = simgrid::dwarf::location_list(*info, attr_frame_base);
- }
-
- // Handle children:
- MC_dwarf_handle_children(info, die, unit, &frame, ns);
-
- // We sort them in order to have an (somewhat) efficient by name
- // lookup:
- boost::range::sort(frame.variables, MC_compare_variable);
-
- // Register it:
- if (klass == simgrid::dwarf::TagClass::Subprogram)
- info->subprograms[frame.id] = std::move(frame);
- else if (klass == simgrid::dwarf::TagClass::Scope)
- parent_frame->scopes.push_back(std::move(frame));
-}
-
-static void mc_dwarf_handle_namespace_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns)
-{
- const char* name = MC_dwarf_attr_integrate_string(die, DW_AT_name);
- xbt_assert(not frame, "Unexpected namespace in a subprogram");
- char* new_ns = ns == nullptr ? xbt_strdup(name) : bprintf("%s::%s", ns, name);
- MC_dwarf_handle_children(info, die, unit, frame, new_ns);
- xbt_free(new_ns);
-}
-
-static void MC_dwarf_handle_children(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns)
-{
- // For each child DIE:
- Dwarf_Die child;
- for (int res = dwarf_child(die, &child); res == 0; res = dwarf_siblingof(&child, &child))
- MC_dwarf_handle_die(info, &child, unit, frame, ns);
-}
-
-static void MC_dwarf_handle_die(simgrid::mc::ObjectInformation* info, Dwarf_Die* die, Dwarf_Die* unit,
- simgrid::mc::Frame* frame, const char* ns)
-{
- int tag = dwarf_tag(die);
- simgrid::dwarf::TagClass klass = simgrid::dwarf::classify_tag(tag);
- switch (klass) {
- // Type:
- case simgrid::dwarf::TagClass::Type:
- MC_dwarf_handle_type_die(info, die, unit, frame, ns);
- break;
-
- // Subprogram or scope:
- case simgrid::dwarf::TagClass::Subprogram:
- case simgrid::dwarf::TagClass::Scope:
- MC_dwarf_handle_scope_die(info, die, unit, frame, ns);
- return;
-
- // Variable:
- case simgrid::dwarf::TagClass::Variable:
- MC_dwarf_handle_variable_die(info, die, unit, frame, ns);
- break;
-
- case simgrid::dwarf::TagClass::Namespace:
- mc_dwarf_handle_namespace_die(info, die, unit, frame, ns);
- break;
-
- default:
- break;
- }
-}
-
-static Elf64_Half get_type(Elf* elf)
-{
- if (const Elf64_Ehdr* ehdr64 = elf64_getehdr(elf))
- return ehdr64->e_type;
- if (const Elf32_Ehdr* ehdr32 = elf32_getehdr(elf))
- return ehdr32->e_type;
- xbt_die("Could not get ELF heeader");
-}
-
-static void read_dwarf_info(simgrid::mc::ObjectInformation* info, Dwarf* dwarf)
-{
- // For each compilation unit:
- Dwarf_Off offset = 0;
- Dwarf_Off next_offset = 0;
- size_t length;
-
- while (dwarf_nextcu(dwarf, offset, &next_offset, &length, nullptr, nullptr, nullptr) == 0) {
- if (Dwarf_Die unit_die; dwarf_offdie(dwarf, offset + length, &unit_die) != nullptr)
- MC_dwarf_handle_children(info, &unit_die, &unit_die, nullptr, nullptr);
- offset = next_offset;
- }
-}
-
-/** Get the build-id (NT_GNU_BUILD_ID) from the ELF file
- *
- * This build-id may is used to locate an external debug (DWARF) file
- * for this ELF file.
- *
- * @param elf libelf handle for an ELF file
- * @return build-id for this ELF file (or an empty vector if none is found)
- */
-static std::vector<std::byte> get_build_id(Elf* elf)
-{
-#ifdef __linux
- // Summary: the GNU build ID is stored in a ("GNU, NT_GNU_BUILD_ID) note
- // found in a PT_NOTE entry in the program header table.
-
- size_t phnum;
- xbt_assert(elf_getphdrnum(elf, &phnum) == 0, "Could not read program headers");
-
- // Iterate over the program headers and find the PT_NOTE ones:
- for (size_t i = 0; i < phnum; ++i) {
- GElf_Phdr phdr_temp;
- const GElf_Phdr* phdr = gelf_getphdr(elf, i, &phdr_temp);
- if (phdr->p_type != PT_NOTE)
- continue;
-
- Elf_Data* data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
-
- // Iterate over the notes and find the NT_GNU_BUILD_ID one:
- size_t pos = 0;
- while (pos < data->d_size) {
- GElf_Nhdr nhdr;
- // Location of the name within Elf_Data:
- size_t name_pos;
- size_t desc_pos;
- pos = gelf_getnote(data, pos, &nhdr, &name_pos, &desc_pos);
- // A build ID note is identified by the pair ("GNU", NT_GNU_BUILD_ID)
- // (a namespace and a type within this namespace):
- if (nhdr.n_type == NT_GNU_BUILD_ID && nhdr.n_namesz == sizeof("GNU") &&
- memcmp(static_cast<std::byte*>(data->d_buf) + name_pos, "GNU", sizeof("GNU")) == 0) {
- XBT_DEBUG("Found GNU/NT_GNU_BUILD_ID note");
- std::byte* start = static_cast<std::byte*>(data->d_buf) + desc_pos;
- std::byte* end = start + nhdr.n_descsz;
- return std::vector<std::byte>(start, end);
- }
- }
- }
-#endif
- return std::vector<std::byte>();
-}
-
-/** Binary data to hexadecimal */
-static inline std::array<char, 2> to_hex(std::byte byte)
-{
- constexpr std::array<char, 16> hexdigits{
- {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}};
- return {hexdigits[std::to_integer<unsigned>(byte >> 4)], hexdigits[std::to_integer<unsigned>(byte & std::byte{0xF})]};
-}
-
-/** Binary data to hexadecimal */
-static std::string to_hex(const std::byte* data, std::size_t count)
-{
- std::string res;
- res.resize(2 * count);
- for (std::size_t i = 0; i < count; i++)
- std::copy_n(cbegin(to_hex(data[i])), 2, &res[2 * i]);
- return res;
-}
-
-/** Binary data to hexadecimal */
-static std::string to_hex(std::vector<std::byte> const& data)
-{
- return to_hex(data.data(), data.size());
-}
-
-/** Base directories for external debug files */
-static constexpr auto debug_paths = {
- "/usr/lib/debug/",
- "/usr/local/lib/debug/",
-};
-
-/** Locate an external debug file from the NT_GNU_BUILD_ID
- *
- * This is one of the mechanisms used for
- * [separate debug files](https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html).
- */
-// Example:
-// /usr/lib/debug/.build-id/0b/dc77f1c29aea2b14ff5acd9a19ab3175ffdeae.debug
-static int find_by_build_id(std::vector<std::byte> id)
-{
- std::string filename;
- std::string hex = to_hex(id);
- for (const char* const& debug_path : debug_paths) {
- // Example:
- filename = std::string(debug_path) + ".build-id/" + to_hex(id.data(), 1) + '/' +
- to_hex(id.data() + 1, id.size() - 1) + ".debug";
- XBT_DEBUG("Checking debug file: %s", filename.c_str());
- if (int fd = open(filename.c_str(), O_RDONLY); fd != -1) {
- XBT_DEBUG("Found debug file: %s\n", hex.c_str());
- return fd;
- }
- xbt_assert(errno != ENOENT, "Could not open file: %s", strerror(errno));
- }
- XBT_DEBUG("No debug info found for build ID %s\n", hex.data());
- return -1;
-}
-
-/** @brief Populate the debugging information of the given ELF object
- *
- * Read the DWARF information of the ELF object and populate the
- * lists of types, variables, functions.
- */
-static void MC_load_dwarf(simgrid::mc::ObjectInformation* info)
-{
- xbt_assert(elf_version(EV_CURRENT) != EV_NONE, "libelf initialization error");
-
- // Open the ELF file:
- int fd = open(info->file_name.c_str(), O_RDONLY);
- xbt_assert(fd >= 0, "Could not open file %s", info->file_name.c_str());
- Elf* elf = elf_begin(fd, ELF_C_READ, nullptr);
- xbt_assert(elf != nullptr && elf_kind(elf) == ELF_K_ELF, "%s is not an ELF file", info->file_name.c_str());
-
- // Remember if this is a `ET_EXEC` (fixed location) or `ET_DYN`:
- if (get_type(elf) == ET_EXEC)
- info->flags |= simgrid::mc::ObjectInformation::Executable;
-
- // Read DWARF debug information in the file:
- if (Dwarf* dwarf = dwarf_begin_elf(elf, DWARF_C_READ, nullptr)) {
- read_dwarf_info(info, dwarf);
- dwarf_end(dwarf);
- elf_end(elf);
- close(fd);
- return;
- }
-
- // If there was no DWARF in the file, try to find it in a separate file.
- // Different methods might be used to store the DWARF information:
- // * GNU NT_GNU_BUILD_ID
- // * .gnu_debuglink
- // See https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
- // for reference of what we are doing.
-
- // Try with NT_GNU_BUILD_ID: we find the build ID in the ELF file and then
- // use this ID to find the file in some known locations in the filesystem.
- if (std::vector<std::byte> build_id = get_build_id(elf); not build_id.empty()) {
- elf_end(elf);
- close(fd);
-
- // Find the debug file using the build id:
- fd = find_by_build_id(build_id);
- xbt_assert(fd != -1,
- "Missing debug info for %s with build-id %s\n"
- "You might want to install the suitable debugging package.\n",
- info->file_name.c_str(), to_hex(build_id).c_str());
-
- // Load the DWARF info from this file:
- XBT_DEBUG("Load DWARF for %s", info->file_name.c_str());
- Dwarf* dwarf = dwarf_begin(fd, DWARF_C_READ);
- xbt_assert(dwarf != nullptr, "No DWARF info for %s", info->file_name.c_str());
- read_dwarf_info(info, dwarf);
- dwarf_end(dwarf);
- close(fd);
- return;
- }
-
- // TODO, try to find DWARF info using .gnu_debuglink.
-
- elf_end(elf);
- close(fd);
- xbt_die("Debugging information not found for %s\n"
- "Try recompiling with -g\n",
- info->file_name.c_str());
-}
-
-// ***** Functions index
-
-static void MC_make_functions_index(simgrid::mc::ObjectInformation* info)
-{
- info->functions_index.clear();
-
- for (auto& [_, e] : info->subprograms) {
- if (e.range.begin() == 0)
- continue;
- simgrid::mc::FunctionIndexEntry entry;
- entry.low_pc = (void*)e.range.begin();
- entry.function = &e;
- info->functions_index.push_back(entry);
- }
-
- info->functions_index.shrink_to_fit();
-
- // Sort the array by low_pc:
- boost::range::sort(info->functions_index,
- [](simgrid::mc::FunctionIndexEntry const& a, simgrid::mc::FunctionIndexEntry const& b) {
- return a.low_pc < b.low_pc;
- });
-}
-
-static void MC_post_process_variables(simgrid::mc::ObjectInformation* info)
-{
- // Someone needs this to be sorted but who?
- boost::range::sort(info->global_variables, MC_compare_variable);
-
- for (simgrid::mc::Variable& variable : info->global_variables)
- if (variable.type_id)
- variable.type = simgrid::util::find_map_ptr(info->types, variable.type_id);
-}
-
-static void mc_post_process_scope(simgrid::mc::ObjectInformation* info, simgrid::mc::Frame* scope)
-{
- if (scope->tag == DW_TAG_inlined_subroutine) {
- // Attach correct namespaced name in inlined subroutine:
- auto i = info->subprograms.find(scope->abstract_origin_id);
- xbt_assert(i != info->subprograms.end(), "Could not lookup abstract origin %" PRIx64,
- (std::uint64_t)scope->abstract_origin_id);
- scope->name = i->second.name;
- }
-
- // Direct:
- for (simgrid::mc::Variable& variable : scope->variables)
- if (variable.type_id)
- variable.type = simgrid::util::find_map_ptr(info->types, variable.type_id);
-
- // Recursive post-processing of nested-scopes:
- for (simgrid::mc::Frame& nested_scope : scope->scopes)
- mc_post_process_scope(info, &nested_scope);
-}
-
-static simgrid::mc::Type* MC_resolve_type(simgrid::mc::ObjectInformation* info, unsigned type_id)
-{
- if (not type_id)
- return nullptr;
- simgrid::mc::Type* type = simgrid::util::find_map_ptr(info->types, type_id);
- if (type == nullptr)
- return nullptr;
-
- // We already have the information on the type:
- if (type->byte_size != 0)
- return type;
-
- // Don't have a name, we can't find a more complete version:
- if (type->name.empty())
- return type;
-
- // Try to find a more complete description of the type:
- // We need to fix in order to support C++.
- if (simgrid::mc::Type** subtype = simgrid::util::find_map_ptr(info->full_types_by_name, type->name))
- type = *subtype;
- return type;
-}
-
-static void MC_post_process_types(simgrid::mc::ObjectInformation* info)
-{
- // Lookup "subtype" field:
- for (auto& [_, i] : info->types) {
- i.subtype = MC_resolve_type(info, i.type_id);
- for (simgrid::mc::Member& member : i.members)
- member.type = MC_resolve_type(info, member.type_id);
- }
-}
-
-namespace simgrid::mc {
-
-void ObjectInformation::ensure_dwarf_loaded()
-{
- if (dwarf_loaded)
- return;
- dwarf_loaded = true;
-
- MC_load_dwarf(this);
- MC_post_process_variables(this);
- MC_post_process_types(this);
- for (auto& [_, entry] : this->subprograms)
- mc_post_process_scope(this, &entry);
- MC_make_functions_index(this);
-}
-
-/** @brief Finds information about a given shared object/executable */
-std::shared_ptr<ObjectInformation> createObjectInformation(std::vector<xbt::VmMap> const& maps, const char* name)
-{
- auto result = std::make_shared<ObjectInformation>();
- result->file_name = name;
- simgrid::mc::find_object_address(maps, result.get());
- return result;
-}
-
-/*************************************************************************/
-
-void postProcessObjectInformation(const RemoteProcessMemory* process, ObjectInformation* info)
-{
- for (auto& [_, t] : info->types) {
- Type* type = &t;
- Type* subtype = type;
- while (subtype->type == DW_TAG_typedef || subtype->type == DW_TAG_volatile_type ||
- subtype->type == DW_TAG_const_type)
- if (subtype->subtype)
- subtype = subtype->subtype;
- else
- break;
-
- // Resolve full_type:
- if (not subtype->name.empty() && subtype->byte_size == 0)
- for (auto const& object_info : process->object_infos) {
- auto i = object_info->full_types_by_name.find(subtype->name);
- if (i != object_info->full_types_by_name.end() && not i->second->name.empty() && i->second->byte_size) {
- type->full_type = i->second;
- break;
- }
- }
- else
- type->full_type = subtype;
- }
-}
-
-} // namespace simgrid::mc
-
-namespace simgrid::dwarf {
-
-/** Convert a DWARF register into a libunwind register
- *
- * DWARF and libunwind does not use the same convention for numbering the
- * registers on some architectures. The function makes the necessary
- * conversion.
- */
-int dwarf_register_to_libunwind(int dwarf_register)
-{
-#if defined(__x86_64__) || defined(__aarch64__)
- // It seems for this arch, DWARF and libunwind agree in the numbering:
- return dwarf_register;
-#elif defined(__i386__)
- // Couldn't find the authoritative source of information for this.
- // This is inspired from http://source.winehq.org/source/dlls/dbghelp/cpu_i386.c#L517.
- constexpr std::array<int, 24> regs{
- {/* 0 */ UNW_X86_EAX, /* 1 */ UNW_X86_ECX, /* 2 */ UNW_X86_EDX, /* 3 */ UNW_X86_EBX,
- /* 4 */ UNW_X86_ESP, /* 5 */ UNW_X86_EBP, /* 6 */ UNW_X86_ESI, /* 7 */ UNW_X86_EDI,
- /* 8 */ UNW_X86_EIP, /* 9 */ UNW_X86_EFLAGS, /* 10 */ UNW_X86_CS, /* 11 */ UNW_X86_SS,
- /* 12 */ UNW_X86_DS, /* 13 */ UNW_X86_ES, /* 14 */ UNW_X86_FS, /* 15 */ UNW_X86_GS,
- /* 16 */ UNW_X86_ST0, /* 17 */ UNW_X86_ST1, /* 18 */ UNW_X86_ST2, /* 19 */ UNW_X86_ST3,
- /* 20 */ UNW_X86_ST4, /* 21 */ UNW_X86_ST5, /* 22 */ UNW_X86_ST6, /* 23 */ UNW_X86_ST7}};
- return regs.at(dwarf_register);
-#else
-#error This architecture is not supported yet for DWARF expression evaluation.
-#endif
-}
-
-} // namespace simgrid::dwarf
+++ /dev/null
-/* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_DWARF_HPP
-#define SIMGRID_MC_DWARF_HPP
-
-#include "xbt/base.h"
-
-#include "src/mc/mc_forward.hpp"
-
-namespace simgrid::dwarf {
-
-XBT_PRIVATE const char* attrname(int attr);
-XBT_PRIVATE const char* tagname(int tag);
-
-XBT_PRIVATE void* resolve_member(const void* base, const simgrid::mc::Type* type, const simgrid::mc::Member* member,
- const simgrid::mc::AddressSpace* snapshot);
-
-XBT_PRIVATE
-int dwarf_register_to_libunwind(int dwarf_register);
-
-} // namespace simgrid::dwarf
-
-#endif
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Warning: autogenerated, do not edit! */
-
-#include "src/mc/inspect/mc_dwarf.hpp"
-
-#include <string>
-#include <unordered_map>
-
-namespace {
-const std::unordered_map<int, const char*> attrname_map = {
- {0x01, "DW_AT_sibling"},
- {0x02, "DW_AT_location"},
- {0x03, "DW_AT_name"},
- {0x09, "DW_AT_ordering"},
- {0x0a, "DW_AT_subscr_data"},
- {0x0b, "DW_AT_byte_size"},
- {0x0c, "DW_AT_bit_offset"},
- {0x0d, "DW_AT_bit_size"},
- {0x0f, "DW_AT_element_list"},
- {0x10, "DW_AT_stmt_list"},
- {0x11, "DW_AT_low_pc"},
- {0x12, "DW_AT_high_pc"},
- {0x13, "DW_AT_language"},
- {0x14, "DW_AT_member"},
- {0x15, "DW_AT_discr"},
- {0x16, "DW_AT_discr_value"},
- {0x17, "DW_AT_visibility"},
- {0x18, "DW_AT_import"},
- {0x19, "DW_AT_string_length"},
- {0x1a, "DW_AT_common_reference"},
- {0x1b, "DW_AT_comp_dir"},
- {0x1c, "DW_AT_const_value"},
- {0x1d, "DW_AT_containing_type"},
- {0x1e, "DW_AT_default_value"},
- {0x20, "DW_AT_inline"},
- {0x21, "DW_AT_is_optional"},
- {0x22, "DW_AT_lower_bound"},
- {0x25, "DW_AT_producer"},
- {0x27, "DW_AT_prototyped"},
- {0x2a, "DW_AT_return_addr"},
- {0x2c, "DW_AT_start_scope"},
- {0x2e, "DW_AT_bit_stride"},
- {0x2f, "DW_AT_upper_bound"},
- {0x31, "DW_AT_abstract_origin"},
- {0x32, "DW_AT_accessibility"},
- {0x33, "DW_AT_address_class"},
- {0x34, "DW_AT_artificial"},
- {0x35, "DW_AT_base_types"},
- {0x36, "DW_AT_calling_convention"},
- {0x37, "DW_AT_count"},
- {0x38, "DW_AT_data_member_location"},
- {0x39, "DW_AT_decl_column"},
- {0x3a, "DW_AT_decl_file"},
- {0x3b, "DW_AT_decl_line"},
- {0x3c, "DW_AT_declaration"},
- {0x3d, "DW_AT_discr_list"},
- {0x3e, "DW_AT_encoding"},
- {0x3f, "DW_AT_external"},
- {0x40, "DW_AT_frame_base"},
- {0x41, "DW_AT_friend"},
- {0x42, "DW_AT_identifier_case"},
- {0x43, "DW_AT_macro_info"},
- {0x44, "DW_AT_namelist_item"},
- {0x45, "DW_AT_priority"},
- {0x46, "DW_AT_segment"},
- {0x47, "DW_AT_specification"},
- {0x48, "DW_AT_static_link"},
- {0x49, "DW_AT_type"},
- {0x4a, "DW_AT_use_location"},
- {0x4b, "DW_AT_variable_parameter"},
- {0x4c, "DW_AT_virtuality"},
- {0x4d, "DW_AT_vtable_elem_location"},
- {0x4e, "DW_AT_allocated"},
- {0x4f, "DW_AT_associated"},
- {0x50, "DW_AT_data_location"},
- {0x51, "DW_AT_byte_stride"},
- {0x52, "DW_AT_entry_pc"},
- {0x53, "DW_AT_use_UTF8"},
- {0x54, "DW_AT_extension"},
- {0x55, "DW_AT_ranges"},
- {0x56, "DW_AT_trampoline"},
- {0x57, "DW_AT_call_column"},
- {0x58, "DW_AT_call_file"},
- {0x59, "DW_AT_call_line"},
- {0x5a, "DW_AT_description"},
- {0x5b, "DW_AT_binary_scale"},
- {0x5c, "DW_AT_decimal_scale"},
- {0x5d, "DW_AT_small"},
- {0x5e, "DW_AT_decimal_sign"},
- {0x5f, "DW_AT_digit_count"},
- {0x60, "DW_AT_picture_string"},
- {0x61, "DW_AT_mutable"},
- {0x62, "DW_AT_threads_scaled"},
- {0x63, "DW_AT_explicit"},
- {0x64, "DW_AT_object_pointer"},
- {0x65, "DW_AT_endianity"},
- {0x66, "DW_AT_elemental"},
- {0x67, "DW_AT_pure"},
- {0x68, "DW_AT_recursive"},
- {0x69, "DW_AT_signature"},
- {0x6a, "DW_AT_main_subprogram"},
- {0x6b, "DW_AT_data_bit_offset"},
- {0x6c, "DW_AT_const_expr"},
- {0x6d, "DW_AT_enum_class"},
- {0x6e, "DW_AT_linkage_name"},
- {0x87, "DW_AT_noreturn"},
- {0x2000, "DW_AT_lo_user"},
- {0x2001, "DW_AT_MIPS_fde"},
- {0x2002, "DW_AT_MIPS_loop_begin"},
- {0x2003, "DW_AT_MIPS_tail_loop_begin"},
- {0x2004, "DW_AT_MIPS_epilog_begin"},
- {0x2005, "DW_AT_MIPS_loop_unroll_factor"},
- {0x2006, "DW_AT_MIPS_software_pipeline_depth"},
- {0x2007, "DW_AT_MIPS_linkage_name"},
- {0x2008, "DW_AT_MIPS_stride"},
- {0x2009, "DW_AT_MIPS_abstract_name"},
- {0x200a, "DW_AT_MIPS_clone_origin"},
- {0x200b, "DW_AT_MIPS_has_inlines"},
- {0x200c, "DW_AT_MIPS_stride_byte"},
- {0x200d, "DW_AT_MIPS_stride_elem"},
- {0x200e, "DW_AT_MIPS_ptr_dopetype"},
- {0x200f, "DW_AT_MIPS_allocatable_dopetype"},
- {0x2010, "DW_AT_MIPS_assumed_shape_dopetype"},
- {0x2011, "DW_AT_MIPS_assumed_size"},
- {0x2101, "DW_AT_sf_names"},
- {0x2102, "DW_AT_src_info"},
- {0x2103, "DW_AT_mac_info"},
- {0x2104, "DW_AT_src_coords"},
- {0x2105, "DW_AT_body_begin"},
- {0x2106, "DW_AT_body_end"},
- {0x2107, "DW_AT_GNU_vector"},
- {0x2108, "DW_AT_GNU_guarded_by"},
- {0x2109, "DW_AT_GNU_pt_guarded_by"},
- {0x210a, "DW_AT_GNU_guarded"},
- {0x210b, "DW_AT_GNU_pt_guarded"},
- {0x210c, "DW_AT_GNU_locks_excluded"},
- {0x210d, "DW_AT_GNU_exclusive_locks_required"},
- {0x210e, "DW_AT_GNU_shared_locks_required"},
- {0x210f, "DW_AT_GNU_odr_signature"},
- {0x2110, "DW_AT_GNU_template_name"},
- {0x2111, "DW_AT_GNU_call_site_value"},
- {0x2112, "DW_AT_GNU_call_site_data_value"},
- {0x2113, "DW_AT_GNU_call_site_target"},
- {0x2114, "DW_AT_GNU_call_site_target_clobbered"},
- {0x2115, "DW_AT_GNU_tail_call"},
- {0x2116, "DW_AT_GNU_all_tail_call_sites"},
- {0x2117, "DW_AT_GNU_all_call_sites"},
- {0x2118, "DW_AT_GNU_all_source_call_sites"},
- {0x2119, "DW_AT_GNU_macros"},
- {0x211a, "DW_AT_GNU_deleted"},
- {0x3fff, "DW_AT_hi_user"},
-};
-}
-
-namespace simgrid::dwarf {
-
-/** @brief Get the name of an attribute (DW_AT_*) from its code
- *
- * @param attr attribute code (see the DWARF specification)
- * @return name of the attribute
- */
-XBT_PRIVATE
-const char* attrname(int attr)
-{
- auto name = attrname_map.find(attr);
- return name == attrname_map.end() ? "DW_AT_unknown" : name->second;
-}
-
-} // namespace simgrid::dwarf
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Warning: autogenerated, do not edit! */
-
-#include "src/mc/inspect/mc_dwarf.hpp"
-
-#include <string>
-#include <unordered_map>
-
-namespace {
-const std::unordered_map<int, const char*> tagname_map = {
- {0x00, "DW_TAG_invalid"},
- {0x01, "DW_TAG_array_type"},
- {0x02, "DW_TAG_class_type"},
- {0x03, "DW_TAG_entry_point"},
- {0x04, "DW_TAG_enumeration_type"},
- {0x05, "DW_TAG_formal_parameter"},
- {0x08, "DW_TAG_imported_declaration"},
- {0x0a, "DW_TAG_label"},
- {0x0b, "DW_TAG_lexical_block"},
- {0x0d, "DW_TAG_member"},
- {0x0f, "DW_TAG_pointer_type"},
- {0x10, "DW_TAG_reference_type"},
- {0x11, "DW_TAG_compile_unit"},
- {0x12, "DW_TAG_string_type"},
- {0x13, "DW_TAG_structure_type"},
- {0x15, "DW_TAG_subroutine_type"},
- {0x16, "DW_TAG_typedef"},
- {0x17, "DW_TAG_union_type"},
- {0x18, "DW_TAG_unspecified_parameters"},
- {0x19, "DW_TAG_variant"},
- {0x1a, "DW_TAG_common_block"},
- {0x1b, "DW_TAG_common_inclusion"},
- {0x1c, "DW_TAG_inheritance"},
- {0x1d, "DW_TAG_inlined_subroutine"},
- {0x1e, "DW_TAG_module"},
- {0x1f, "DW_TAG_ptr_to_member_type"},
- {0x20, "DW_TAG_set_type"},
- {0x21, "DW_TAG_subrange_type"},
- {0x22, "DW_TAG_with_stmt"},
- {0x23, "DW_TAG_access_declaration"},
- {0x24, "DW_TAG_base_type"},
- {0x25, "DW_TAG_catch_block"},
- {0x26, "DW_TAG_const_type"},
- {0x27, "DW_TAG_constant"},
- {0x28, "DW_TAG_enumerator"},
- {0x29, "DW_TAG_file_type"},
- {0x2a, "DW_TAG_friend"},
- {0x2b, "DW_TAG_namelist"},
- {0x2c, "DW_TAG_namelist_item"},
- {0x2d, "DW_TAG_packed_type"},
- {0x2e, "DW_TAG_subprogram"},
- {0x2f, "DW_TAG_template_type_parameter"},
- {0x30, "DW_TAG_template_value_parameter"},
- {0x31, "DW_TAG_thrown_type"},
- {0x32, "DW_TAG_try_block"},
- {0x33, "DW_TAG_variant_part"},
- {0x34, "DW_TAG_variable"},
- {0x35, "DW_TAG_volatile_type"},
- {0x36, "DW_TAG_dwarf_procedure"},
- {0x37, "DW_TAG_restrict_type"},
- {0x38, "DW_TAG_interface_type"},
- {0x39, "DW_TAG_namespace"},
- {0x3a, "DW_TAG_imported_module"},
- {0x3b, "DW_TAG_unspecified_type"},
- {0x3c, "DW_TAG_partial_unit"},
- {0x3d, "DW_TAG_imported_unit"},
- {0x3f, "DW_TAG_condition"},
- {0x40, "DW_TAG_shared_type"},
- {0x41, "DW_TAG_type_unit"},
- {0x42, "DW_TAG_rvalue_reference_type"},
- {0x43, "DW_TAG_template_alias"},
- {0x47, "DW_TAG_atomic_type"},
- {0x4080, "DW_TAG_lo_user"},
- {0x4081, "DW_TAG_MIPS_loop"},
- {0x4101, "DW_TAG_format_label"},
- {0x4102, "DW_TAG_function_template"},
- {0x4103, "DW_TAG_class_template"},
- {0x4104, "DW_TAG_GNU_BINCL"},
- {0x4105, "DW_TAG_GNU_EINCL"},
- {0x4106, "DW_TAG_GNU_template_template_param"},
- {0x4107, "DW_TAG_GNU_template_parameter_pack"},
- {0x4108, "DW_TAG_GNU_formal_parameter_pack"},
- {0x4109, "DW_TAG_GNU_call_site"},
- {0x410a, "DW_TAG_GNU_call_site_parameter"},
- {0xffff, "DW_TAG_hi_user"},
-};
-}
-
-namespace simgrid::dwarf {
-
-/** @brief Get the name of a dwarf tag (DW_TAG_*) from its code
- *
- * @param tag tag code (see the DWARF specification)
- * @return name of the tag
- */
-XBT_PRIVATE
-const char* tagname(int tag)
-{
- auto name = tagname_map.find(tag);
- return name == tagname_map.end() ? "DW_TAG_unknown" : name->second;
-}
-
-} // namespace simgrid::dwarf
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/inspect/Type.hpp"
-#include "src/mc/inspect/mc_dwarf.hpp"
-#include "src/mc/mc_private.hpp"
-
-namespace simgrid::dwarf {
-
-/** Resolve snapshot in the process address space
- *
- * @param object Process address of the struct/class
- * @param type Type of the struct/class
- * @param member Member description
- * @param snapshot Snapshot (or nullptr)
- * @return Process address of the given member of the 'object' struct/class
- */
-void* resolve_member(const void* base, const simgrid::mc::Type* /*type*/, const simgrid::mc::Member* member,
- const simgrid::mc::AddressSpace* address_space)
-{
- ExpressionContext state;
- state.address_space = address_space;
-
- ExpressionStack stack;
- stack.push((ExpressionStack::value_type)base);
- simgrid::dwarf::execute(member->location_expression, state, stack);
- return (void*)stack.top();
-}
-
-} // namespace simgrid::dwarf
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/** \file Libunwind support for mc_address_space objects. */
-
-// We need this for the register indices:
-// #define _GNU_SOURCE
-
-#include "src/mc/inspect/mc_unw.hpp"
-#include "src/mc/inspect/Frame.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include <cstring>
-
-// On x86_64, libunwind unw_context_t has the same layout as ucontext_t:
-#include <sys/types.h>
-#include <sys/ucontext.h>
-#ifdef __FreeBSD__
-typedef register_t greg_t;
-#endif
-
-#include <libunwind.h>
-
-namespace simgrid::mc {
-
-// ***** Implementation
-
-/** Get frame unwind information (libunwind method)
- *
- * Delegates to the local/ptrace implementation.
- */
-int UnwindContext::find_proc_info(unw_addr_space_t /*as*/, unw_word_t ip, unw_proc_info_t* pip, int need_unwind_info,
- void* arg) noexcept
-{
- const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
- return unw_get_accessors(context->process_->unw_underlying_addr_space)
- ->find_proc_info(context->process_->unw_underlying_addr_space, ip, pip, need_unwind_info,
- context->process_->unw_underlying_context);
-}
-
-/** Release frame unwind information (libunwind method)
- *
- * Delegates to the local/ptrace implementation.
- */
-void UnwindContext::put_unwind_info(unw_addr_space_t /*as*/, unw_proc_info_t* pip, void* arg) noexcept
-{
- const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
- return unw_get_accessors(context->process_->unw_underlying_addr_space)
- ->put_unwind_info(context->process_->unw_underlying_addr_space, pip, context->process_->unw_underlying_context);
-}
-
-/** (libunwind method)
- *
- * Not implemented.
- */
-int UnwindContext::get_dyn_info_list_addr(unw_addr_space_t /*as*/, unw_word_t* dilap, void* arg) noexcept
-{
- const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
- return unw_get_accessors(context->process_->unw_underlying_addr_space)
- ->get_dyn_info_list_addr(context->process_->unw_underlying_addr_space, dilap,
- context->process_->unw_underlying_context);
-}
-
-/** Read from the target address space memory (libunwind method)
- *
- * Delegates to the `simgrid::mc::Process*`.
- */
-int UnwindContext::access_mem(unw_addr_space_t /*as*/, unw_word_t addr, unw_word_t* valp, int write, void* arg) noexcept
-{
- const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
- if (write)
- return -UNW_EREADONLYREG;
- context->address_space_->read_bytes(valp, sizeof(unw_word_t), remote(addr));
- return 0;
-}
-
-void* UnwindContext::get_reg(unw_context_t* context, unw_regnum_t regnum) noexcept
-{
-#ifdef __x86_64
- mcontext_t* mcontext = &context->uc_mcontext;
- switch (regnum) {
-#ifdef __linux__
- case UNW_X86_64_RAX:
- return &mcontext->gregs[REG_RAX];
- case UNW_X86_64_RDX:
- return &mcontext->gregs[REG_RDX];
- case UNW_X86_64_RCX:
- return &mcontext->gregs[REG_RCX];
- case UNW_X86_64_RBX:
- return &mcontext->gregs[REG_RBX];
- case UNW_X86_64_RSI:
- return &mcontext->gregs[REG_RSI];
- case UNW_X86_64_RDI:
- return &mcontext->gregs[REG_RDI];
- case UNW_X86_64_RBP:
- return &mcontext->gregs[REG_RBP];
- case UNW_X86_64_RSP:
- return &mcontext->gregs[REG_RSP];
- case UNW_X86_64_R8:
- return &mcontext->gregs[REG_R8];
- case UNW_X86_64_R9:
- return &mcontext->gregs[REG_R9];
- case UNW_X86_64_R10:
- return &mcontext->gregs[REG_R10];
- case UNW_X86_64_R11:
- return &mcontext->gregs[REG_R11];
- case UNW_X86_64_R12:
- return &mcontext->gregs[REG_R12];
- case UNW_X86_64_R13:
- return &mcontext->gregs[REG_R13];
- case UNW_X86_64_R14:
- return &mcontext->gregs[REG_R14];
- case UNW_X86_64_R15:
- return &mcontext->gregs[REG_R15];
- case UNW_X86_64_RIP:
- return &mcontext->gregs[REG_RIP];
-#elif defined __FreeBSD__
- case UNW_X86_64_RAX:
- return &mcontext->mc_rax;
- case UNW_X86_64_RDX:
- return &mcontext->mc_rdx;
- case UNW_X86_64_RCX:
- return &mcontext->mc_rcx;
- case UNW_X86_64_RBX:
- return &mcontext->mc_rbx;
- case UNW_X86_64_RSI:
- return &mcontext->mc_rsi;
- case UNW_X86_64_RDI:
- return &mcontext->mc_rdi;
- case UNW_X86_64_RBP:
- return &mcontext->mc_rbp;
- case UNW_X86_64_RSP:
- return &mcontext->mc_rsp;
- case UNW_X86_64_R8:
- return &mcontext->mc_r8;
- case UNW_X86_64_R9:
- return &mcontext->mc_r9;
- case UNW_X86_64_R10:
- return &mcontext->mc_r10;
- case UNW_X86_64_R11:
- return &mcontext->mc_r11;
- case UNW_X86_64_R12:
- return &mcontext->mc_r12;
- case UNW_X86_64_R13:
- return &mcontext->mc_r13;
- case UNW_X86_64_R14:
- return &mcontext->mc_r14;
- case UNW_X86_64_R15:
- return &mcontext->mc_r15;
- case UNW_X86_64_RIP:
- return &mcontext->mc_rip;
-#else
-#error "Unable to get register from ucontext, please add your case"
-#endif
- default:
- return nullptr;
- }
-#else
- return nullptr;
-#endif
-}
-
-/** Read a standard register (libunwind method)
- */
-int UnwindContext::access_reg(unw_addr_space_t /*as*/, unw_regnum_t regnum, unw_word_t* valp, int write,
- void* arg) noexcept
-{
- auto* as_context = static_cast<simgrid::mc::UnwindContext*>(arg);
- unw_context_t* context = &as_context->unwind_context_;
- if (write)
- return -UNW_EREADONLYREG;
- const greg_t* preg = (greg_t*)get_reg(context, regnum);
- if (not preg)
- return -UNW_EBADREG;
- *valp = *preg;
- return 0;
-}
-
-/** Find information about a function (libunwind method)
- */
-int UnwindContext::get_proc_name(unw_addr_space_t /*as*/, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
- void* arg) noexcept
-{
- const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
- const simgrid::mc::Frame* frame = context->process_->find_function(remote(addr));
- if (not frame)
- return -UNW_ENOINFO;
- *offp = (unw_word_t)frame->range.begin() - addr;
-
- strncpy(bufp, frame->name.c_str(), buf_len);
- if (bufp[buf_len - 1]) {
- bufp[buf_len - 1] = 0;
- return -UNW_ENOMEM;
- }
-
- return 0;
-}
-
-// ***** Init
-
-unw_addr_space_t UnwindContext::createUnwindAddressSpace()
-{
- /** Virtual table for our `libunwind` implementation
- *
- * Stack unwinding on a `simgrid::mc::Process*` (for memory, unwinding information)
- * and `ucontext_t` (for processor registers).
- *
- * It works with the `simgrid::mc::UnwindContext` context.
- *
- * Use nullptr as access_fpreg and resume, as we don't need them.
- */
- unw_accessors_t accessors = {};
- accessors.find_proc_info = &find_proc_info;
- accessors.put_unwind_info = &put_unwind_info;
- accessors.get_dyn_info_list_addr = &get_dyn_info_list_addr;
- accessors.access_mem = &access_mem;
- accessors.access_reg = &access_reg;
- accessors.access_fpreg = nullptr;
- accessors.resume = nullptr;
- accessors.get_proc_name = &get_proc_name;
- return unw_create_addr_space(&accessors, BYTE_ORDER);
-}
-
-void UnwindContext::initialize(simgrid::mc::RemoteProcessMemory& process_memory, const unw_context_t* c)
-{
- this->address_space_ = &process_memory;
- this->process_ = &process_memory;
-
- // Take a copy of the context for our own purpose:
- this->unwind_context_ = *c;
-#if SIMGRID_PROCESSOR_x86_64 || SIMGRID_PROCESSOR_i686
-#ifdef __linux__
- // On x86_64, ucontext_t contains a pointer to itself for FP registers.
- // We don't really need support for FR registers as they are caller saved
- // and probably never use those fields as libunwind-x86_64 does not read
- // FP registers from the unw_context_t
- // Let's ignore this and see what happens:
- this->unwind_context_.uc_mcontext.fpregs = nullptr;
-#endif
-#elif SIMGRID_PROCESSOR_arm64
-#ifdef __linux__
- // On ARM64, ucontext_t doesn't contain `fpregs` and the FP registers
- // are instead held in the `__reserved` field of the struct. It doesn't
- // appear anything needs to be done here, although this should be verified
-#endif
-#else
- // Do we need to do any fixup like this?
-#error Target CPU type is not handled.
-#endif
-}
-
-unw_cursor_t UnwindContext::cursor()
-{
- unw_cursor_t cursor;
- xbt_assert(process_ != nullptr && address_space_ != nullptr &&
- unw_init_remote(&cursor, process_->unw_addr_space, this) == 0,
- "UnwindContext not initialized");
- return cursor;
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_UNW_HPP
-#define SIMGRID_MC_UNW_HPP
-
-/** @file
- * Libunwind implementation for the model-checker
- *
- * Libunwind provides a pluggable stack unwinding API: the way the current
- * registers and memory is accessed, the way unwinding information is found
- * is pluggable.
- *
- * This component implements the libunwind API for he model-checker:
- *
- * * reading memory from a simgrid::mc::AddressSpace*;
- *
- * * reading stack registers from a saved snapshot (context).
- *
- * Parts of the libunwind information fetching is currently handled by the
- * standard `libunwind` implementations (either the local one or the ptrace one)
- * because parsing `.eh_frame` section is not fun and `libdw` does not help
- * much here.
- */
-
-#include "src/mc/mc_forward.hpp"
-#include "xbt/base.h"
-
-#include <cstdio>
-#include <libunwind.h>
-
-namespace simgrid::unw {
-
-XBT_PRIVATE unw_addr_space_t create_addr_space();
-XBT_PRIVATE void* create_context(unw_addr_space_t as, pid_t pid);
-} // namespace simgrid::unw
-
-namespace simgrid::mc {
-
-class UnwindContext {
- simgrid::mc::AddressSpace* address_space_ = nullptr;
- simgrid::mc::RemoteProcessMemory* process_ = nullptr;
- unw_context_t unwind_context_ = {};
-
-public:
- void initialize(simgrid::mc::RemoteProcessMemory& process, const unw_context_t* c);
- unw_cursor_t cursor();
-
-private: // Methods and virtual table for libunwind
- static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t* pip, int need_unwind_info,
- void* arg) noexcept;
- static void put_unwind_info(unw_addr_space_t as, unw_proc_info_t* pip, void* arg) noexcept;
- static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t* dilap, void* arg) noexcept;
- static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t* valp, int write, void* arg) noexcept;
- static void* get_reg(unw_context_t* context, unw_regnum_t regnum) noexcept;
- static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t* valp, int write, void* arg) noexcept;
- static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
- void* arg) noexcept;
-
-public:
- // Create a libunwind address space:
- static unw_addr_space_t createUnwindAddressSpace();
-};
-
-void dumpStack(FILE* file, unw_cursor_t* cursor);
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/** \file Libunwind namespace implementation using process_vm_readv. */
-
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/inspect/mc_unw.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include <sys/types.h>
-#include <sys/uio.h>
-
-#include <fcntl.h>
-#include <libunwind-ptrace.h>
-#include <libunwind.h>
-
-
-/** Partial structure of libunwind-ptrace context in order to get the PID
- *
- * HACK, The context type for libunwind-race is an opaque type.
- * We need to get the PID which is the first field. This is a hack
- * which might break if the libunwind-ptrace structure changes.
- */
-struct _UPT_info {
- pid_t pid;
- // Other things...
-};
-
-/** Read from the memory, avoid using `ptrace` (libunwind method) */
-static int access_mem(const unw_addr_space_t as, const unw_word_t addr, unw_word_t* const valp, const int write,
- void* const arg)
-{
- if (write)
- return -UNW_EINVAL;
- pid_t pid = static_cast<_UPT_info*>(arg)->pid;
- size_t size = sizeof(unw_word_t);
-
-#if HAVE_PROCESS_VM_READV /* linux but not freebsd */
- // process_vm_read implementation.
- // This is only available since Linux 3.2.
-
- struct iovec local = {valp, size};
- struct iovec remote = {(void*)addr, size};
- if (ssize_t s = process_vm_readv(pid, &local, 1, &remote, 1, 0); s >= 0) {
- if ((size_t)s != size)
- return -UNW_EINVAL;
- else
- return 0;
- } else if (errno != ENOSYS) {
- return -UNW_EINVAL;
- }
-#endif
-
- // /proc/${pid}/mem implementation.
- // On recent kernels, we do not need to ptrace the target process.
- // On older kernels, it is necessary to ptrace the target process.
- size_t count = size;
- auto off = static_cast<off_t>(addr);
- auto* buf = reinterpret_cast<std::byte*>(valp);
- int fd = simgrid::mc::open_vm(pid, O_RDONLY);
- if (fd < 0)
- return -UNW_EINVAL;
-
- while (count > 0) {
- ssize_t nread = pread(fd, buf, count, off);
- if (nread == 0) {
- close(fd);
- return -UNW_EINVAL;
- }
- if (nread == -1)
- // ptrace implementation.
- // We need to have PTRACE_ATTACH-ed it before.
- return _UPT_access_mem(as, addr, valp, write, arg);
-
- count -= nread;
- buf += nread;
- off += nread;
- }
- close(fd);
- return 0;
-}
-
-namespace simgrid::unw {
-
-unw_addr_space_t create_addr_space()
-{
- /** Virtual table for our `libunwind-process_vm_readv` implementation.
- *
- * This implementation reuse most the code of `libunwind-ptrace` but
- * does not use ptrace() to read the target process memory by
- * `process_vm_readv()` or `/dev/${pid}/mem` if possible.
- *
- * Does not support any MC-specific behavior (privatization, snapshots)
- * and `ucontext_t`.
- *
- * It works with `void*` contexts allocated with `_UPT_create(pid)`.
- */
- // TODO, we could get rid of this if we properly stop the model-checked
- // process before reading the memory.
- unw_accessors_t accessors = _UPT_accessors;
- accessors.access_mem = &access_mem;
- return unw_create_addr_space(&accessors, BYTE_ORDER);
-}
-
-void* create_context(unw_addr_space_t /*as*/, pid_t pid)
-{
- return _UPT_create(pid);
-}
-
-} // namespace simgrid::unw
#include "src/mc/mc_config.hpp"
#include "src/mc/mc_replay.hpp"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-#endif
-
XBT_LOG_NEW_DEFAULT_CATEGORY(mc, "All MC categories");
bool simgrid_mc_replay_show_backtraces = false;
return get_model_checking_mode() == ModelCheckingMode::APP_SIDE ||
get_model_checking_mode() == ModelCheckingMode::CHECKER_SIDE;
}
-
-void MC_automaton_new_propositional_symbol_pointer(const char *name, int* value)
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
- "This should be called from the client side");
- if (MC_is_active())
- AppSide::get()->declare_symbol(name, value);
-#endif
-}
-
-void MC_ignore(void* addr, size_t size)
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
- "This should be called from the client side");
- if (MC_is_active())
- AppSide::get()->ignore_memory(addr, size);
-#endif
-}
-
-void MC_unignore(void* addr, size_t size)
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
- "This should be called from the client side");
- if (MC_is_active())
- AppSide::get()->unignore_memory(addr, size);
-#endif
-}
-
-void MC_ignore_heap(void *address, size_t size)
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
- "This should be called from the client side");
- if (MC_is_active())
- AppSide::get()->ignore_heap(address, size);
-#endif
-}
-
-void MC_unignore_heap(void* address, size_t size)
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- xbt_assert(get_model_checking_mode() != ModelCheckingMode::CHECKER_SIDE,
- "This should be called from the client side");
- if (MC_is_active())
- AppSide::get()->unignore_heap(address, size);
-#endif
-}
#include "src/simgrid/sg_config.hpp"
#include <simgrid/modelchecker.h>
-#if SIMGRID_HAVE_STATEFUL_MC
-#include <string_view>
-#endif
-
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(xbt_cfg);
static simgrid::mc::ModelCheckingMode model_checking_mode = simgrid::mc::ModelCheckingMode::NONE;
_mc_cfg_cb_check("value to enable/disable timeout for wait requests", not MC_record_replay_is_active());
}};
-int _sg_mc_max_visited_states = 0;
-
static simgrid::config::Flag<std::string> cfg_mc_reduction{
"model-check/reduction", "Specify the kind of exploration reduction (either none or DPOR)", "dpor",
[](std::string_view value) {
"give a specific random seed to initialize the uniform distribution", 0,
[](int) { _mc_cfg_cb_check("Random seed"); }};
-#if SIMGRID_HAVE_STATEFUL_MC
-simgrid::config::Flag<int> _sg_mc_checkpoint{
- "model-check/checkpoint", "Specify the amount of steps between checkpoints during stateful model-checking "
- "(default: 0 => stateless verification). If value=1, one checkpoint is saved for each "
- "step => faster verification, but huge memory consumption; higher values are good "
- "compromises between speed and memory consumption.",
- 0, [](int) { _mc_cfg_cb_check("checkpointing value"); }};
-
-simgrid::config::Flag<std::string> _sg_mc_property_file{
- "model-check/property", "Name of the file containing the property, as formatted by the ltl2ba program.", "",
- [](const std::string&) { _mc_cfg_cb_check("property"); }};
-
simgrid::config::Flag<bool> _sg_mc_comms_determinism{
- "model-check/communications-determinism",
- "Whether to enable the detection of communication determinism",
- false,
+ "model-check/communications-determinism", "Whether to enable the detection of communication determinism", false,
[](bool) {
_mc_cfg_cb_check("value to enable/disable the detection of determinism in the communications schemes");
}};
-
simgrid::config::Flag<bool> _sg_mc_send_determinism{
"model-check/send-determinism",
"Enable/disable the detection of send-determinism in the communications schemes",
"Whether to enable the unfolding-based dynamic partial order reduction to MPI programs", false, [](bool) {
_mc_cfg_cb_check("value to to enable/disable the unfolding-based dynamic partial order reduction to MPI programs");
}};
-#endif
simgrid::config::Flag<std::string> _sg_mc_buffering{
"smpi/buffering",
1000,
[](int) { _mc_cfg_cb_check("max depth value"); }};
-static simgrid::config::Flag<int> _sg_mc_max_visited_states__{
- "model-check/visited",
- "Specify the number of visited state stored for state comparison reduction: any branch leading to a state that is "
- "already stored is cut.\n"
- "If value=5, the last 5 visited states are stored. If value=0 (the default), no state is stored and this reduction "
- "technique is disabled.",
- 0, [](int value) {
- _mc_cfg_cb_check("number of stored visited states");
- _sg_mc_max_visited_states = value;
- }};
-
-simgrid::config::Flag<bool> _sg_mc_termination{
- "model-check/termination", "Whether to enable non progressive cycle detection", false,
- [](bool) { _mc_cfg_cb_check("value to enable/disable the detection of non progressive cycles"); }};
-
simgrid::mc::ReductionMode simgrid::mc::get_model_checking_reduction()
{
- if ((cfg_mc_reduction.get() == "dpor" || cfg_mc_reduction.get() == "sdpor" || cfg_mc_reduction.get() == "odpor") &&
- _sg_mc_max_visited_states__ > 0) {
- XBT_INFO("Disabling DPOR since state-equality reduction is activated with 'model-check/visited'");
- return simgrid::mc::ReductionMode::none;
- }
-
if (cfg_mc_reduction.get() == "none") {
return ReductionMode::none;
} else if (cfg_mc_reduction.get() == "dpor") {
extern XBT_PRIVATE simgrid::config::Flag<bool> _sg_mc_timeout;
extern XBT_PRIVATE simgrid::config::Flag<int> _sg_mc_max_depth;
extern XBT_PRIVATE simgrid::config::Flag<int> _sg_mc_random_seed;
-extern "C" XBT_PUBLIC int _sg_mc_max_visited_states;
extern XBT_PRIVATE simgrid::config::Flag<std::string> _sg_mc_dot_output_file;
-extern XBT_PRIVATE simgrid::config::Flag<bool> _sg_mc_termination;
extern XBT_PUBLIC simgrid::config::Flag<std::string> _sg_mc_strategy;
#endif
*/
#define MC_ENV_SOCKET_FD "SIMGRID_MC_SOCKET_FD"
-/** Environment variable name defined when ptrace(2) is used to control the MCed process.
- */
-#define MC_ENV_NEED_PTRACE "SIMGRID_MC_NEED_PTRACE"
-
/** Environment variable used to request additional system statistics.
*/
#define MC_ENV_SYSTEM_STATISTICS "SIMGRID_MC_SYSTEM_STATISTICS"
#include "src/kernel/actor/ActorImpl.hpp"
#include "src/mc/mc.h"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/api/RemoteApp.hpp"
-#include "src/mc/explo/Exploration.hpp"
-#include "src/mc/inspect/mc_unw.hpp"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_private.hpp"
-#include "src/mc/remote/AppSide.hpp"
-#include "src/mc/sosp/Snapshot.hpp"
-
-#include <array>
-#include <boost/core/demangle.hpp>
-#include <cerrno>
-#include <cstring>
-#include <libunwind.h>
-#endif
-
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
}
-#if SIMGRID_HAVE_STATEFUL_MC
-
-namespace simgrid::mc {
-
-/******************************* Core of MC *******************************/
-/**************************************************************************/
-void dumpStack(FILE* file, unw_cursor_t* cursor)
-{
- int nframe = 0;
- std::array<char, 100> buffer;
-
- unw_word_t off;
- do {
- const char* name = not unw_get_proc_name(cursor, buffer.data(), buffer.size(), &off) ? buffer.data() : "?";
- // Unmangle C++ names:
- std::string realname = boost::core::demangle(name);
-
-#if defined(__x86_64__)
- unw_word_t rip = 0;
- unw_word_t rsp = 0;
- unw_get_reg(cursor, UNW_X86_64_RIP, &rip);
- unw_get_reg(cursor, UNW_X86_64_RSP, &rsp);
- fprintf(file, " %i: %s (RIP=0x%" PRIx64 " RSP=0x%" PRIx64 ")\n", nframe, realname.c_str(), (std::uint64_t)rip,
- (std::uint64_t)rsp);
-#else
- fprintf(file, " %i: %s\n", nframe, realname.c_str());
-#endif
-
- ++nframe;
- } while (unw_step(cursor));
-}
-
-} // namespace simgrid::mc
-#endif
-
double MC_process_clock_get(const simgrid::kernel::actor::ActorImpl* process)
{
if (process) {
#include "src/mc/mc_base.hpp"
#include "src/mc/mc_config.hpp"
#include "src/mc/mc_environ.h"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-#endif
#if HAVE_SMPI
#include "src/smpi/include/private.hpp"
#endif
if (std::getenv(MC_ENV_SOCKET_FD) == nullptr) // We are not in MC mode: don't initialize the MC world
return nullptr;
- XBT_DEBUG("Initialize the MC world. %s=%s", MC_ENV_NEED_PTRACE, std::getenv(MC_ENV_NEED_PTRACE));
+ XBT_DEBUG("Initialize the MC world.");
simgrid::mc::set_model_checking_mode(ModelCheckingMode::APP_SIDE);
instance_ = std::make_unique<simgrid::mc::AppSide>(fd);
- // Wait for the model-checker:
- if (getenv(MC_ENV_NEED_PTRACE) != nullptr) {
- errno = 0;
-#if defined __linux__
- ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);
-#elif defined BSD
- ptrace(PT_TRACE_ME, 0, nullptr, 0);
-#else
- xbt_die("no ptrace equivalent coded for this platform, please don't use the liveness checker here.");
-#endif
-
- xbt_assert(errno == 0 && raise(SIGSTOP) == 0, "Could not wait for the model-checker (errno = %d: %s)", errno,
- strerror(errno));
- }
-
instance_->handle_messages();
return instance_.get();
}
answer.value = status;
xbt_assert(channel_.send(answer) == 0, "Could not send response to WAIT_CHILD: %s", strerror(errno));
}
-void AppSide::handle_need_meminfo()
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- this->need_memory_info_ = true;
- s_mc_message_need_meminfo_reply_t answer = {};
- answer.type = MessageType::NEED_MEMINFO_REPLY;
- answer.mmalloc_default_mdp = mmalloc_get_current_heap();
- xbt_assert(channel_.send(answer) == 0, "Could not send response to the request for meminfo: %s", strerror(errno));
-#else
- xbt_die("SimGrid was compiled without MC suppport, so liveness and similar features are not available.");
-#endif
-}
void AppSide::handle_actors_status() const
{
auto const& actor_list = kernel::EngineImpl::get_instance()->get_actor_list();
handle_wait_child((s_mc_message_int_t*)message_buffer.data());
break;
- case MessageType::NEED_MEMINFO:
- assert_msg_size("NEED_MEMINFO", s_mc_message_t);
- handle_need_meminfo();
- break;
-
case MessageType::ACTORS_STATUS:
assert_msg_size("ACTORS_STATUS", s_mc_message_t);
handle_actors_status();
void AppSide::main_loop()
{
simgrid::mc::processes_time.resize(simgrid::kernel::actor::ActorImpl::get_maxpid());
- MC_ignore_heap(simgrid::mc::processes_time.data(),
- simgrid::mc::processes_time.size() * sizeof(simgrid::mc::processes_time[0]));
- kernel::activity::CommImpl::setup_mc();
sthread_disable();
coverage_checkpoint();
this->handle_messages();
}
-void AppSide::ignore_memory(void* addr, std::size_t size) const
-{
- if (not MC_is_active() || not need_memory_info_)
- return;
-
-#if SIMGRID_HAVE_STATEFUL_MC
- s_mc_message_ignore_memory_t message = {};
- message.type = MessageType::IGNORE_MEMORY;
- message.addr = (std::uintptr_t)addr;
- message.size = size;
- xbt_assert(channel_.send(message) == 0, "Could not send IGNORE_MEMORY message to model-checker: %s", strerror(errno));
-#else
- xbt_die("Cannot really call ignore_memory() in non-SIMGRID_MC mode.");
-#endif
-}
-
-void AppSide::unignore_memory(void* addr, std::size_t size) const
-{
- if (not MC_is_active() || not need_memory_info_)
- return;
-
-#if SIMGRID_HAVE_STATEFUL_MC
- s_mc_message_ignore_memory_t message = {};
- message.type = MessageType::UNIGNORE_MEMORY;
- message.addr = (std::uintptr_t)addr;
- message.size = size;
- xbt_assert(channel_.send(message) == 0, "Could not send UNIGNORE_MEMORY message to model-checker: %s",
- strerror(errno));
-#else
- xbt_die("Cannot really call unignore_memory() in non-SIMGRID_MC mode.");
-#endif
-}
-
-void AppSide::ignore_heap(void* address, std::size_t size) const
-{
- if (not MC_is_active() || not need_memory_info_)
- return;
-
-#if SIMGRID_HAVE_STATEFUL_MC
- const s_xbt_mheap_t* heap = mmalloc_get_current_heap();
-
- s_mc_message_ignore_heap_t message = {};
- message.type = MessageType::IGNORE_HEAP;
- message.address = address;
- message.size = size;
- message.block = ((char*)address - (char*)heap->heapbase) / BLOCKSIZE + 1;
- if (heap->heapinfo[message.block].type == 0) {
- message.fragment = -1;
- heap->heapinfo[message.block].busy_block.ignore++;
- } else {
- message.fragment = (ADDR2UINT(address) % BLOCKSIZE) >> heap->heapinfo[message.block].type;
- heap->heapinfo[message.block].busy_frag.ignore[message.fragment]++;
- }
-
- xbt_assert(channel_.send(message) == 0, "Could not send ignored region to MCer: %s", strerror(errno));
-#else
- xbt_die("Cannot really call ignore_heap() in non-SIMGRID_MC mode.");
-#endif
-}
-
-void AppSide::unignore_heap(void* address, std::size_t size) const
-{
- if (not MC_is_active() || not need_memory_info_)
- return;
-
-#if SIMGRID_HAVE_STATEFUL_MC
- s_mc_message_ignore_memory_t message = {};
- message.type = MessageType::UNIGNORE_HEAP;
- message.addr = (std::uintptr_t)address;
- message.size = size;
- xbt_assert(channel_.send(message) == 0, "Could not send IGNORE_HEAP message to model-checker: %s", strerror(errno));
-#else
- xbt_die("Cannot really call unignore_heap() in non-SIMGRID_MC mode.");
-#endif
-}
-
-void AppSide::declare_symbol(const char* name, int* value) const
-{
- if (not MC_is_active() || not need_memory_info_) {
- XBT_CRITICAL("Ignore AppSide::declare_symbol(%s)", name);
- return;
- }
-
-#if SIMGRID_HAVE_STATEFUL_MC
- s_mc_message_register_symbol_t message = {};
- message.type = MessageType::REGISTER_SYMBOL;
- xbt_assert(strlen(name) + 1 <= message.name.size(), "Symbol is too long");
- strncpy(message.name.data(), name, message.name.size() - 1);
- message.callback = nullptr;
- message.data = value;
- xbt_assert(channel_.send(message) == 0, "Could send REGISTER_SYMBOL message to model-checker: %s", strerror(errno));
-#else
- xbt_die("Cannot really call declare_symbol() in non-SIMGRID_MC mode.");
-#endif
-}
-
-/** Register a stack in the model checker
- *
- * The stacks are allocated in the heap. The MC handle them specifically
- * when we analyze/compare the content of the heap so it must be told where
- * they are with this function.
- */
-#if HAVE_UCONTEXT_H /* Apple don't want us to use ucontexts */
-void AppSide::declare_stack(void* stack, size_t size, ucontext_t* context) const
-{
- if (not MC_is_active() || not need_memory_info_)
- return;
-
-#if SIMGRID_HAVE_STATEFUL_MC
- const s_xbt_mheap_t* heap = mmalloc_get_current_heap();
-
- s_stack_region_t region = {};
- region.address = stack;
- region.context = context;
- region.size = size;
- region.block = ((char*)stack - (char*)heap->heapbase) / BLOCKSIZE + 1;
-
- s_mc_message_stack_region_t message = {};
- message.type = MessageType::STACK_REGION;
- message.stack_region = region;
- xbt_assert(channel_.send(message) == 0, "Could not send STACK_REGION to model-checker: %s", strerror(errno));
-#else
- xbt_die("Cannot really call declare_stack() in non-SIMGRID_MC mode.");
-#endif
-}
-#endif
-
} // namespace simgrid::mc
private:
Channel channel_;
static std::unique_ptr<AppSide> instance_;
- bool need_memory_info_ = false; /* by default we don't send memory info, unless we got a NEED_MEMINFO */
std::unordered_map<int, int> child_statuses_;
public:
void handle_finalize(const s_mc_message_int_t* msg) const;
void handle_fork(const s_mc_message_fork_t* msg);
void handle_wait_child(const s_mc_message_int_t* msg);
- void handle_need_meminfo();
void handle_actors_status() const;
void handle_actors_maxpid() const;
Channel& get_channel() { return channel_; }
XBT_ATTRIB_NORETURN void main_loop();
void report_assertion_failure();
- void ignore_memory(void* addr, std::size_t size) const;
- void unignore_memory(void* addr, std::size_t size) const;
- void ignore_heap(void* addr, std::size_t size) const;
- void unignore_heap(void* addr, std::size_t size) const;
- void declare_symbol(const char* name, int* value) const;
-#if HAVE_UCONTEXT_H
- void declare_stack(void* stack, size_t size, ucontext_t* context) const;
-#endif
// TODO, remove the singleton antipattern.
static AppSide* get();
#include "xbt/config.hpp"
#include "xbt/system_error.hpp"
-#if SIMGRID_HAVE_STATEFUL_MC
-#include "src/mc/explo/LivenessChecker.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-#endif
-
#ifdef __linux__
#include <sys/prctl.h>
#endif
namespace simgrid::mc {
-XBT_ATTRIB_NORETURN static void run_child_process(int socket, const std::vector<char*>& args, bool need_ptrace)
+XBT_ATTRIB_NORETURN static void run_child_process(int socket, const std::vector<char*>& args)
{
/* On startup, simix_global_init() calls simgrid::mc::Client::initialize(), which checks whether the MC_ENV_SOCKET_FD
* env variable is set. If so, MC mode is assumed, and the client is setup from its side
#endif
setenv(MC_ENV_SOCKET_FD, std::to_string(socket).c_str(), 1);
- if (need_ptrace)
- setenv(MC_ENV_NEED_PTRACE, "1", 1);
/* Setup the tokenizer that parses the cfg:model-check/setenv parameter */
using Tokenizer = boost::tokenizer<boost::char_separator<char>>;
}
/* When this constructor is called, no other checkerside exists */
-CheckerSide::CheckerSide(const std::vector<char*>& args, bool need_memory_info) : running_(true)
+CheckerSide::CheckerSide(const std::vector<char*>& args) : running_(true)
{
- XBT_DEBUG("Create a CheckerSide. Needs_meminfo: %s", need_memory_info ? "YES" : "no");
+ XBT_DEBUG("Create a CheckerSide.");
// Create an AF_UNIX socketpair used for exchanging messages between the model-checker process (ancestor)
// and the application process (child)
if (pid_ == 0) { // Child
::close(sockets[1]);
- run_child_process(sockets[0], args, need_memory_info); // We need ptrace if we need the mem info
+ run_child_process(sockets[0], args);
DIE_IMPOSSIBLE;
}
channel_.reset_socket(sockets[1]);
setup_events(false); /* we need a signal handler too */
- if (need_memory_info) {
-#if SIMGRID_HAVE_STATEFUL_MC
- // setup ptrace and sync with the app
- wait_application_process(pid_);
-
- // Request the initial memory on need
- channel_.send(MessageType::NEED_MEMINFO);
- s_mc_message_need_meminfo_reply_t answer;
- ssize_t answer_size = channel_.receive(answer);
- xbt_assert(answer_size != -1, "Could not receive message");
- xbt_assert(answer.type == MessageType::NEED_MEMINFO_REPLY,
- "The received message is not the NEED_MEMINFO_REPLY I was expecting but of type %s",
- to_c_str(answer.type));
- xbt_assert(answer_size == sizeof answer, "Broken message (size=%zd; expected %zu)", answer_size, sizeof answer);
-
- /* We now have enough info to create the memory address space */
- remote_memory_ = std::make_unique<simgrid::mc::RemoteProcessMemory>(pid_, answer.mmalloc_default_mdp);
-#else
- xbt_die("Cannot introspect memory without MC support");
-#endif
- }
-
wait_for_requests();
}
memcpy(&base_message, buffer, sizeof(base_message));
switch (base_message.type) {
- case MessageType::IGNORE_HEAP: {
- consumed = sizeof(s_mc_message_ignore_heap_t);
-#if SIMGRID_HAVE_STATEFUL_MC
- if (remote_memory_ != nullptr) {
- s_mc_message_ignore_heap_t message;
- xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
- memcpy(&message, buffer, sizeof(message));
-
- IgnoredHeapRegion region;
- region.block = message.block;
- region.fragment = message.fragment;
- region.address = message.address;
- region.size = message.size;
- get_remote_memory()->ignore_heap(region);
- } else
-#endif
- XBT_INFO("Ignoring a IGNORE_HEAP message because we don't need to introspect memory.");
- break;
- }
-
- case MessageType::UNIGNORE_HEAP: {
- consumed = sizeof(s_mc_message_ignore_memory_t);
-#if SIMGRID_HAVE_STATEFUL_MC
- if (remote_memory_ != nullptr) {
- s_mc_message_ignore_memory_t message;
- xbt_assert(size == static_cast<ssize_t>(sizeof(message)), "Broken message");
- memcpy(&message, buffer, sizeof(message));
- get_remote_memory()->unignore_heap((void*)message.addr, message.size);
- } else
-#endif
- XBT_INFO("Ignoring an UNIGNORE_HEAP message because we don't need to introspect memory.");
- break;
- }
-
- case MessageType::IGNORE_MEMORY: {
- consumed = sizeof(s_mc_message_ignore_memory_t);
-#if SIMGRID_HAVE_STATEFUL_MC
- if (remote_memory_ != nullptr) {
- s_mc_message_ignore_memory_t message;
- xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
- memcpy(&message, buffer, sizeof(message));
- get_remote_memory()->ignore_region(message.addr, message.size);
- } else
-#endif
- XBT_INFO("Ignoring an IGNORE_MEMORY message because we don't need to introspect memory.");
- break;
- }
-
- case MessageType::UNIGNORE_MEMORY: {
- consumed = sizeof(s_mc_message_ignore_memory_t);
-#if SIMGRID_HAVE_STATEFUL_MC
- if (remote_memory_ != nullptr) {
- s_mc_message_ignore_memory_t message;
- xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
- memcpy(&message, buffer, sizeof(message));
- get_remote_memory()->unignore_region(message.addr, message.size);
- } else
-#endif
- XBT_INFO("Ignoring an UNIGNORE_MEMORY message because we don't need to introspect memory.");
- break;
- }
-
- case MessageType::STACK_REGION: {
- consumed = sizeof(s_mc_message_stack_region_t);
-#if SIMGRID_HAVE_STATEFUL_MC
- if (remote_memory_ != nullptr) {
- s_mc_message_stack_region_t message;
- xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
- memcpy(&message, buffer, sizeof(message));
- get_remote_memory()->stack_areas().push_back(message.stack_region);
- } else
-#endif
- XBT_INFO("Ignoring an STACK_REGION message because we don't need to introspect memory.");
- break;
- }
-
- case MessageType::REGISTER_SYMBOL: {
- consumed = sizeof(s_mc_message_register_symbol_t);
-#if SIMGRID_HAVE_STATEFUL_MC
- s_mc_message_register_symbol_t message;
- xbt_assert(size >= static_cast<ssize_t>(sizeof(message)), "Broken message");
- memcpy(&message, buffer, sizeof(message));
- xbt_assert(not message.callback, "Support for client-side function proposition is not implemented.");
- XBT_DEBUG("Received symbol: %s", message.name.data());
-
- LivenessChecker::automaton_register_symbol(*get_remote_memory(), message.name.data(), remote((int*)message.data));
-#else
- xbt_die("Please don't use liveness properties when MC is compiled out.");
-#endif
- break;
- }
case MessageType::WAITING:
consumed = sizeof(s_mc_message_t);
XBT_DEBUG("Resume the application");
if (get_channel().send(MessageType::CONTINUE) != 0)
throw xbt::errno_error();
- clear_memory_cache();
if (running())
dispatch_events();
}
-void CheckerSide::clear_memory_cache()
-{
-#if SIMGRID_HAVE_STATEFUL_MC
- if (remote_memory_)
- remote_memory_->clear_cache();
-#endif
-}
-
void CheckerSide::handle_dead_child(int status)
{
// From PTRACE_O_TRACEEXIT:
event* socket_event_;
event* signal_event_;
std::unique_ptr<event_base, decltype(&event_base_free)> base_{nullptr, &event_base_free};
-#if SIMGRID_HAVE_STATEFUL_MC
- std::unique_ptr<RemoteProcessMemory> remote_memory_;
-#endif
Channel channel_;
bool running_ = false;
CheckerSide* child_checker_ = nullptr;
void setup_events(bool socket_only); // Part of the initialization
- void clear_memory_cache();
void handle_dead_child(int status); // Launched when the dying child is the PID we follow
void handle_waitpid(); // Launched when receiving a sigchild
public:
explicit CheckerSide(int socket, CheckerSide* child_checker);
- explicit CheckerSide(const std::vector<char*>& args, bool need_memory_introspection);
+ explicit CheckerSide(const std::vector<char*>& args);
~CheckerSide();
// No copy:
pid_t get_pid() const { return pid_; }
bool running() const { return running_; }
void terminate() { running_ = false; }
-#if SIMGRID_HAVE_STATEFUL_MC
- RemoteProcessMemory* get_remote_memory() { return remote_memory_.get(); }
-#endif
};
} // namespace simgrid::mc
#include "simgrid/forward.h" // aid_t
#include "src/mc/datatypes.h"
-#include "src/xbt/mmalloc/mmalloc.h"
#include <xbt/utility.hpp>
#include <array>
// ***** Messages
namespace simgrid::mc {
-XBT_DECLARE_ENUM_CLASS(MessageType, NONE, NEED_MEMINFO, NEED_MEMINFO_REPLY, FORK, FORK_REPLY, WAIT_CHILD,
- WAIT_CHILD_REPLY, CONTINUE, IGNORE_HEAP, UNIGNORE_HEAP, IGNORE_MEMORY, UNIGNORE_MEMORY,
- STACK_REGION, REGISTER_SYMBOL, DEADLOCK_CHECK, DEADLOCK_CHECK_REPLY, WAITING, SIMCALL_EXECUTE,
- SIMCALL_EXECUTE_REPLY, ASSERTION_FAILED, ACTORS_STATUS, ACTORS_STATUS_REPLY_COUNT,
- ACTORS_STATUS_REPLY_SIMCALL, ACTORS_STATUS_REPLY_TRANSITION, ACTORS_MAXPID, ACTORS_MAXPID_REPLY,
- FINALIZE, FINALIZE_REPLY);
+XBT_DECLARE_ENUM_CLASS(MessageType, NONE, FORK, FORK_REPLY, WAIT_CHILD, WAIT_CHILD_REPLY, CONTINUE, DEADLOCK_CHECK,
+ DEADLOCK_CHECK_REPLY, WAITING, SIMCALL_EXECUTE, SIMCALL_EXECUTE_REPLY, ASSERTION_FAILED,
+ ACTORS_STATUS, ACTORS_STATUS_REPLY_COUNT, ACTORS_STATUS_REPLY_SIMCALL,
+ ACTORS_STATUS_REPLY_TRANSITION, ACTORS_MAXPID, ACTORS_MAXPID_REPLY, FINALIZE, FINALIZE_REPLY);
} // namespace simgrid::mc
constexpr unsigned MC_MESSAGE_LENGTH = 512;
};
/* Client->Server */
-struct s_mc_message_ignore_heap_t {
- simgrid::mc::MessageType type;
- int block;
- int fragment;
- void* address;
- size_t size;
-};
-
-struct s_mc_message_ignore_memory_t {
- simgrid::mc::MessageType type;
- uint64_t addr;
- size_t size;
-};
-
-struct s_mc_message_stack_region_t {
- simgrid::mc::MessageType type;
- s_stack_region_t stack_region;
-};
-
-struct s_mc_message_register_symbol_t {
- simgrid::mc::MessageType type;
- std::array<char, 128> name;
- int (*callback)(void*);
- void* data;
-};
/* Server -> client */
-struct s_mc_message_need_meminfo_reply_t {
- simgrid::mc::MessageType type;
- xbt_mheap_t mmalloc_default_mdp;
-};
-
struct s_mc_message_fork_t {
simgrid::mc::MessageType type;
std::array<char, MC_SOCKET_NAME_LEN> socket_name;
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/AddressSpace.hpp"
-#include "src/mc/sosp/ChunkedData.hpp"
-
-namespace simgrid::mc {
-
-/** Take a per-page snapshot of a region
- *
- * @param addr The start of the region (must be at the beginning of a page)
- * @param page_count Number of pages of the region
- * @return Snapshot page numbers of this new snapshot
- */
-ChunkedData::ChunkedData(PageStore& store, const AddressSpace& as, RemotePtr<void> addr, std::size_t page_count)
- : store_(&store)
-{
- this->pagenos_.resize(page_count);
- std::vector<char> buffer(xbt_pagesize);
-
- for (size_t i = 0; i != page_count; ++i) {
- RemotePtr<void> page = remote((void*)simgrid::mc::mmu::join(i, addr.address()));
- xbt_assert(simgrid::mc::mmu::split(page.address()).second == 0, "Not at the beginning of a page");
-
- /* Adding another copy (and a syscall) will probably slow things a lot.
- TODO, optimize this somehow (at least by grouping the syscalls)
- if needed. Either:
- - reduce the number of syscalls
- - let the application snapshot itself
- - move the segments in shared memory (this will break `fork` however)
- */
-
- as.read_bytes(buffer.data(), xbt_pagesize, page);
-
- pagenos_[i] = store_->store_page(buffer.data());
- }
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_CHUNKED_DATA_HPP
-#define SIMGRID_MC_CHUNKED_DATA_HPP
-
-#include <vector>
-
-#include "src/mc/mc_forward.hpp"
-#include "src/mc/remote/RemotePtr.hpp"
-#include "src/mc/sosp/PageStore.hpp"
-
-namespace simgrid::mc {
-
-/** A byte-string represented as a sequence of chunks from a PageStore
- *
- * In order to save memory when taking memory snapshots, a given byte-string
- * is split in fixed-size chunks. Identical chunks (either from the same
- * snapshot or more probably from different snapshots) share the same memory
- * storage.
- *
- * Thus a chunked is represented as a sequence of indices of each chunk.
- */
-class ChunkedData {
- /** This is where we store the chunks */
- PageStore* store_ = nullptr;
- /** Indices of the chunks in the `PageStore` */
- std::vector<std::size_t> pagenos_;
-
-public:
- ChunkedData() = default;
- void clear()
- {
- for (std::size_t const& pageno : pagenos_)
- store_->unref_page(pageno);
- pagenos_.clear();
- }
- ~ChunkedData() { clear(); }
-
- // Copy and move
- ChunkedData(ChunkedData const& that) : store_(that.store_), pagenos_(that.pagenos_)
- {
- for (std::size_t const& pageno : pagenos_)
- store_->ref_page(pageno);
- }
- ChunkedData(ChunkedData&& that) noexcept : pagenos_(std::move(that.pagenos_))
- {
- std::swap(store_, that.store_);
- that.pagenos_.clear();
- }
- ChunkedData& operator=(ChunkedData const& that)
- {
- if (this != &that) {
- this->clear();
- store_ = that.store_;
- pagenos_ = that.pagenos_;
- for (std::size_t const& pageno : pagenos_)
- store_->ref_page(pageno);
- }
- return *this;
- }
- ChunkedData& operator=(ChunkedData&& that) noexcept
- {
- if (this != &that) {
- this->clear();
- store_ = that.store_;
- that.store_ = nullptr;
- pagenos_ = std::move(that.pagenos_);
- that.pagenos_.clear();
- }
- return *this;
- }
-
- /** How many pages are used */
- std::size_t page_count() const { return pagenos_.size(); }
-
- /** Get a chunk index */
- std::size_t pageno(std::size_t i) const { return pagenos_[i]; }
-
- /** Get a view of the chunk indices */
- const std::size_t* pagenos() const { return pagenos_.data(); }
-
- /** Get a pointer to a chunk */
- void* page(std::size_t i) const { return store_->get_page(pagenos_[i]); }
-
- ChunkedData(PageStore& store, const AddressSpace& as, RemotePtr<void> addr, std::size_t page_count);
-};
-
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <sys/mman.h>
-#ifdef __FreeBSD__
-#define MAP_POPULATE MAP_PREFAULT_READ
-#endif
-
-#include "src/internal_config.h"
-#include "xbt/log.h"
-#include "xbt/sysdep.h"
-
-#include "src/3rd-party/xxhash.hpp"
-#include "src/mc/mc_mmu.hpp"
-#include "src/mc/sosp/PageStore.hpp"
-
-#include <cstring> // memcpy, memcmp
-#include <unistd.h>
-
-namespace simgrid::mc {
-
-/** @brief Compute a hash for the given memory page
- *
- * The page is used before inserting the page in the page store in order to find duplicate of this page in the page
- * store.
- *
- * @param data Memory page
- * @return hash off the page
- */
-static XBT_ALWAYS_INLINE PageStore::hash_type mc_hash_page(const void* data)
-{
- return xxh::xxhash<64>(data, xbt_pagesize);
-}
-
-// ***** snapshot_page_manager
-
-PageStore::PageStore(std::size_t size) : capacity_(size)
-{
- // Using mmap in order to be able to expand the region by relocating it somewhere else in the virtual memory space:
- void* memory =
- ::mmap(nullptr, size << xbt_pagebits, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
- xbt_assert(memory != MAP_FAILED, "Could not mmap initial snapshot pages.");
-
- this->top_index_ = 0;
- this->memory_ = memory;
- this->page_counts_.resize(size);
-}
-
-PageStore::~PageStore()
-{
- ::munmap(this->memory_, this->capacity_ << xbt_pagebits);
-}
-
-void PageStore::resize(std::size_t size)
-{
- size_t old_bytesize = this->capacity_ << xbt_pagebits;
- size_t new_bytesize = size << xbt_pagebits;
- void* new_memory;
-
- // Expand the memory region by moving it into another
- // virtual memory address if necessary:
-#if HAVE_MREMAP
- new_memory = mremap(this->memory_, old_bytesize, new_bytesize, MREMAP_MAYMOVE);
- xbt_assert(new_memory != MAP_FAILED, "Could not mremap snapshot pages.");
-#else
- if (new_bytesize > old_bytesize) {
- // Grow: first try to add new space after current map
- new_memory = mmap((char*)this->memory_ + old_bytesize, new_bytesize - old_bytesize, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
- xbt_assert(new_memory != MAP_FAILED, "Could not mremap snapshot pages.");
- // Check if expanding worked
- if (new_memory == (char*)this->memory_ + old_bytesize) {
- new_memory = this->memory_;
- } else {
- // New memory segment could not be put at the end of this->memory_,
- // so cancel this one and try to relocate everything and copy data
- munmap(new_memory, new_bytesize - old_bytesize);
- new_memory =
- mmap(nullptr, new_bytesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
- xbt_assert(new_memory != MAP_FAILED, "Could not mremap snapshot pages.");
- memcpy(new_memory, this->memory_, old_bytesize);
- munmap(this->memory_, old_bytesize);
- }
- } else {
- // We don't have functions to shrink a mapping, so leave memory as
- // it is for now
- new_memory = this->memory_;
- }
-#endif
-
- this->capacity_ = size;
- this->memory_ = new_memory;
- this->page_counts_.resize(size, 0);
-}
-
-/** Allocate a free page
- *
- * @return index of the free page
- */
-std::size_t PageStore::alloc_page()
-{
- if (this->free_pages_.empty()) {
- // Expand the region:
- if (this->top_index_ == this->capacity_)
- // All the pages are allocated, we need add more pages:
- this->resize(2 * this->capacity_);
-
- // Use a page from the top:
- return this->top_index_++;
- } else {
- // Use a page from free_pages_ (inside of the region):
- size_t res = this->free_pages_[this->free_pages_.size() - 1];
- this->free_pages_.pop_back();
- return res;
- }
-}
-
-void PageStore::remove_page(std::size_t pageno)
-{
- this->free_pages_.push_back(pageno);
- const void* page = this->get_page(pageno);
- hash_type hash = mc_hash_page(page);
- this->hash_index_[hash].erase(pageno);
-}
-
-/** Store a page in memory */
-std::size_t PageStore::store_page(const void* page)
-{
- xbt_assert(top_index_ <= this->capacity_, "top_index is not consistent");
-
- // First, we check if a page with the same content is already in the page store:
- // 1. compute the hash of the page
- // 2. find pages with the same hash using `hash_index_`
- // 3. find a page with the same content
- hash_type hash = mc_hash_page(page);
-
- // Try to find a duplicate in set of pages with the same hash:
- page_set_type& page_set = this->hash_index_[hash];
- for (size_t const& pageno : page_set) {
- const void* snapshot_page = this->get_page(pageno);
- if (memcmp(page, snapshot_page, xbt_pagesize) == 0) {
- // If a page with the same content is already in the page store it's reused and its refcount is incremented.
- page_counts_[pageno]++;
- return pageno;
- }
- }
-
- // Otherwise, a new page is allocated in the page store and the content of the page is `memcpy()`-ed to this new page.
- std::size_t pageno = alloc_page();
- xbt_assert(this->page_counts_[pageno] == 0, "Allocated page is already used");
- void* snapshot_page = this->get_page(pageno);
- memcpy(snapshot_page, page, xbt_pagesize);
- page_set.insert(pageno);
- page_counts_[pageno]++;
- return pageno;
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_PAGESTORE_HPP
-#define SIMGRID_MC_PAGESTORE_HPP
-
-#include "src/mc/mc_forward.hpp"
-#include "src/mc/mc_mmu.hpp"
-
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#ifndef XBT_ALWAYS_INLINE
-#define XBT_ALWAYS_INLINE inline __attribute__((always_inline))
-#endif
-
-namespace simgrid::mc {
-
-/** @brief Storage for snapshot memory pages
- *
- * The first (lower) layer of the per-page snapshot mechanism is a page store:
- * its responsibility is to store immutable shareable reference-counted memory
- * pages independently of the snapshotting logic. Snapshot management and
- * representation is handled to an higher layer. READMORE
- *
- * Data structure:
- *
- * * A pointer (`memory_`) to a (currently anonymous) `mmap()`ed memory
- * region holding the memory pages (the address of the first page).
- *
- * We want to keep this memory region aligned on the memory pages (so
- * that we might be able to create non-linear memory mappings on those
- * pages in the future) and be able to expand it without copying the
- * data (there will be a lot of pages here): we will be able to
- * efficiently expand the memory mapping using `mremap()`, moving it
- * to another virtual address if necessary.
- *
- * Because we will move this memory mapping on the virtual address
- * space, only the index of the page will be stored in the snapshots
- * and the page will always be looked up by going through `memory`:
- *
- * void* page = (char*) page_store->memory + page_index << pagebits;
- *
- * * The number of pages mapped in virtual memory (`capacity_`). Once all
- * those pages are used, we need to expand the page store with
- * `mremap()`.
- *
- * * A reference count for each memory page `page_counts_`. Each time a
- * snapshot references a page, the counter is incremented. If a
- * snapshot is freed, the reference count is decremented. When the
- * reference count, of a page reaches 0 it is added to a list of available
- * pages (`free_pages_`).
- *
- * * A list of free pages `free_pages_` which can be reused. This avoids having
- * to scan the reference count list to find a free page.
- *
- * * When we are expanding the memory map we do not want to add thousand of page
- * to the `free_pages_` list and remove them just afterwards. The `top_index_`
- * field is an index after which all pages are free and are not in the `free_pages_`
- * list.
- *
- * * When we are adding a page, we need to check if a page with the same
- * content is already in the page store in order to reuse it. For this
- * reason, we maintain an index (`hash_index_`) mapping the hash of a
- * page to the list of page indices with this hash.
- * We use a fast (non cryptographic) hash so there may be conflicts:
- * we must be able to store multiple indices for the same hash.
- *
- */
-class PageStore {
-public: // Types
- using hash_type = std::uint64_t;
-
-private:
- // Types
- // We are using a cheap hash to index a page.
- // We should expect collision and we need to associate multiple page indices
- // to the same hash.
- using page_set_type = std::unordered_set<std::size_t>;
- using pages_map_type = std::unordered_map<hash_type, page_set_type>;
-
- // Fields:
- /** First page */
- void* memory_;
- /** Number of available pages in virtual memory */
- std::size_t capacity_;
- /** Top of the used pages (index of the next available page) */
- std::size_t top_index_;
- /** Page reference count */
- std::vector<std::uint64_t> page_counts_;
- /** Index of available pages before the top */
- std::vector<std::size_t> free_pages_;
- /** Index from page hash to page index */
- pages_map_type hash_index_;
-
- // Methods
- void resize(std::size_t size);
- std::size_t alloc_page();
- void remove_page(std::size_t pageno);
-
-public:
- // Constructors
- PageStore(PageStore const&) = delete;
- PageStore& operator=(PageStore const&) = delete;
- explicit PageStore(std::size_t size);
- ~PageStore();
-
- // Methods
-
- /** @brief Decrement the reference count for a given page
- *
- * Decrement the reference count of this page. Used when a snapshot is destroyed.
- *
- * If the reference count reaches zero, the page is recycled:
- * it is added to the `free_pages_` list and removed from the `hash_index_`.
- *
- * */
- void unref_page(std::size_t pageno);
-
- /** @brief Increment the refcount for a given page
- *
- * This method used to increase a reference count of a page if we know
- * that the content of a page is the same as a page already in the page
- * store.
- *
- * This will be the case if a page if soft clean: we know that is has not
- * changed since the previous snapshot/restoration and we can avoid
- * hashing the page, comparing byte-per-byte to candidates.
- * */
- void ref_page(size_t pageno);
-
- /** @brief Store a page in the page store */
- std::size_t store_page(const void* page);
-
- /** @brief Get a page from its page number
- *
- * @param pageno Number of the memory page in the store
- * @return Start of the page
- */
- void* get_page(std::size_t pageno) const;
-
- // Debug/test methods
-
- /** @brief Get the number of references for a page */
- std::size_t get_ref(std::size_t pageno) const;
-
- /** @brief Get the number of used pages */
- std::size_t size() const;
-
- /** @brief Get the capacity of the page store
- *
- * The capacity is expanded by a system call (mremap).
- * */
- std::size_t capacity() const;
-};
-
-XBT_ALWAYS_INLINE void PageStore::unref_page(std::size_t pageno)
-{
- if ((--this->page_counts_[pageno]) == 0)
- this->remove_page(pageno);
-}
-
-XBT_ALWAYS_INLINE void PageStore::ref_page(size_t pageno)
-{
- ++this->page_counts_[pageno];
-}
-
-XBT_ALWAYS_INLINE void* PageStore::get_page(std::size_t pageno) const
-{
- return (void*)simgrid::mc::mmu::join(pageno, (std::uintptr_t)this->memory_);
-}
-
-XBT_ALWAYS_INLINE std::size_t PageStore::get_ref(std::size_t pageno) const
-{
- return this->page_counts_[pageno];
-}
-
-XBT_ALWAYS_INLINE std::size_t PageStore::size() const
-{
- return this->top_index_ - this->free_pages_.size();
-}
-
-XBT_ALWAYS_INLINE std::size_t PageStore::capacity() const
-{
- return this->capacity_;
-}
-
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2015-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/3rd-party/catch.hpp"
-
-#include <array>
-#include <cstdint>
-#include <cstring>
-#include <iostream>
-
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <memory>
-
-#include "src/mc/sosp/PageStore.hpp"
-
-/***********************************/
-// a class to hold the variable used in the test cases
-class pstore_test_helper {
- const size_t pagesize = getpagesize();
- simgrid::mc::PageStore store{50};
- std::byte* data =
- static_cast<std::byte*>(mmap(nullptr, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
- std::array<size_t, 4> pageno = {0, 0, 0, 0};
- int value = 0;
-
- void new_content(std::byte* buf, size_t size);
-
-public:
- // member functions used by the test suite(s)
- void init();
- void store_page_once();
- void store_same_page();
- void store_new_page();
- void unref_pages();
- void reallocate_page();
-};
-
-void pstore_test_helper::init()
-{
- REQUIRE(data != nullptr);
- REQUIRE(store.size() == 0);
-}
-
-void pstore_test_helper::store_page_once()
-{
- new_content(data, pagesize);
- pageno[0] = store.store_page(data);
- REQUIRE(store.get_ref(pageno[0]) == 1);
- const auto* copy = store.get_page(pageno[0]);
- REQUIRE(::memcmp(data, copy, pagesize) == 0); // The page data should be the same
- REQUIRE(store.size() == 1);
-}
-
-void pstore_test_helper::store_same_page()
-{
- pageno[1] = store.store_page(data);
- REQUIRE(pageno[0] == pageno[1]); // Page should be the same
- REQUIRE(store.get_ref(pageno[0]) == 2);
- REQUIRE(store.size() == 1);
-}
-
-void pstore_test_helper::store_new_page()
-{
- new_content(data, pagesize);
- pageno[2] = store.store_page(data);
- REQUIRE(pageno[0] != pageno[2]); // The new page should be different
- REQUIRE(store.size() == 2);
-}
-
-void pstore_test_helper::unref_pages()
-{
- store.unref_page(pageno[0]);
- REQUIRE(store.get_ref(pageno[0]) == 1);
- REQUIRE(store.size() == 2);
-
- store.unref_page(pageno[1]);
- REQUIRE(store.size() == 1);
-}
-
-void pstore_test_helper::reallocate_page()
-{
- new_content(data, pagesize);
- pageno[3] = store.store_page(data);
- REQUIRE(pageno[0] == pageno[3]); // The old page should be reused
- REQUIRE(store.get_ref(pageno[3]) == 1);
- REQUIRE(store.size() == 2);
-}
-
-void pstore_test_helper::new_content(std::byte* buf, size_t size)
-{
- value++;
- std::fill_n(buf, size, static_cast<std::byte>(value));
-}
-
-TEST_CASE("MC page store, used during checkpoint", "MC::PageStore")
-{
- pstore_test_helper pstore_test;
- pstore_test.init();
-
- INFO("Store page once");
- pstore_test.store_page_once();
-
- INFO("Store the same page");
- pstore_test.store_same_page();
-
- INFO("Store a new page");
- pstore_test.store_new_page();
-
- INFO("Unref pages");
- pstore_test.unref_pages();
-
- INFO("Reallocate pages");
- pstore_test.reallocate_page();
-}
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/sosp/Region.hpp"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_forward.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include <cstdlib>
-#include <sys/mman.h>
-#ifdef __FreeBSD__
-#define MAP_POPULATE MAP_PREFAULT_READ
-#endif
-
-namespace simgrid::mc {
-
-Region::Region(PageStore& store, const RemoteProcessMemory& memory, RegionType region_type, void* start_addr,
- size_t size)
- : region_type_(region_type), start_addr_(start_addr), size_(size)
-{
- xbt_assert((((uintptr_t)start_addr) & (xbt_pagesize - 1)) == 0, "Start address not at the beginning of a page");
-
- chunks_ = ChunkedData(store, memory, RemotePtr<void>(start_addr), mmu::chunk_count(size));
-}
-
-/** @brief Restore a region from a snapshot
- *
- * @param region Target region
- */
-void Region::restore(const RemoteProcessMemory& memory) const
-{
- xbt_assert(((start().address()) & (xbt_pagesize - 1)) == 0, "Not at the beginning of a page");
- xbt_assert(simgrid::mc::mmu::chunk_count(size()) == get_chunks().page_count());
-
- for (size_t i = 0; i != get_chunks().page_count(); ++i) {
- auto* target_page = (void*)simgrid::mc::mmu::join(i, (std::uintptr_t)(void*)start().address());
- const void* source_page = get_chunks().page(i);
- memory.write_bytes(source_page, xbt_pagesize, remote(target_page));
- }
-}
-
-static XBT_ALWAYS_INLINE void* mc_translate_address_region(uintptr_t addr, const simgrid::mc::Region* region)
-{
- auto [pageno, offset] = simgrid::mc::mmu::split(addr - region->start().address());
- void* snapshot_page = region->get_chunks().page(pageno);
- return (char*)snapshot_page + offset;
-}
-
-void* Region::read(void* target, const void* addr, std::size_t size) const
-{
- xbt_assert(contain(simgrid::mc::remote(addr)), "Trying to read out of the region boundary.");
-
- // Last byte of the region:
- const void* end_addr = (const char*)addr + size - 1;
- if (simgrid::mc::mmu::same_chunk((std::uintptr_t)addr, (std::uintptr_t)end_addr)) {
- // The memory is contained in a single page:
- return mc_translate_address_region((uintptr_t)addr, this);
- }
- // Otherwise, the memory spans several pages. Let's copy it all into the provided buffer
- xbt_assert(target != nullptr, "Missing destination buffer for fragmented memory access");
-
- // TODO, we assume the chunks are aligned to natural chunk boundaries.
- // We should remove this assumption.
-
- // Page of the last byte of the memory area:
- size_t page_end = simgrid::mc::mmu::split((std::uintptr_t)end_addr).first;
-
- void* dest = target; // iterator in the buffer to where we should copy next
-
- // Read each page:
- while (simgrid::mc::mmu::split((std::uintptr_t)addr).first != page_end) {
- const void* snapshot_addr = mc_translate_address_region((uintptr_t)addr, this);
- auto* next_page = (void*)simgrid::mc::mmu::join(simgrid::mc::mmu::split((std::uintptr_t)addr).first + 1, 0);
- size_t readable = (char*)next_page - (const char*)addr;
- memcpy(dest, snapshot_addr, readable);
- addr = (const char*)addr + readable;
- dest = (char*)dest + readable;
- size -= readable;
- }
-
- // Read the end:
- const void* snapshot_addr = mc_translate_address_region((uintptr_t)addr, this);
- memcpy(dest, snapshot_addr, size);
-
- return target;
-}
-
-} // namespace simgrid::mc
-
-/** Compare memory between snapshots (with known regions)
- *
- * @param addr1 Address in the first snapshot
- * @param region1 Region of the address in the first snapshot
- * @param addr2 Address in the second snapshot
- * @param region2 Region of the address in the second snapshot
- * @return same semantic as memcmp
- */
-int MC_snapshot_region_memcmp(const void* addr1, const simgrid::mc::Region* region1, const void* addr2,
- const simgrid::mc::Region* region2, size_t size)
-{
- // Using alloca() for large allocations may trigger stack overflow:
- // use malloc if the buffer is too big.
- bool stack_alloc = size < 64;
- void* buffer1a = stack_alloc ? alloca(size) : ::operator new(size);
- void* buffer2a = stack_alloc ? alloca(size) : ::operator new(size);
- const void* buffer1 = region1->read(buffer1a, addr1, size);
- const void* buffer2 = region2->read(buffer2a, addr2, size);
- int res;
- if (buffer1 == buffer2)
- res = 0;
- else
- res = memcmp(buffer1, buffer2, size);
- if (not stack_alloc) {
- ::operator delete(buffer1a);
- ::operator delete(buffer2a);
- }
- return res;
-}
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_SOSP_REGION_HPP
-#define SIMGRID_MC_SOSP_REGION_HPP
-
-#include "src/mc/remote/RemotePtr.hpp"
-#include "src/mc/sosp/ChunkedData.hpp"
-
-#include <memory>
-#include <vector>
-
-namespace simgrid::mc {
-
-enum class RegionType { Heap = 1, Data = 2 };
-
-/** A copy/snapshot of a given memory region, where identical pages are stored only once */
-class Region {
-public:
- static const RegionType HeapRegion = RegionType::Heap;
- static const RegionType DataRegion = RegionType::Data;
-
-private:
- RegionType region_type_;
- simgrid::mc::ObjectInformation* object_info_ = nullptr;
-
- /** @brief Virtual address of the region in the simulated process */
- void* start_addr_ = nullptr;
-
- /** @brief Size of the data region in bytes */
- std::size_t size_ = 0;
-
- ChunkedData chunks_;
-
-public:
- Region(PageStore& store, const RemoteProcessMemory& memory, RegionType type, void* start_addr, size_t size);
- Region(Region const&) = delete;
- Region& operator=(Region const&) = delete;
- Region(Region&& that) = delete;
- Region& operator=(Region&& that) = delete;
-
- // Data
-
- ChunkedData const& get_chunks() const { return chunks_; }
-
- simgrid::mc::ObjectInformation* object_info() const { return object_info_; }
- void object_info(simgrid::mc::ObjectInformation* info) { object_info_ = info; }
-
- // Other getters
-
- RemotePtr<void> start() const { return remote(start_addr_); }
- RemotePtr<void> end() const { return remote((char*)start_addr_ + size_); }
- std::size_t size() const { return size_; }
- RegionType region_type() const { return region_type_; }
-
- bool contain(RemotePtr<void> p) const { return p >= start() && p < end(); }
-
- /** @brief Restore a region from a snapshot */
- void restore(const RemoteProcessMemory& memory) const;
-
- /** @brief Read memory that was snapshotted in this region
- *
- * @param target Buffer to store contiguously the value if it spans over several pages
- * @param addr Process (non-snapshot) address of the data
- * @param size Size of the data to read in bytes
- * @return Pointer where the data is located (either target buffer or original location)
- */
- void* read(void* target, const void* addr, std::size_t size) const;
-};
-
-} // namespace simgrid::mc
-
-int MC_snapshot_region_memcmp(const void* addr1, const simgrid::mc::Region* region1, const void* addr2,
- const simgrid::mc::Region* region2, std::size_t size);
-
-static XBT_ALWAYS_INLINE void* MC_region_read_pointer(const simgrid::mc::Region* region, const void* addr)
-{
- void* res;
- return *(void**)region->read(&res, addr, sizeof(void*));
-}
-
-#endif
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#define _FILE_OFFSET_BITS 64 /* needed for pread_whole to work as expected on 32bits */
-
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include "src/mc/explo/Exploration.hpp"
-#include "src/mc/explo/LivenessChecker.hpp"
-#include "src/mc/sosp/Snapshot.hpp"
-#include "xbt/file.hpp"
-#include "xbt/log.h"
-#include "xbt/system_error.hpp"
-
-#include <fcntl.h>
-#include <libunwind-ptrace.h>
-#include <sys/mman.h> // PROT_*
-
-#include <algorithm>
-#include <cerrno>
-#include <cstring>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <string_view>
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_process, mc, "MC process information");
-
-namespace simgrid::mc {
-
-// ***** Helper stuff
-
-static bool is_filtered_lib(std::string_view libname)
-{
- return libname != "libsimgrid";
-}
-
-static std::string get_lib_name(const std::string& pathname)
-{
- std::string map_basename = simgrid::xbt::Path(pathname).get_base_name();
- std::string libname;
-
- if (size_t pos = map_basename.rfind(".so"); pos != std::string::npos) {
- // strip the extension (matching regex "\.so.*$")
- libname.assign(map_basename, 0, pos);
-
- // strip the version suffix (matching regex "-[.0-9-]*$")
- while (true) {
- pos = libname.rfind('-');
- if (pos == std::string::npos || libname.find_first_not_of(".0123456789", pos + 1) != std::string::npos)
- break;
- libname.erase(pos);
- }
- }
-
- return libname;
-}
-
-static ssize_t pread_whole(int fd, void* buf, size_t count, off_t offset)
-{
- auto* buffer = static_cast<char*>(buf);
- ssize_t real_count = count;
- while (count) {
- ssize_t res = pread(fd, buffer, count, offset);
- if (res > 0) {
- count -= res;
- buffer += res;
- offset += res;
- } else if (res == 0)
- return -1;
- else if (errno != EINTR) {
- XBT_ERROR("pread_whole: %s", strerror(errno));
- return -1;
- }
- }
- return real_count;
-}
-
-static ssize_t pwrite_whole(int fd, const void* buf, size_t count, off_t offset)
-{
- const auto* buffer = static_cast<const char*>(buf);
- ssize_t real_count = count;
- while (count) {
- ssize_t res = pwrite(fd, buffer, count, offset);
- if (res > 0) {
- count -= res;
- buffer += res;
- offset += res;
- } else if (res == 0)
- return -1;
- else if (errno != EINTR) {
- XBT_ERROR("pwrite_whole: %s", strerror(errno));
- return -1;
- }
- }
- return real_count;
-}
-
-int open_vm(pid_t pid, int flags)
-{
- std::string buffer = "/proc/" + std::to_string(pid) + "/mem";
- return open(buffer.c_str(), flags);
-}
-
-// ***** RemoteProcessMemory
-
-RemoteProcessMemory::RemoteProcessMemory(pid_t pid, xbt_mheap_t mmalloc_default_mdp) : AddressSpace(this), pid_(pid)
-{
- this->heap_address = remote(mmalloc_default_mdp);
-
- this->memory_map_ = simgrid::xbt::get_memory_map(this->pid_);
- this->init_memory_map_info();
-
- int fd = open_vm(this->pid_, O_RDWR);
- xbt_assert(fd >= 0, "Could not open file for process virtual address space");
- this->memory_file = fd;
-
- this->unw_addr_space = simgrid::mc::UnwindContext::createUnwindAddressSpace();
- this->unw_underlying_addr_space = simgrid::unw::create_addr_space();
- this->unw_underlying_context = simgrid::unw::create_context(this->unw_underlying_addr_space, this->pid_);
-
- auto ignored_local_variables = {
- std::make_pair("e", "*"),
- std::make_pair("_log_ev", "*"),
-
- /* Ignore local variable about time used for tracing */
- std::make_pair("start_time", "*"),
- };
- for (auto const& [var, frame] : ignored_local_variables)
- ignore_local_variable(var, frame);
-
- ignore_global_variable("counter"); // Static variable used for tracing
-}
-
-RemoteProcessMemory::~RemoteProcessMemory()
-{
- if (this->memory_file >= 0)
- close(this->memory_file);
-
- if (this->unw_underlying_addr_space != unw_local_addr_space) {
- if (this->unw_underlying_addr_space)
- unw_destroy_addr_space(this->unw_underlying_addr_space);
- if (this->unw_underlying_context)
- _UPT_destroy(this->unw_underlying_context);
- }
-
- unw_destroy_addr_space(this->unw_addr_space);
-}
-
-/** Refresh the information about the process
- *
- * Do not use directly, this is used by the getters when appropriate
- * in order to have fresh data.
- */
-void RemoteProcessMemory::refresh_heap()
-{
- // Read/dereference/refresh the std_heap pointer:
- this->read(this->heap.get(), this->heap_address);
- this->cache_flags_ |= RemoteProcessMemory::cache_heap;
-}
-
-/** Refresh the information about the process
- *
- * Do not use directly, this is used by the getters when appropriate
- * in order to have fresh data.
- * */
-void RemoteProcessMemory::refresh_malloc_info()
-{
- // Refresh process->heapinfo:
- if (this->cache_flags_ & RemoteProcessMemory::cache_malloc)
- return;
- size_t count = this->heap->heaplimit + 1;
- if (this->heap_info.size() < count)
- this->heap_info.resize(count);
- this->read_bytes(this->heap_info.data(), count * sizeof(malloc_info), remote(this->heap->heapinfo));
- this->cache_flags_ |= RemoteProcessMemory::cache_malloc;
-}
-std::size_t RemoteProcessMemory::get_remote_heap_bytes()
-{
- return mmalloc_get_bytes_used_remote(get_heap()->heaplimit, get_malloc_info());
-}
-
-/** @brief Finds the range of the different memory segments and binary paths */
-void RemoteProcessMemory::init_memory_map_info()
-{
- XBT_DEBUG("Get debug information ...");
- this->maestro_stack_start_ = nullptr;
- this->maestro_stack_end_ = nullptr;
- this->object_infos.clear();
- this->binary_info = nullptr;
-
- std::vector<simgrid::xbt::VmMap> const& maps = this->memory_map_;
-
- const char* current_name = nullptr;
-
- for (size_t i = 0; i < maps.size(); i++) {
- simgrid::xbt::VmMap const& reg = maps[i];
- const char* pathname = maps[i].pathname.c_str();
-
- // Nothing to do
- if (maps[i].pathname.empty()) {
- current_name = nullptr;
- continue;
- }
-
- // [stack], [vvar], [vsyscall], [vdso] ...
- if (pathname[0] == '[') {
- if ((reg.prot & PROT_WRITE) && not memcmp(pathname, "[stack]", 7)) {
- this->maestro_stack_start_ = remote(reg.start_addr);
- this->maestro_stack_end_ = remote(reg.end_addr);
- }
- current_name = nullptr;
- continue;
- }
-
- if (current_name && strcmp(current_name, pathname) == 0)
- continue;
-
- current_name = pathname;
- if (not(reg.prot & PROT_READ) && (reg.prot & PROT_EXEC))
- continue;
-
- const bool is_executable = not i;
- std::string libname;
- if (not is_executable) {
- libname = get_lib_name(pathname);
- if (is_filtered_lib(libname)) {
- continue;
- }
- }
-
- std::shared_ptr<simgrid::mc::ObjectInformation> info =
- simgrid::mc::createObjectInformation(this->memory_map_, pathname);
- this->object_infos.push_back(info);
- if (is_executable)
- this->binary_info = info;
- }
-
- xbt_assert(this->maestro_stack_start_, "Did not find maestro_stack_start");
- xbt_assert(this->maestro_stack_end_, "Did not find maestro_stack_end");
-
- XBT_DEBUG("Get debug information done !");
-}
-
-std::shared_ptr<simgrid::mc::ObjectInformation> RemoteProcessMemory::find_object_info(RemotePtr<void> addr) const
-{
- for (auto const& object_info : this->object_infos)
- if (addr.address() >= (std::uint64_t)object_info->start && addr.address() <= (std::uint64_t)object_info->end)
- return object_info;
- return nullptr;
-}
-
-std::shared_ptr<ObjectInformation> RemoteProcessMemory::find_object_info_exec(RemotePtr<void> addr) const
-{
- for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
- if (addr.address() >= (std::uint64_t)info->start_exec && addr.address() <= (std::uint64_t)info->end_exec)
- return info;
- return nullptr;
-}
-
-std::shared_ptr<ObjectInformation> RemoteProcessMemory::find_object_info_rw(RemotePtr<void> addr) const
-{
- for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
- if (addr.address() >= (std::uint64_t)info->start_rw && addr.address() <= (std::uint64_t)info->end_rw)
- return info;
- return nullptr;
-}
-
-simgrid::mc::Frame* RemoteProcessMemory::find_function(RemotePtr<void> ip) const
-{
- std::shared_ptr<simgrid::mc::ObjectInformation> info = this->find_object_info_exec(ip);
- return info ? info->find_function((void*)ip.address()) : nullptr;
-}
-
-/** Find (one occurrence of) the named variable definition
- */
-const simgrid::mc::Variable* RemoteProcessMemory::find_variable(const char* name) const
-{
- // First lookup the variable in the executable shared object.
- // A global variable used directly by the executable code from a library
- // is reinstantiated in the executable memory .data/.bss.
- // We need to look up the variable in the executable first.
- if (this->binary_info) {
- std::shared_ptr<simgrid::mc::ObjectInformation> const& info = this->binary_info;
- const simgrid::mc::Variable* var = info->find_variable(name);
- if (var)
- return var;
- }
-
- for (std::shared_ptr<simgrid::mc::ObjectInformation> const& info : this->object_infos) {
- const simgrid::mc::Variable* var = info->find_variable(name);
- if (var)
- return var;
- }
-
- return nullptr;
-}
-
-void RemoteProcessMemory::read_variable(const char* name, void* target, size_t size) const
-{
- const simgrid::mc::Variable* var = this->find_variable(name);
- xbt_assert(var, "Variable %s not found", name);
- xbt_assert(var->address, "No simple location for this variable");
-
- if (not var->type->full_type) // Try to resolve this type. The needed ObjectInfo was maybe (lazily) loaded recently
- for (auto const& object_info : this->object_infos)
- postProcessObjectInformation(this, object_info.get());
- xbt_assert(var->type->full_type, "Partial type for %s (even after re-resolving types), cannot retrieve its size.",
- name);
- xbt_assert((size_t)var->type->full_type->byte_size == size, "Unexpected size for %s (expected %zu, received %zu).",
- name, size, (size_t)var->type->full_type->byte_size);
- this->read_bytes(target, size, remote(var->address));
-}
-
-std::string RemoteProcessMemory::read_string(RemotePtr<char> address) const
-{
- if (not address)
- return {};
-
- std::vector<char> res(128);
- off_t off = 0;
-
- while (true) {
- ssize_t c = pread(this->memory_file, res.data() + off, res.size() - off, (off_t)address.address() + off);
- if (c == -1 && errno == EINTR)
- continue;
- xbt_assert(c > 0, "Could not read string from remote process");
-
- if (memchr(res.data() + off, '\0', c))
- return res.data();
-
- off += c;
- if (off == (off_t)res.size())
- res.resize(res.size() * 2);
- }
-}
-
-void* RemoteProcessMemory::read_bytes(void* buffer, std::size_t size, RemotePtr<void> address,
- ReadOptions /*options*/) const
-{
- xbt_assert(pread_whole(this->memory_file, buffer, size, (size_t)address.address()) != -1,
- "Read at %p from process %lli failed", (void*)address.address(), (long long)this->pid_);
- return buffer;
-}
-
-/** Write data to a process memory
- *
- * @param buffer local memory address (source)
- * @param len data size
- * @param address target process memory address (target)
- */
-void RemoteProcessMemory::write_bytes(const void* buffer, size_t len, RemotePtr<void> address) const
-{
- xbt_assert(pwrite_whole(this->memory_file, buffer, len, (size_t)address.address()) != -1,
- "Write to process %lli failed", (long long)this->pid_);
-}
-
-static void zero_buffer_init(const void** zero_buffer, size_t zero_buffer_size)
-{
- int fd = open("/dev/zero", O_RDONLY);
- xbt_assert(fd >= 0, "Could not open /dev/zero");
- *zero_buffer = mmap(nullptr, zero_buffer_size, PROT_READ, MAP_SHARED, fd, 0);
- xbt_assert(*zero_buffer != MAP_FAILED, "Could not map the zero buffer");
- close(fd);
-}
-
-void RemoteProcessMemory::clear_bytes(RemotePtr<void> address, size_t len) const
-{
- static constexpr size_t zero_buffer_size = 10 * 4096;
- static const void* zero_buffer;
- static std::once_flag zero_buffer_flag;
-
- std::call_once(zero_buffer_flag, zero_buffer_init, &zero_buffer, zero_buffer_size);
- while (len) {
- size_t s = len > zero_buffer_size ? zero_buffer_size : len;
- this->write_bytes(zero_buffer, s, address);
- address = remote((char*)address.address() + s);
- len -= s;
- }
-}
-
-void RemoteProcessMemory::ignore_region(std::uint64_t addr, std::size_t size)
-{
- IgnoredRegion region;
- region.addr = addr;
- region.size = size;
-
- auto pos = std::lower_bound(ignored_regions_.begin(), ignored_regions_.end(), region,
- [](auto const& reg1, auto const& reg2) {
- return reg1.addr < reg2.addr || (reg1.addr == reg2.addr && reg1.size < reg2.size);
- });
- if (pos == ignored_regions_.end() || pos->addr != addr || pos->size != size)
- ignored_regions_.insert(pos, region);
-}
-
-void RemoteProcessMemory::unignore_region(std::uint64_t addr, std::size_t size)
-{
- IgnoredRegion region;
- region.addr = addr;
- region.size = size;
-
- auto pos = std::lower_bound(ignored_regions_.begin(), ignored_regions_.end(), region,
- [](auto const& reg1, auto const& reg2) {
- return reg1.addr < reg2.addr || (reg1.addr == reg2.addr && reg1.size < reg2.size);
- });
- if (pos != ignored_regions_.end())
- ignored_regions_.erase(pos);
-}
-
-void RemoteProcessMemory::ignore_heap(IgnoredHeapRegion const& region)
-{
- // Binary search the position of insertion:
- auto pos = std::lower_bound(ignored_heap_.begin(), ignored_heap_.end(), region.address,
- [](auto const& reg, auto const* addr) { return reg.address < addr; });
- if (pos == ignored_heap_.end() || pos->address != region.address) {
- // Insert it:
- ignored_heap_.insert(pos, region);
- }
-}
-
-void RemoteProcessMemory::unignore_heap(void* address, size_t size)
-{
- // Binary search:
- auto pos = std::lower_bound(ignored_heap_.begin(), ignored_heap_.end(), address,
- [](auto const& reg, auto const* addr) { return reg.address < addr; });
- if (pos != ignored_heap_.end() && static_cast<char*>(pos->address) <= static_cast<char*>(address) + size)
- ignored_heap_.erase(pos);
-}
-
-void RemoteProcessMemory::ignore_local_variable(const char* var_name, const char* frame_name) const
-{
- if (frame_name != nullptr && strcmp(frame_name, "*") == 0)
- frame_name = nullptr;
- for (std::shared_ptr<simgrid::mc::ObjectInformation> const& info : this->object_infos)
- info->remove_local_variable(var_name, frame_name);
-}
-
-void RemoteProcessMemory::dump_stack() const
-{
- unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, BYTE_ORDER);
- if (as == nullptr) {
- XBT_ERROR("Could not initialize ptrace address space");
- return;
- }
-
- void* context = _UPT_create(this->pid_);
- if (context == nullptr) {
- unw_destroy_addr_space(as);
- XBT_ERROR("Could not initialize ptrace context");
- return;
- }
-
- unw_cursor_t cursor;
- if (unw_init_remote(&cursor, as, context) != 0) {
- _UPT_destroy(context);
- unw_destroy_addr_space(as);
- XBT_ERROR("Could not initialize ptrace cursor");
- return;
- }
-
- simgrid::mc::dumpStack(stderr, &cursor);
-
- _UPT_destroy(context);
- unw_destroy_addr_space(as);
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* mc::RemoteClient: representative of the Client memory on the MC side */
-
-/* Copyright (c) 2008-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_PROCESS_H
-#define SIMGRID_MC_PROCESS_H
-
-#include "src/mc/AddressSpace.hpp"
-#include "src/mc/datatypes.h"
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/remote/RemotePtr.hpp"
-#include "src/xbt/memory_map.hpp"
-#include "src/xbt/mmalloc/mmprivate.h"
-
-#include <libunwind.h>
-#include <vector>
-
-namespace simgrid::mc {
-
-struct IgnoredRegion {
- std::uint64_t addr;
- std::size_t size;
-};
-
-struct IgnoredHeapRegion {
- int block;
- int fragment;
- void* address;
- std::size_t size;
-};
-
-/** The Application's process memory, seen from the Checker perspective. This class is not needed if you don't need to
- * introspect the application process.
- *
- * Responsabilities:
- * - reading from the process memory (`AddressSpace`);
- * - accessing the system state of the process (heap, …);
- * - stack unwinding;
- * - etc.
- */
-class RemoteProcessMemory final : public AddressSpace {
-private:
- // Those flags are used to track down which cached information
- // is still up to date and which information needs to be updated.
- static constexpr int cache_none = 0;
- static constexpr int cache_heap = 1;
- static constexpr int cache_malloc = 2;
-
-public:
- explicit RemoteProcessMemory(pid_t pid, xbt_mheap_t mmalloc_default_mdp);
- ~RemoteProcessMemory() override;
-
- RemoteProcessMemory(RemoteProcessMemory const&) = delete;
- RemoteProcessMemory(RemoteProcessMemory&&) = delete;
- RemoteProcessMemory& operator=(RemoteProcessMemory const&) = delete;
- RemoteProcessMemory& operator=(RemoteProcessMemory&&) = delete;
-
- /* ************* */
- /* Low-level API */
- /* ************* */
-
- // Read memory:
- void* read_bytes(void* buffer, std::size_t size, RemotePtr<void> address,
- ReadOptions options = ReadOptions::none()) const override;
-
- void read_variable(const char* name, void* target, size_t size) const;
- template <class T> void read_variable(const char* name, T* target) const
- {
- read_variable(name, target, sizeof(*target));
- }
- template <class T> Remote<T> read_variable(const char* name) const
- {
- Remote<T> res;
- read_variable(name, res.get_buffer(), sizeof(T));
- return res;
- }
-
- std::string read_string(RemotePtr<char> address) const;
- using AddressSpace::read_string;
-
- // Write memory:
- void write_bytes(const void* buffer, size_t len, RemotePtr<void> address) const;
- void clear_bytes(RemotePtr<void> address, size_t len) const;
-
- // Debug information:
- std::shared_ptr<ObjectInformation> find_object_info(RemotePtr<void> addr) const;
- std::shared_ptr<ObjectInformation> find_object_info_exec(RemotePtr<void> addr) const;
- std::shared_ptr<ObjectInformation> find_object_info_rw(RemotePtr<void> addr) const;
- Frame* find_function(RemotePtr<void> ip) const;
- const Variable* find_variable(const char* name) const;
-
- // Heap access:
- xbt_mheap_t get_heap()
- {
- if (not(cache_flags_ & RemoteProcessMemory::cache_heap))
- refresh_heap();
- return this->heap.get();
- }
- const malloc_info* get_malloc_info()
- {
- if (not(this->cache_flags_ & RemoteProcessMemory::cache_malloc))
- this->refresh_malloc_info();
- return this->heap_info.data();
- }
- /* Get the amount of memory mallocated in the remote process (requires mmalloc) */
- std::size_t get_remote_heap_bytes();
-
- void clear_cache() { this->cache_flags_ = RemoteProcessMemory::cache_none; }
-
- std::vector<IgnoredRegion> const& ignored_regions() const { return ignored_regions_; }
- void ignore_region(std::uint64_t address, std::size_t size);
- void unignore_region(std::uint64_t address, std::size_t size);
-
- bool in_maestro_stack(RemotePtr<void> p) const
- {
- return p >= this->maestro_stack_start_ && p < this->maestro_stack_end_;
- }
-
- void ignore_global_variable(const char* name) const
- {
- for (std::shared_ptr<ObjectInformation> const& info : this->object_infos)
- info->remove_global_variable(name);
- }
-
- std::vector<s_stack_region_t>& stack_areas() { return stack_areas_; }
- std::vector<s_stack_region_t> const& stack_areas() const { return stack_areas_; }
-
- std::vector<IgnoredHeapRegion> const& ignored_heap() const { return ignored_heap_; }
- void ignore_heap(IgnoredHeapRegion const& region);
- void unignore_heap(void* address, size_t size);
-
- void ignore_local_variable(const char* var_name, const char* frame_name) const;
-
- void dump_stack() const;
-
-private:
- void init_memory_map_info();
- void refresh_heap();
- void refresh_malloc_info();
-
- pid_t pid_ = -1;
- std::vector<xbt::VmMap> memory_map_;
- RemotePtr<void> maestro_stack_start_;
- RemotePtr<void> maestro_stack_end_;
- int memory_file = -1;
- std::vector<IgnoredRegion> ignored_regions_;
- std::vector<s_stack_region_t> stack_areas_;
- std::vector<IgnoredHeapRegion> ignored_heap_;
-
- /** State of the cache (which variables are up to date) */
- int cache_flags_ = RemoteProcessMemory::cache_none;
-
-public:
- // object info
- // TODO, make private (first, objectify simgrid::mc::ObjectInformation*)
- std::vector<std::shared_ptr<ObjectInformation>> object_infos;
- std::shared_ptr<ObjectInformation> binary_info;
-
- /** Address of the heap structure in the MCed process. */
- RemotePtr<s_xbt_mheap_t> heap_address;
-
- /** Copy of the heap structure of the process
- *
- * This is refreshed with the `MC_process_refresh` call.
- * This is not used if the process is the current one:
- * use `get_heap_info()` in order to use it.
- */
- std::unique_ptr<s_xbt_mheap_t> heap = std::make_unique<s_xbt_mheap_t>();
-
- /** Copy of the allocation info structure
- *
- * This is refreshed with the `MC_process_refresh` call.
- * This is not used if the process is the current one:
- * use `get_malloc_info()` in order to use it.
- */
- std::vector<malloc_info> heap_info;
-
- // Libunwind-data
- /** Full-featured MC-aware libunwind address space for the process
- *
- * This address space is using a simgrid::mc::UnwindContext*
- * (with simgrid::mc::Process* / simgrid::mc::AddressSpace*
- * and unw_context_t).
- */
- unw_addr_space_t unw_addr_space = nullptr;
-
- /** Underlying libunwind address-space
- *
- * The `find_proc_info`, `put_unwind_info`, `get_dyn_info_list_addr`
- * operations of the native MC address space is currently delegated
- * to this address space (either the local or a ptrace unwinder).
- */
- unw_addr_space_t unw_underlying_addr_space = nullptr;
-
- /** The corresponding context
- */
- void* unw_underlying_context = nullptr;
-};
-
-/** Open a FD to a remote process memory (`/dev/$pid/mem`) */
-XBT_PRIVATE int open_vm(pid_t pid, int flags);
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/mc/sosp/Snapshot.hpp"
-#include "src/mc/mc_config.hpp"
-
-#include <cstddef> /* std::size_t */
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_snapshot, mc, "Taking and restoring snapshots");
-namespace simgrid::mc {
-/************************************* Take Snapshot ************************************/
-/****************************************************************************************/
-
-void Snapshot::snapshot_regions(RemoteProcessMemory& memory)
-{
- snapshot_regions_.clear();
-
- for (auto const& object_info : memory.object_infos)
- add_region(RegionType::Data, memory, object_info.get(), object_info->start_rw,
- object_info->end_rw - object_info->start_rw);
-
- const s_xbt_mheap_t* heap = memory.get_heap();
- void* start_heap = heap->base;
- void* end_heap = heap->breakval;
-
- add_region(RegionType::Heap, memory, nullptr, start_heap, (char*)end_heap - (char*)start_heap);
- heap_bytes_used_ = mmalloc_get_bytes_used_remote(heap->heaplimit, memory.get_malloc_info());
-}
-
-/** @brief Checks whether the variable is in scope for a given IP.
- *
- * A variable may be defined only from a given value of IP.
- *
- * @param var Variable description
- * @param scope Scope description
- * @param ip Instruction pointer
- * @return true if the variable is valid
- * */
-static bool valid_variable(const simgrid::mc::Variable* var, simgrid::mc::Frame* scope, const void* ip)
-{
- // The variable is not yet valid:
- if (scope->range.begin() + var->start_scope > (std::uint64_t)ip)
- return false;
- else
- return true;
-}
-
-static void fill_local_variables_values(mc_stack_frame_t stack_frame, Frame* scope,
- std::vector<s_local_variable_t>& result, AddressSpace* memory)
-{
- if (not scope || not scope->range.contain(stack_frame->ip))
- return;
-
- for (const Variable& current_variable : scope->variables) {
- if (not valid_variable(¤t_variable, scope, (void*)stack_frame->ip))
- continue;
-
- if (not current_variable.type) {
- XBT_VERB("Ignore local variable without type: '%s' [%s]", current_variable.name.c_str(),
- stack_frame->frame->name.c_str());
- continue;
- }
-
- s_local_variable_t new_var;
- new_var.subprogram = stack_frame->frame;
- new_var.ip = stack_frame->ip;
- new_var.name = current_variable.name;
- new_var.type = current_variable.type;
- new_var.address = nullptr;
-
- if (current_variable.address != nullptr)
- new_var.address = current_variable.address;
- else if (not current_variable.location_list.empty()) {
- dwarf::Location location =
- simgrid::dwarf::resolve(current_variable.location_list, current_variable.object_info,
- &(stack_frame->unw_cursor), (void*)stack_frame->frame_base, memory);
-
- xbt_assert(location.in_memory(), "Cannot handle non-address variable");
- new_var.address = location.address();
- } else
- xbt_die("No address");
-
- result.push_back(std::move(new_var));
- }
-
- // Recursive processing of nested scopes:
- for (Frame& nested_scope : scope->scopes)
- fill_local_variables_values(stack_frame, &nested_scope, result, memory);
-}
-
-static std::vector<s_local_variable_t> get_local_variables_values(std::vector<s_mc_stack_frame_t>& stack_frames,
- AddressSpace* memory)
-{
- std::vector<s_local_variable_t> variables;
- for (s_mc_stack_frame_t& stack_frame : stack_frames)
- fill_local_variables_values(&stack_frame, stack_frame.frame, variables, memory);
- return variables;
-}
-
-static std::vector<s_mc_stack_frame_t> unwind_stack_frames(UnwindContext* stack_context,
- const RemoteProcessMemory* process_memory)
-{
- std::vector<s_mc_stack_frame_t> result;
-
- unw_cursor_t c = stack_context->cursor();
-
- // TODO, check condition check (unw_init_local==0 means end of frame)
-
- while (true) {
- s_mc_stack_frame_t stack_frame;
-
- stack_frame.unw_cursor = c;
-
- unw_word_t ip;
- unw_word_t sp;
-
- unw_get_reg(&c, UNW_REG_IP, &ip);
- unw_get_reg(&c, UNW_REG_SP, &sp);
-
- stack_frame.ip = ip;
- stack_frame.sp = sp;
-
- // TODO, use real addresses in frame_t instead of fixing it here
-
- Frame* frame = process_memory->find_function(remote(ip));
- stack_frame.frame = frame;
-
- if (frame) {
- stack_frame.frame_name = frame->name;
- stack_frame.frame_base = (unw_word_t)frame->frame_base(c);
- } else {
- stack_frame.frame_base = 0;
- stack_frame.frame_name = "";
- }
-
- result.push_back(std::move(stack_frame));
-
- /* Stop before context switch with maestro */
- if (frame != nullptr && frame->name == "smx_ctx_wrapper")
- break;
-
- int ret = unw_step(&c);
- xbt_assert(ret >= 0, "Error while unwinding stack");
- xbt_assert(ret != 0, "Unexpected end of stack.");
- }
-
- xbt_assert(not result.empty(), "unw_init_local failed");
-
- return result;
-}
-
-void Snapshot::snapshot_stacks(RemoteProcessMemory& process_memory)
-{
- for (auto const& stack : process_memory.stack_areas()) {
- s_mc_snapshot_stack_t st;
-
- // Read the context from remote process memory:
- unw_context_t context;
- process_memory.read_bytes(&context, sizeof(context), remote(stack.context));
-
- st.context.initialize(process_memory, &context);
-
- st.stack_frames = unwind_stack_frames(&st.context, &process_memory);
- st.local_variables = get_local_variables_values(st.stack_frames, &process_memory);
-
- unw_word_t sp = st.stack_frames[0].sp;
-
- stacks_.push_back(std::move(st));
-
- size_t stack_size = (char*)stack.address + stack.size - (char*)sp;
- stack_sizes_.push_back(stack_size);
- }
-}
-
-void Snapshot::handle_ignore()
-{
- xbt_assert(get_remote_process_memory());
-
- // Copy the memory:
- for (auto const& region : get_remote_process_memory()->ignored_regions()) {
- s_mc_snapshot_ignored_data_t ignored_data;
- ignored_data.start = (void*)region.addr;
- ignored_data.data.resize(region.size);
- get_remote_process_memory()->read_bytes(ignored_data.data.data(), region.size, remote(region.addr));
- ignored_data_.push_back(std::move(ignored_data));
- }
-
- // Zero the memory:
- for (auto const& region : get_remote_process_memory()->ignored_regions())
- get_remote_process_memory()->clear_bytes(remote(region.addr), region.size);
-}
-
-void Snapshot::ignore_restore() const
-{
- for (auto const& ignored_data : ignored_data_)
- get_remote_process_memory()->write_bytes(ignored_data.data.data(), ignored_data.data.size(),
- remote(ignored_data.start));
-}
-
-Snapshot::Snapshot(long num_state, PageStore& store, RemoteProcessMemory& memory)
- : AddressSpace(&memory), page_store_(store), num_state_(num_state)
-{
- XBT_DEBUG("Taking snapshot %ld", num_state);
-
- handle_ignore();
-
- /* Save the std heap and the writable mapped pages of libsimgrid and binary */
- snapshot_regions(memory);
-
- to_ignore_ = memory.ignored_heap();
-
- if (_sg_mc_max_visited_states > 0 || not _sg_mc_property_file.get().empty()) {
- snapshot_stacks(memory);
- hash_ = this->do_hash();
- }
-
- ignore_restore();
-}
-
-void Snapshot::add_region(RegionType type, const RemoteProcessMemory& memory, ObjectInformation* object_info,
- void* start_addr, std::size_t size)
-{
- if (type == RegionType::Data)
- xbt_assert(object_info, "Missing object info for object.");
- else if (type == RegionType::Heap)
- xbt_assert(not object_info, "Unexpected object info for heap region.");
-
- auto region = std::make_unique<Region>(page_store_, memory, type, start_addr, size);
- region->object_info(object_info);
- snapshot_regions_.push_back(std::move(region));
-}
-
-void* Snapshot::read_bytes(void* buffer, std::size_t size, RemotePtr<void> address, ReadOptions options) const
-{
- const Region* region = this->get_region((void*)address.address());
- if (region) {
- void* res = region->read(buffer, (void*)address.address(), size);
- if (buffer == res || options & ReadOptions::lazy())
- return res;
- else {
- memcpy(buffer, res, size);
- return buffer;
- }
- } else
- return this->get_remote_process_memory()->read_bytes(buffer, size, address, options);
-}
-/** @brief Find the snapshotted region from a pointer
- *
- * @param addr Pointer
- * */
-Region* Snapshot::get_region(const void* addr) const
-{
- size_t n = snapshot_regions_.size();
- for (size_t i = 0; i != n; ++i) {
- Region* region = snapshot_regions_[i].get();
- if (not(region && region->contain(simgrid::mc::remote(addr))))
- continue;
-
- return region;
- }
-
- return nullptr;
-}
-
-/** @brief Find the snapshotted region from a pointer, with a hinted_region */
-Region* Snapshot::get_region(const void* addr, Region* hinted_region) const
-{
- if (hinted_region->contain(simgrid::mc::remote(addr)))
- return hinted_region;
- else
- return get_region(addr);
-}
-
-void Snapshot::restore(RemoteProcessMemory& memory) const
-{
- XBT_DEBUG("Restore snapshot %ld", num_state_);
-
- // Restore regions
- for (std::unique_ptr<Region> const& region : snapshot_regions_) {
- region->restore(memory);
- }
-
- ignore_restore();
- memory.clear_cache();
-}
-
-/* ----------- Hashing logic -------------- */
-class djb_hash {
- hash_type state_ = 5381LL;
-
-public:
- template <class T> void update(T& x) { state_ = (state_ << 5) + state_ + x; }
- hash_type value() const { return state_; }
-};
-hash_type Snapshot::do_hash() const
-{
- XBT_DEBUG("START hash %ld", num_state_);
- djb_hash hash;
- // TODO:
- // * nb_processes
- // * heap_bytes_used
- // * root variables
- // * basic stack frame information
- // * stack frame local variables
- XBT_DEBUG("END hash %ld", num_state_);
- return hash.value();
-}
-
-} // namespace simgrid::mc
+++ /dev/null
-/* Copyright (c) 2007-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifndef SIMGRID_MC_SNAPSHOT_HPP
-#define SIMGRID_MC_SNAPSHOT_HPP
-
-#include "src/mc/inspect/mc_unw.hpp"
-#include "src/mc/sosp/Region.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-// ***** MC Snapshot
-
-/** Ignored data
- *
- * Some parts of the snapshot are ignored by zeroing them out: the real values is stored here.
- */
-struct s_mc_snapshot_ignored_data_t {
- void* start;
- std::vector<char> data;
-};
-
-/** Information about a given stack frame */
-struct s_mc_stack_frame_t {
- /** Instruction pointer */
- unw_word_t ip;
- /** Stack pointer */
- unw_word_t sp;
- unw_word_t frame_base;
- simgrid::mc::Frame* frame;
- std::string frame_name;
- unw_cursor_t unw_cursor;
-};
-using mc_stack_frame_t = s_mc_stack_frame_t*;
-
-struct s_local_variable_t {
- simgrid::mc::Frame* subprogram;
- unsigned long ip;
- std::string name;
- simgrid::mc::Type* type;
- void* address;
-};
-using local_variable_t = s_local_variable_t*;
-using const_local_variable_t = const s_local_variable_t*;
-
-struct XBT_PRIVATE s_mc_snapshot_stack_t {
- std::vector<s_local_variable_t> local_variables;
- simgrid::mc::UnwindContext context;
- std::vector<s_mc_stack_frame_t> stack_frames;
-};
-using mc_snapshot_stack_t = s_mc_snapshot_stack_t*;
-using const_mc_snapshot_stack_t = const s_mc_snapshot_stack_t*;
-
-namespace simgrid::mc {
-
-using hash_type = std::uint64_t;
-
-class XBT_PRIVATE Snapshot final : public AddressSpace {
- PageStore& page_store_;
-
-public:
- /* Initialization */
- Snapshot(long num_state, PageStore& store, RemoteProcessMemory& memory);
-
- /* Regular use */
- bool on_heap(const void* address) const
- {
- const s_xbt_mheap_t* heap = get_remote_process_memory()->get_heap();
- return address >= heap->heapbase && address < heap->breakval;
- }
-
- void* read_bytes(void* buffer, std::size_t size, RemotePtr<void> address,
- ReadOptions options = ReadOptions::none()) const override;
- Region* get_region(const void* addr) const;
- Region* get_region(const void* addr, Region* hinted_region) const;
- void restore(RemoteProcessMemory& memory) const;
-
- bool equals_to(const Snapshot& other, RemoteProcessMemory& memory);
-
- // To be private
- long num_state_;
- std::size_t heap_bytes_used_ = 0;
- std::vector<std::unique_ptr<Region>> snapshot_regions_;
- std::vector<std::size_t> stack_sizes_;
- std::vector<s_mc_snapshot_stack_t> stacks_;
- std::vector<simgrid::mc::IgnoredHeapRegion> to_ignore_;
- std::uint64_t hash_ = 0;
- std::vector<s_mc_snapshot_ignored_data_t> ignored_data_;
-
-private:
- void add_region(RegionType type, const RemoteProcessMemory& memory, ObjectInformation* object_info, void* start_addr,
- std::size_t size);
- void snapshot_regions(RemoteProcessMemory& memory);
- void snapshot_stacks(RemoteProcessMemory& memory);
- void handle_ignore();
- void ignore_restore() const;
- hash_type do_hash() const;
-};
-} // namespace simgrid::mc
-
-#endif
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "src/3rd-party/catch.hpp"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/sosp/Snapshot.hpp"
-
-#include <cstddef>
-#include <memory>
-#include <sys/mman.h>
-#include <xbt/random.hpp>
-
-class snap_test_helper {
- simgrid::mc::PageStore page_store_{500};
- simgrid::mc::RemoteProcessMemory memory_{getpid(), nullptr};
-
- struct prologue_return {
- size_t size;
- std::byte* src;
- std::byte* dstn;
- std::unique_ptr<simgrid::mc::Region> region0;
- std::unique_ptr<simgrid::mc::Region> region;
- };
- prologue_return prologue(int n); // common to the below 5 fxs
-
- static void init_memory(std::byte* mem, size_t size);
-
-public:
- void read_whole_region();
- void read_region_parts();
- void compare_whole_region();
- void compare_region_parts();
- void read_pointer();
-
- static void basic_requirements();
-};
-
-void snap_test_helper::init_memory(std::byte* mem, size_t size)
-{
- std::generate_n(mem, size, []() { return static_cast<std::byte>(simgrid::xbt::random::uniform_int(0, 0xff)); });
-}
-
-void snap_test_helper::basic_requirements()
-{
- REQUIRE(xbt_pagesize == getpagesize());
- REQUIRE(1 << xbt_pagebits == xbt_pagesize);
-}
-
-snap_test_helper::prologue_return snap_test_helper::prologue(int n)
-{
- // Store region page(s):
- size_t byte_size = n * xbt_pagesize;
- auto* source =
- static_cast<std::byte*>(mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
- INFO("Could not allocate source memory");
- REQUIRE(source != MAP_FAILED);
-
- // Init memory and take snapshots:
- init_memory(source, byte_size);
- auto region0 =
- std::make_unique<simgrid::mc::Region>(page_store_, memory_, simgrid::mc::RegionType::Data, source, byte_size);
- for (int i = 0; i < n; i += 2) {
- init_memory(source + i * xbt_pagesize, xbt_pagesize);
- }
- auto region =
- std::make_unique<simgrid::mc::Region>(page_store_, memory_, simgrid::mc::RegionType::Data, source, byte_size);
-
- auto* destination =
- static_cast<std::byte*>(mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
- INFO("Could not allocate destination memory");
- REQUIRE(destination != MAP_FAILED);
-
- return {.size = byte_size,
- .src = source,
- .dstn = destination,
- .region0 = std::move(region0),
- .region = std::move(region)};
-}
-
-void snap_test_helper::read_whole_region()
-{
- for (int n = 1; n != 32; ++n) {
- prologue_return ret = prologue(n);
- const void* read = ret.region->read(ret.dstn, ret.src, ret.size);
- INFO("Mismatch in MC_region_read()");
- REQUIRE(not memcmp(ret.src, read, ret.size));
-
- munmap(ret.dstn, ret.size);
- munmap(ret.src, ret.size);
- }
-}
-
-void snap_test_helper::read_region_parts()
-{
- for (int n = 1; n != 32; ++n) {
- prologue_return ret = prologue(n);
-
- for (int j = 0; j != 100; ++j) {
- size_t offset = simgrid::xbt::random::uniform_int(0, ret.size - 1);
- size_t size = simgrid::xbt::random::uniform_int(0, ret.size - offset - 1);
- const void* read = ret.region->read(ret.dstn, (const char*)ret.src + offset, size);
- INFO("Mismatch in MC_region_read()");
- REQUIRE(not memcmp((char*)ret.src + offset, read, size));
- }
- munmap(ret.dstn, ret.size);
- munmap(ret.src, ret.size);
- }
-}
-
-void snap_test_helper::compare_whole_region()
-{
- for (int n = 1; n != 32; ++n) {
- prologue_return ret = prologue(n);
-
- INFO("Unexpected match in MC_snapshot_region_memcmp() with previous snapshot");
- REQUIRE(MC_snapshot_region_memcmp(ret.src, ret.region0.get(), ret.src, ret.region.get(), ret.size));
-
- munmap(ret.dstn, ret.size);
- munmap(ret.src, ret.size);
- }
-}
-
-void snap_test_helper::compare_region_parts()
-{
- for (int n = 1; n != 32; ++n) {
- prologue_return ret = prologue(n);
-
- for (int j = 0; j != 100; ++j) {
- size_t offset = simgrid::xbt::random::uniform_int(0, ret.size - 1);
- size_t size = simgrid::xbt::random::uniform_int(0, ret.size - offset - 1);
-
- INFO("Mismatch in MC_snapshot_region_memcmp()");
- REQUIRE(not MC_snapshot_region_memcmp((char*)ret.src + offset, ret.region.get(), (char*)ret.src + offset,
- ret.region.get(), size));
- }
- munmap(ret.dstn, ret.size);
- munmap(ret.src, ret.size);
- }
-}
-
-const int some_global_variable = 42;
-const void* const some_global_pointer = &some_global_variable;
-void snap_test_helper::read_pointer()
-{
- prologue_return ret = prologue(1);
- memcpy(ret.src, &some_global_pointer, sizeof(void*));
- const simgrid::mc::Region region2(page_store_, memory_, simgrid::mc::RegionType::Data, ret.src, ret.size);
- INFO("Mismtach in MC_region_read_pointer()");
- REQUIRE(MC_region_read_pointer(®ion2, ret.src) == some_global_pointer);
-
- munmap(ret.dstn, ret.size);
- munmap(ret.src, ret.size);
-}
-
-/*************** End: class snap_test_helper *****************************/
-
-TEST_CASE("MC::Snapshot: A copy/snapshot of a given memory region", "MC::Snapshot")
-{
- INFO("Sparse snapshot (using pages)");
-
- snap_test_helper::basic_requirements();
-
- snap_test_helper snap_test;
-
- INFO("Read whole region");
- snap_test.read_whole_region();
-
- INFO("Read region parts");
- snap_test.read_region_parts();
-
- INFO("Compare whole region");
- snap_test.compare_whole_region();
-
- INFO("Compare region parts");
- snap_test.compare_region_parts();
-
- INFO("Read pointer");
- snap_test.read_pointer();
-}
#include "src/simgrid/module.hpp"
#include "src/simgrid/sg_config.hpp"
#include "src/smpi/include/smpi_config.hpp"
-#include "src/xbt/mmalloc/mmalloc.h"
#include <string_view>
[](int value) { simgrid::kernel::context::Context::guard_size = value * xbt_pagesize; }};
static simgrid::config::Flag<int> cfg_context_nthreads{
- "contexts/nthreads", "Number of parallel threads used to execute user contexts", 1, [](int nthreads) {
-#if HAVE_MMALLOC
- xbt_assert(
- nthreads == 1 || not malloc_use_mmalloc(),
- "Parallel simulation is forbidden in the verified program, as there is no protection against race "
- "conditions in mmalloc itself. Please don't be so greedy and show some mercy for our implementation.");
-#endif
- simgrid::kernel::context::Context::set_nthreads(nthreads);
- }};
+ "contexts/nthreads", "Number of parallel threads used to execute user contexts", 1,
+ [](int nthreads) { simgrid::kernel::context::Context::set_nthreads(nthreads); }};
/* synchronization mode for parallel user contexts */
#if HAVE_FUTEX_H
XBT_HELP("This program was linked against %s (git: %s), found in %s.", SIMGRID_VERSION_STRING, SIMGRID_GIT_VERSION,
SIMGRID_INSTALL_PREFIX);
-#if SIMGRID_HAVE_STATEFUL_MC
- XBT_HELP(" Stateful model-checking support compiled in.");
-#else
- XBT_HELP(" Stateful model-checking support disabled at compilation.");
-#endif
-
#if SIMGRID_HAVE_NS3
XBT_HELP(" ns-3 support compiled in.");
#else
timer_ = xbt_os_timer_new();
state_ = SmpiProcessState::UNINITIALIZED;
info_env_ = MPI_INFO_NULL;
- MC_ignore_heap(timer_, xbt_os_timer_size());
#if HAVE_PAPI
if (not smpi_cfg_papi_events_file().empty()) {
Datatype::Datatype(int size, MPI_Aint lb, MPI_Aint ub, int flags) : size_(size), lb_(lb), ub_(ub), flags_(flags)
{
this->add_f();
- MC_ignore(&refcount_, sizeof refcount_);
}
// for predefined types, so refcount_ = 0.
: name_(name), id(std::to_string(ident)), size_(size), lb_(lb), ub_(ub), flags_(flags), refcount_(0)
{
id2type_lookup.try_emplace(id, this);
- MC_ignore(&refcount_, sizeof refcount_);
}
Datatype::Datatype(Datatype* datatype, int* ret)
void Datatype::ref()
{
refcount_++;
- MC_ignore(&refcount_, sizeof refcount_);
}
void Datatype::unref(MPI_Datatype datatype)
{
if (datatype->refcount_ > 0)
datatype->refcount_--;
- MC_ignore(&datatype->refcount_, sizeof datatype->refcount_);
if (datatype->refcount_ == 0 && not(datatype->flags_ & DT_FLAG_PREDEFINED))
delete datatype;
+++ /dev/null
-/* Free a block of memory allocated by `mmalloc'. */
-
-/* Copyright (c) 2010-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1990, 1991, 1992 Free Software Foundation
-
- Written May 1989 by Mike Haertel.
- Heavily modified Mar 1992 by Fred Fish. (fnf@cygnus.com) */
-
-#include "mmprivate.h"
-#include "src/mc/mc.h"
-
-/* Return memory to the heap.
- Like `mfree' but don't call a mfree_hook if there is one. */
-
-/* Return memory to the heap. */
-void mfree(struct mdesc *mdp, void *ptr)
-{
- size_t frag_nb;
- size_t i;
- size_t it;
-
- if (ptr == NULL)
- return;
-
- size_t block = BLOCK(ptr);
-
- if ((char *) ptr < (char *) mdp->heapbase || block > mdp->heapsize) {
- if ((char*)ptr <= (char*)mmalloc_preinit_buffer + mmalloc_preinit_buffer_size &&
- (char*)ptr >= (char*)mmalloc_preinit_buffer)
- /* This points to the static buffer for fake mallocs done by dlsym before mmalloc initialization, ignore it */
- return;
-
- fprintf(stderr,"Ouch, this pointer is not mine, I refuse to free it. Give me valid pointers, or give me death!!\n");
- abort();
- }
-
- int type = mdp->heapinfo[block].type;
-
- switch (type) {
- case MMALLOC_TYPE_HEAPINFO:
- fprintf(stderr, "Asked to free a fragment in a heapinfo block. I'm confused.\n");
- abort();
- break;
-
- case MMALLOC_TYPE_FREE: /* Already free */
- fprintf(stderr, "Asked to free a fragment in a block that is already free. I'm puzzled.\n");
- abort();
- break;
-
- case MMALLOC_TYPE_UNFRAGMENTED:
- /* Get as many statistics as early as we can. */
- mdp -> heapstats.chunks_used--;
- mdp -> heapstats.bytes_used -=
- mdp -> heapinfo[block].busy_block.size * BLOCKSIZE;
- mdp -> heapstats.bytes_free +=
- mdp -> heapinfo[block].busy_block.size * BLOCKSIZE;
-
- if (MC_is_active() && mdp->heapinfo[block].busy_block.ignore > 0)
- MC_unignore_heap(ptr, mdp->heapinfo[block].busy_block.busy_size);
-
- /* Find the free cluster previous to this one in the free list.
- Start searching at the last block referenced; this may benefit
- programs with locality of allocation. */
- i = mdp->heapindex;
- if (i > block) {
- while (i > block) {
- i = mdp->heapinfo[i].free_block.prev;
- }
- } else {
- do {
- i = mdp->heapinfo[i].free_block.next;
- }
- while ((i != 0) && (i < block));
- i = mdp->heapinfo[i].free_block.prev;
- }
-
- /* Determine how to link this block into the free list. */
- if (block == i + mdp->heapinfo[i].free_block.size) {
-
- /* Coalesce this block with its predecessor. */
- mdp->heapinfo[i].free_block.size += mdp->heapinfo[block].busy_block.size;
- /* Mark all my ex-blocks as free */
- for (it=0; it<mdp->heapinfo[block].busy_block.size; it++) {
- if (mdp->heapinfo[block+it].type < 0) {
- fprintf(stderr,
- "Internal Error: Asked to free a block already marked as free (block=%zu it=%zu type=%d). "
- "Please report this bug.\n",
- block, it, mdp->heapinfo[block].type);
- abort();
- }
- mdp->heapinfo[block+it].type = MMALLOC_TYPE_FREE;
- }
-
- block = i;
- } else {
- /* Really link this block back into the free list. */
- mdp->heapinfo[block].free_block.size = mdp->heapinfo[block].busy_block.size;
- mdp->heapinfo[block].free_block.next = mdp->heapinfo[i].free_block.next;
- mdp->heapinfo[block].free_block.prev = i;
- mdp->heapinfo[i].free_block.next = block;
- mdp->heapinfo[mdp->heapinfo[block].free_block.next].free_block.prev = block;
- mdp -> heapstats.chunks_free++;
- /* Mark all my ex-blocks as free */
- for (it=0; it<mdp->heapinfo[block].free_block.size; it++) {
- if (mdp->heapinfo[block+it].type <0) {
- fprintf(stderr,
- "Internal error: Asked to free a block already marked as free (block=%zu it=%zu/%zu type=%d). "
- "Please report this bug.\n",
- block, it, mdp->heapinfo[block].free_block.size, mdp->heapinfo[block].type);
- abort();
- }
- mdp->heapinfo[block+it].type = MMALLOC_TYPE_FREE;
- }
- }
-
- /* Now that the block is linked in, see if we can coalesce it
- with its successor (by deleting its successor from the list
- and adding in its size). */
- if (block + mdp->heapinfo[block].free_block.size ==
- mdp->heapinfo[block].free_block.next) {
- mdp->heapinfo[block].free_block.size
- += mdp->heapinfo[mdp->heapinfo[block].free_block.next].free_block.size;
- mdp->heapinfo[block].free_block.next
- = mdp->heapinfo[mdp->heapinfo[block].free_block.next].free_block.next;
- mdp->heapinfo[mdp->heapinfo[block].free_block.next].free_block.prev = block;
- mdp -> heapstats.chunks_free--;
- }
-
- /* Now see if we can return stuff to the system. */
-#if 0
- blocks = mdp -> heapinfo[block].free.size;
- if (blocks >= FINAL_FREE_BLOCKS && block + blocks == mdp -> heaplimit
- && mdp -> morecore (mdp, 0) == ADDRESS (block + blocks))
- {
- register size_t bytes = blocks * BLOCKSIZE;
- mdp -> heaplimit -= blocks;
- mdp -> morecore (mdp, -bytes);
- mdp -> heapinfo[mdp -> heapinfo[block].free.prev].free.next
- = mdp -> heapinfo[block].free.next;
- mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev
- = mdp -> heapinfo[block].free.prev;
- block = mdp -> heapinfo[block].free.prev;
- mdp -> heapstats.chunks_free--;
- mdp -> heapstats.bytes_free -= bytes;
- }
-#endif
-
- /* Set the next search to begin at this block.
- This is probably important to the trick where realloc returns the block to
- the system before reasking for the same block with a bigger size. */
- mdp->heapindex = block;
- break;
-
- default:
- if (type < 0) {
- fprintf(stderr, "Unknown mmalloc block type.\n");
- abort();
- }
-
- /* Do some of the statistics. */
- mdp -> heapstats.chunks_used--;
- mdp -> heapstats.bytes_used -= 1 << type;
- mdp -> heapstats.chunks_free++;
- mdp -> heapstats.bytes_free += 1 << type;
-
- frag_nb = RESIDUAL(ptr, BLOCKSIZE) >> type;
-
- if( mdp->heapinfo[block].busy_frag.frag_size[frag_nb] == -1){
- fprintf(stderr, "Asked to free a fragment that is already free. I'm puzzled\n");
- abort();
- }
-
- if (MC_is_active() && mdp->heapinfo[block].busy_frag.ignore[frag_nb] > 0)
- MC_unignore_heap(ptr, mdp->heapinfo[block].busy_frag.frag_size[frag_nb]);
-
- /* Set size used in the fragment to -1 */
- mdp->heapinfo[block].busy_frag.frag_size[frag_nb] = -1;
- mdp->heapinfo[block].busy_frag.ignore[frag_nb] = 0;
-
- if (mdp->heapinfo[block].busy_frag.nfree ==
- (BLOCKSIZE >> type) - 1) {
- /* If all fragments of this block are free, remove this block from its swag and free the whole block. */
- xbt_swag_remove(&mdp->heapinfo[block],&mdp->fraghead[type]);
-
- /* pretend that this block is used and free it so that it gets properly coalesced with adjacent free blocks */
- mdp->heapinfo[block].type = MMALLOC_TYPE_UNFRAGMENTED;
- mdp->heapinfo[block].busy_block.size = 1;
- mdp->heapinfo[block].busy_block.busy_size = 0;
-
- /* Keep the statistics accurate. */
- mdp -> heapstats.chunks_used++;
- mdp -> heapstats.bytes_used += BLOCKSIZE;
- mdp -> heapstats.chunks_free -= BLOCKSIZE >> type;
- mdp -> heapstats.bytes_free -= BLOCKSIZE;
-
- mfree(mdp, ADDRESS(block));
- } else if (mdp->heapinfo[block].busy_frag.nfree != 0) {
- /* If some fragments of this block are free, you know what? I'm already happy. */
- ++mdp->heapinfo[block].busy_frag.nfree;
- } else {
- /* No fragments of this block were free before the one we just released,
- * so add this block to the swag and announce that
- it is the first free fragment of this block. */
- mdp->heapinfo[block].busy_frag.nfree = 1;
- mdp->heapinfo[block].freehook.prev = NULL;
- mdp->heapinfo[block].freehook.next = NULL;
-
- xbt_swag_insert(&mdp->heapinfo[block],&mdp->fraghead[type]);
- }
- break;
- }
-}
+++ /dev/null
-/* Build the entire mmalloc library as a single object module. This
- avoids having clients pick up part of their allocation routines
- from mmalloc and part from libc, which results in undefined
- behavior. It should also still be possible to build the library
- as a standard library with multiple objects. */
-
-/* Copyright (c) 2010-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1996, 2000 Free Software Foundation */
-
-#define _GNU_SOURCE
-#include "src/internal_config.h"
-#if HAVE_UNISTD_H
-#include <unistd.h> /* Prototypes for lseek, sbrk (maybe) */
-#endif
-
-#include "swag.c"
-#include "mfree.c"
-#include "mmalloc.c"
-#include "mrealloc.c"
-#include "mmorecore.c"
-#include "mm_legacy.c"
-#include "mm_module.c"
+++ /dev/null
-/* External interface to a mmap'd malloc managed region. */
-
-/* Copyright (c) 2012-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1992, 2000 Free Software Foundation, Inc.
-
- Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
-
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If
- not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
-
-#include <fcntl.h> /* After sys/types.h, at least for dpx/2. */
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "mmprivate.h"
-
-// This is the underlying implementation of mmalloc_get_bytes_used_remote.
-// Is it used directly to evaluate the bytes used from a different process.
-size_t mmalloc_get_bytes_used_remote(size_t heaplimit, const malloc_info* heapinfo)
-{
- int bytes = 0;
- for (size_t i = 0; i < heaplimit; ++i) {
- if (heapinfo[i].type == MMALLOC_TYPE_UNFRAGMENTED) {
- if (heapinfo[i].busy_block.busy_size > 0)
- bytes += heapinfo[i].busy_block.busy_size;
- } else if (heapinfo[i].type > 0) {
- for (size_t j = 0; j < (size_t)(BLOCKSIZE >> heapinfo[i].type); j++) {
- if (heapinfo[i].busy_frag.frag_size[j] > 0)
- bytes += heapinfo[i].busy_frag.frag_size[j];
- }
- }
- }
- return bytes;
-}
+++ /dev/null
-/* Copyright (c) 2010-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Redefine the classical malloc/free/realloc functions so that they fit well in the mmalloc framework */
-#define _GNU_SOURCE
-
-#include "mmprivate.h"
-#include "src/mc/mc_environ.h" // MC_ENV_SOCKET_FD
-#include <dlfcn.h>
-#include <math.h>
-#include <stdlib.h>
-
-/* ***** Whether to use `mmalloc` of the underlying malloc ***** */
-
-static int __malloc_use_mmalloc;
-
-int malloc_use_mmalloc(void)
-{
- return __malloc_use_mmalloc;
-}
-
-/* ***** Current heap ***** */
-
-/* The mmalloc() package can use a single implicit malloc descriptor
- for mmalloc/mrealloc/mfree operations which do not supply an explicit
- descriptor. This allows mmalloc() to provide
- backwards compatibility with the non-mmap'd version. */
-xbt_mheap_t __mmalloc_default_mdp = NULL;
-
-/* The heap we are currently using. */
-static xbt_mheap_t __mmalloc_current_heap = NULL;
-
-xbt_mheap_t mmalloc_get_current_heap(void)
-{
- return __mmalloc_current_heap;
-}
-
-/* Override the malloc-like functions if MC is activated at compile time */
-/* ***** Temporary allocator
- *
- * This is used before we have found the real malloc implementation with dlsym.
- */
-
-static size_t fake_alloc_index;
-static uint64_t buffer[mmalloc_preinit_buffer_size];
-uint64_t* mmalloc_preinit_buffer = buffer;
-
-/* Fake implementations, they are used to fool dlsym:
- * dlsym used calloc and falls back to some other mechanism
- * if this fails.
- */
-static void* mm_fake_malloc(size_t n)
-{
- mmalloc_preinit_buffer = buffer;
-
- // How many uint64_t do w need?
- size_t count = n / sizeof(uint64_t);
- if (n % sizeof(uint64_t))
- count++;
- // Check that we have enough available memory:
- if (fake_alloc_index + count >= mmalloc_preinit_buffer_size) {
- puts("mmalloc is not initialized yet, but the static buffer used as malloc replacement is already exhausted. "
- "Please increase `mmalloc_preinit_buffer_size` in mm_legacy.c\n");
- exit(127);
- }
- // Allocate it:
- uint64_t* res = buffer + fake_alloc_index;
- fake_alloc_index += count;
- return res;
-}
-
-static void* mm_fake_calloc(size_t nmemb, size_t size)
-{
- // This is fresh .bss data, we don't need to clear it:
- size_t n = nmemb * size;
- return mm_fake_malloc(n);
-}
-
-static void* mm_fake_realloc(XBT_ATTRIB_UNUSED void* p, size_t s)
-{
- return mm_fake_malloc(s);
-}
-
-static void mm_fake_free(XBT_ATTRIB_UNUSED void* p)
-{
- // Nothing to do
-}
-
-/* Function signatures for the main malloc functions: */
-typedef void* (*mm_malloc_t)(size_t size);
-typedef void (*mm_free_t)(void*);
-typedef void* (*mm_calloc_t)(size_t nmemb, size_t size);
-typedef void* (*mm_realloc_t)(void *ptr, size_t size);
-
-/* Function pointers to the real/next implementations: */
-static mm_malloc_t mm_real_malloc;
-static mm_free_t mm_real_free;
-static mm_calloc_t mm_real_calloc;
-static mm_realloc_t mm_real_realloc;
-
-static int mm_initializing;
-static int mm_initialized;
-
-/** Constructor functions used to initialize the malloc implementation
- */
-XBT_ATTRIB_CONSTRUCTOR(101) static void mm_legacy_constructor()
-{
- if (mm_initialized)
- return;
- mm_initializing = 1;
- __malloc_use_mmalloc = getenv(MC_ENV_SOCKET_FD) != NULL;
- if (__malloc_use_mmalloc) {
- __mmalloc_current_heap = mmalloc_preinit();
- } else {
-#if HAVE_DLFUNC
- mm_real_realloc = (void *(*)(void *, size_t))dlfunc(RTLD_NEXT, "realloc");
- mm_real_malloc = (void *(*)(size_t))dlfunc(RTLD_NEXT, "malloc");
- mm_real_free = (void (*)(void *))dlfunc(RTLD_NEXT, "free");
- mm_real_calloc = (void *(*)(size_t, size_t))dlfunc(RTLD_NEXT, "calloc");
-#else
- mm_real_realloc = dlsym(RTLD_NEXT, "realloc");
- mm_real_malloc = dlsym(RTLD_NEXT, "malloc");
- mm_real_free = dlsym(RTLD_NEXT, "free");
- mm_real_calloc = dlsym(RTLD_NEXT, "calloc");
-#endif
- }
-
- mm_initializing = 0;
- mm_initialized = 1;
-}
-
-/* ***** malloc/free implementation
- *
- * They call either the underlying/native/RTLD_NEXT implementation (non MC mode)
- * or the mm implementation (MC mode).
- *
- * If we are initializing the malloc subsystem, we call the fake/dummy `malloc`
- * implementation. This is necessary because `dlsym` calls `malloc` and friends.
- */
-
-#define GET_HEAP() __mmalloc_current_heap
-
-void *malloc(size_t n)
-{
- if (!mm_initialized) {
- if (mm_initializing)
- return mm_fake_malloc(n);
- mm_legacy_constructor();
- }
-
- if (!__malloc_use_mmalloc) {
- return mm_real_malloc(n);
- }
-
- xbt_mheap_t mdp = GET_HEAP();
- if (!mdp)
- return NULL;
-
- return mmalloc(mdp, n);
-}
-
-void *calloc(size_t nmemb, size_t size)
-{
- if (!mm_initialized) {
- if (mm_initializing)
- return mm_fake_calloc(nmemb, size);
- mm_legacy_constructor();
- }
-
- if (!__malloc_use_mmalloc) {
- return mm_real_calloc(nmemb, size);
- }
-
- xbt_mheap_t mdp = GET_HEAP();
- if (!mdp)
- return NULL;
-
- void *ret = mmalloc(mdp, nmemb*size);
- // This was already done in the callee:
- if(!(mdp->options & XBT_MHEAP_OPTION_MEMSET)) {
- memset(ret, 0, nmemb * size);
- }
- return ret;
-}
-
-void *realloc(void *p, size_t s)
-{
- if (!mm_initialized) {
- if (mm_initializing)
- return mm_fake_realloc(p, s);
- mm_legacy_constructor();
- }
-
- if (!__malloc_use_mmalloc) {
- return mm_real_realloc(p, s);
- }
-
- xbt_mheap_t mdp = GET_HEAP();
- if (!mdp)
- return NULL;
-
- return mrealloc(mdp, p, s);
-}
-
-void free(void *p)
-{
- if (!mm_initialized) {
- if (mm_initializing)
- return mm_fake_free(p);
- mm_legacy_constructor();
- }
-
- if (!__malloc_use_mmalloc) {
- mm_real_free(p);
- return;
- }
-
- if (!p)
- return;
-
- xbt_mheap_t mdp = GET_HEAP();
- mfree(mdp, p);
-}
+++ /dev/null
-/* Initialization for access to a mmap'd malloc managed region. */
-
-/* Copyright (c) 2012-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1992, 2000 Free Software Foundation, Inc.
-
- Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
-
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If
- not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
-
-#include <sys/types.h>
-#include <fcntl.h> /* After sys/types.h, at least for dpx/2. */
-#include <sys/stat.h>
-#include <string.h>
-#include "mmprivate.h"
-
-/* Initialize access to a mmalloc managed region.
-
- The mapping is established starting at the specified address BASEADDR
- in the process address space.
-
- The provided BASEADDR should be chosen carefully in order to avoid
- bumping into existing mapped regions or future mapped regions.
-
- On success, returns a "malloc descriptor" which is used in subsequent
- calls to other mmalloc package functions. It is explicitly "void *"
- so that users of the package don't have to worry about the actual
- implementation details.
-
- On failure, returns NULL. */
-
-xbt_mheap_t xbt_mheap_new(void* baseaddr, int options)
-{
- /* NULL is not a valid baseaddr as we cannot map anything there. C'mon, user. Think! */
- if (baseaddr == NULL)
- return NULL;
-
- /* We start off with the malloc descriptor allocated on the stack, until we build it up enough to
- * call _mmalloc_mmap_morecore() to allocate the first page of the region and copy it there. Ensure that it is
- * zero'd and then initialize the fields that we know values for. */
-
- struct mdesc mtemp;
- xbt_mheap_t mdp = &mtemp;
- memset((char *) mdp, 0, sizeof(mtemp));
- strncpy(mdp->magic, MMALLOC_MAGIC, MMALLOC_MAGIC_SIZE);
- mdp->headersize = sizeof(mtemp);
- mdp->version = MMALLOC_VERSION;
- mdp->base = mdp->breakval = mdp->top = baseaddr;
- mdp->next_mdesc = NULL;
- mdp->options = options;
-
- /* If we have not been passed a valid open file descriptor for the file
- to map to, then open /dev/zero and use that to map to. */
-
- /* Now try to map in the first page, copy the malloc descriptor structure there, and arrange to return a pointer to
- * this new copy. If the mapping fails, then close the file descriptor if it was opened by us, and arrange to return
- * a NULL. */
-
- void* mbase = mmorecore(mdp, sizeof(mtemp));
- if (mbase == NULL) {
- fprintf(stderr, "morecore failed to get some more memory!\n");
- abort();
- }
- memcpy(mbase, mdp, sizeof(mtemp));
-
- /* Add the new heap to the linked list of heaps attached by mmalloc */
- if(__mmalloc_default_mdp){
- mdp = __mmalloc_default_mdp;
- while(mdp->next_mdesc)
- mdp = mdp->next_mdesc;
-
- mdp->next_mdesc = (struct mdesc *)mbase;
- }
-
- return mbase;
-}
-
-/** Terminate access to a mmalloc managed region by unmapping all memory pages associated with the region, and closing
- * the file descriptor if it is one that we opened.
-
- Returns NULL on success.
-
- Returns the malloc descriptor on failure, which can subsequently be used for further action, such as obtaining more
- information about the nature of the failure.
-
- Note that the malloc descriptor that we are using is currently located in region we are about to unmap, so we first
- make a local copy of it on the stack and use the copy. */
-
-void *xbt_mheap_destroy(xbt_mheap_t mdp)
-{
- if (mdp != NULL) {
- /* Remove the heap from the linked list of heaps attached by mmalloc */
- struct mdesc* mdptemp = __mmalloc_default_mdp;
- while(mdptemp->next_mdesc != mdp )
- mdptemp = mdptemp->next_mdesc;
-
- mdptemp->next_mdesc = mdp->next_mdesc;
-
- struct mdesc mtemp = *mdp;
-
- /* Now unmap all the pages associated with this region by asking for a
- negative increment equal to the current size of the region. */
-
- if (mmorecore(&mtemp, (char *)mtemp.base - (char *)mtemp.breakval) == NULL) {
- /* Deallocating failed. Update the original malloc descriptor with any changes */
- *mdp = mtemp;
- } else {
- mdp = NULL;
- }
- }
-
- return mdp;
-}
-
-/* Safety gap from the heap's break address.
- * Try to increase this first if you experience strange errors under valgrind. */
-#define HEAP_OFFSET (128UL<<20)
-
-/* Initialize the default malloc descriptor.
- *
- * There is no malloc_postexit() destroying the default mdp, because it would break ldl trying to free its memory
- */
-xbt_mheap_t mmalloc_preinit(void)
-{
- if (__mmalloc_default_mdp == NULL) {
- unsigned long mmalloc_pagesize = (unsigned long)sysconf(_SC_PAGESIZE);
- unsigned long mask = ~(mmalloc_pagesize - 1);
- void* addr = (void*)(((unsigned long)sbrk(0) + HEAP_OFFSET) & mask);
- __mmalloc_default_mdp = xbt_mheap_new(addr, XBT_MHEAP_OPTION_MEMSET);
- }
- mmalloc_assert(__mmalloc_default_mdp != NULL, "__mmalloc_default_mdp cannot be NULL");
-
- return __mmalloc_default_mdp;
-}
+++ /dev/null
-/* Memory allocator `malloc'. */
-
-/* Copyright (c) 2010-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1990, 1991, 1992 Free Software Foundation
-
- Written May 1989 by Mike Haertel.
- Heavily modified Mar 1992 by Fred Fish for mmap'd version. */
-
-#include <string.h> /* Prototypes for memcpy, memmove, memset, etc */
-#include <stdio.h>
-#include "mmprivate.h"
-
-/* Prototypes for local functions */
-
-static void initialize(xbt_mheap_t mdp);
-static void *register_morecore(xbt_mheap_t mdp, size_t size);
-static void* mmalloc_aligned(xbt_mheap_t mdp, size_t size);
-
-/* Allocation aligned on block boundary.
- *
- * It never returns NULL, but dies verbosely on error.
- */
-static void* mmalloc_aligned(struct mdesc* mdp, size_t size)
-{
- void *result;
- unsigned long int adj;
-
- result = mmorecore(mdp, size);
-
- /* if this reservation does not fill up the last block of our resa,
- * complete the reservation by also asking for the full latest block.
- *
- * Also, the returned block is aligned to the end of block (but I've
- * no fucking idea of why, actually -- http://abstrusegoose.com/432 --
- * but not doing so seems to lead to issues).
- */
- adj = RESIDUAL(result, BLOCKSIZE);
- if (adj != 0) {
- adj = BLOCKSIZE - adj;
- mmorecore(mdp, adj);
- result = (char *) result + adj;
- }
- return result;
-}
-
-/** Initialize heapinfo about the heapinfo pages :) */
-static void initialize_heapinfo_heapinfo(const s_xbt_mheap_t* mdp)
-{
- // Update heapinfo about the heapinfo pages (!):
- mmalloc_assert((uintptr_t)mdp->heapinfo % BLOCKSIZE == 0, "Failed assert in initialize_heapinfo_heapinfo()");
- size_t block = BLOCK(mdp->heapinfo);
- size_t nblocks = mdp->heapsize * sizeof(malloc_info) / BLOCKSIZE;
- // Mark them as free:
- for (size_t j=0; j!=nblocks; ++j) {
- mdp->heapinfo[block+j].type = MMALLOC_TYPE_FREE;
- mdp->heapinfo[block+j].free_block.size = 0;
- mdp->heapinfo[block+j].free_block.next = 0;
- mdp->heapinfo[block+j].free_block.prev = 0;
- }
- mdp->heapinfo[block].free_block.size = nblocks;
-}
-
-/* Finish the initialization of the mheap. If we want to inline it
- * properly, we need to make the mmalloc_aligned function publicly visible, too */
-static void initialize(xbt_mheap_t mdp)
-{
- // Update mdp meta-data:
- mdp->heapsize = HEAP / BLOCKSIZE;
- mdp->heapinfo = (malloc_info*)mmalloc_aligned(mdp, mdp->heapsize * sizeof(malloc_info));
- mdp->heapbase = (void *) mdp->heapinfo;
- mdp->flags |= MMALLOC_INITIALIZED;
-
- // Update root heapinfo:
- memset((void *) mdp->heapinfo, 0, mdp->heapsize * sizeof(malloc_info));
- mdp->heapinfo[0].type = MMALLOC_TYPE_FREE;
- mdp->heapinfo[0].free_block.size = 0;
- mdp->heapinfo[0].free_block.next = mdp->heapinfo[0].free_block.prev = 0;
- mdp->heapindex = 0;
-
- initialize_heapinfo_heapinfo(mdp);
-
- for (int i = 0; i < BLOCKLOG; i++) {
- malloc_info mi; /* to compute the offset of the swag hook */
- xbt_swag_init(&(mdp->fraghead[i]), xbt_swag_offset(mi, freehook));
- }
-}
-
-static inline void update_hook(void **a, size_t offset)
-{
- if (*a)
- *a = (char*)*a + offset;
-}
-
-/* Get neatly aligned memory from the low level layers, and register it
- * into the heap info table as necessary. */
-static void *register_morecore(struct mdesc *mdp, size_t size)
-{
- void* result = mmalloc_aligned(mdp, size); // Never returns NULL
-
- /* Check if we need to grow the info table (in a multiplicative manner) */
- if (BLOCK((char*)result + size) > mdp->heapsize) {
- size_t newsize = mdp->heapsize;
- while (BLOCK((char*)result + size) > newsize)
- newsize *= 2;
-
- /* Copy old info into new location */
- malloc_info* oldinfo = mdp->heapinfo;
- malloc_info* newinfo = (malloc_info*)mmalloc_aligned(mdp, newsize * sizeof(malloc_info));
- memcpy(newinfo, oldinfo, mdp->heapsize * sizeof(malloc_info));
-
- /* Initialize the new blockinfo : */
- memset((char*) newinfo + mdp->heapsize * sizeof(malloc_info), 0,
- (newsize - mdp->heapsize)* sizeof(malloc_info));
-
- /* Update the swag of busy blocks containing free fragments by applying the offset to all swag_hooks. Yeah. My hand is right in the fan and I still type */
- size_t offset=((char*)newinfo)-((char*)oldinfo);
-
- for (size_t i = 1 /*first element of heapinfo describes the mdesc area*/; i < mdp->heaplimit; i++) {
- update_hook(&newinfo[i].freehook.next, offset);
- update_hook(&newinfo[i].freehook.prev, offset);
- }
- // also update the starting points of the swag
- for (int i = 0; i < BLOCKLOG; i++) {
- update_hook(&mdp->fraghead[i].head, offset);
- update_hook(&mdp->fraghead[i].tail, offset);
- }
- mdp->heapinfo = newinfo;
-
- /* mark the space previously occupied by the block info as free by first marking it
- * as occupied in the regular way, and then freing it */
- for (size_t it = 0; it < BLOCKIFY(mdp->heapsize * sizeof(malloc_info)); it++) {
- newinfo[BLOCK(oldinfo)+it].type = MMALLOC_TYPE_UNFRAGMENTED;
- newinfo[BLOCK(oldinfo)+it].busy_block.ignore = 0;
- }
-
- newinfo[BLOCK(oldinfo)].busy_block.size = BLOCKIFY(mdp->heapsize * sizeof(malloc_info));
- newinfo[BLOCK(oldinfo)].busy_block.busy_size = size;
- mfree(mdp, (void *) oldinfo);
- mdp->heapsize = newsize;
-
- initialize_heapinfo_heapinfo(mdp);
- }
-
- mdp->heaplimit = BLOCK((char *) result + size);
- return result;
-}
-
-/* Allocate memory from the heap. */
-void *mmalloc(xbt_mheap_t mdp, size_t size) {
- void *res= mmalloc_no_memset(mdp,size);
- if (mdp->options & XBT_MHEAP_OPTION_MEMSET) {
- memset(res,0,size);
- }
- return res;
-}
-
-static void mmalloc_mark_used(xbt_mheap_t mdp, size_t block, size_t nblocks, size_t requested_size)
-{
- for (size_t it = 0; it < nblocks; it++) {
- mdp->heapinfo[block + it].type = MMALLOC_TYPE_UNFRAGMENTED;
- mdp->heapinfo[block + it].busy_block.busy_size = 0;
- mdp->heapinfo[block + it].busy_block.ignore = 0;
- mdp->heapinfo[block + it].busy_block.size = 0;
- }
- mdp->heapinfo[block].busy_block.size = nblocks;
- mdp->heapinfo[block].busy_block.busy_size = requested_size;
- mdp->heapstats.chunks_used++;
- mdp->heapstats.bytes_used += nblocks * BLOCKSIZE;
-}
-
-/* Splitting mmalloc this way is mandated by a trick in mrealloc, that gives
- back the memory of big blocks to the system before reallocating them: we don't
- want to loose the beginning of the area when this happens */
-void *mmalloc_no_memset(xbt_mheap_t mdp, size_t size)
-{
- void *result;
- size_t block;
-
- size_t requested_size = size; // The amount of memory requested by user, for real
-
- /* Work even if the user was stupid enough to ask a ridiculously small block (even 0-length),
- * ie return a valid block that can be realloced and freed.
- * glibc malloc does not use this trick but return a constant pointer, but we need to enlist the free fragments later on.
- */
- if (size < SMALLEST_POSSIBLE_MALLOC)
- size = SMALLEST_POSSIBLE_MALLOC;
-
- if (!(mdp->flags & MMALLOC_INITIALIZED))
- initialize(mdp);
-
- /* Determine the allocation policy based on the request size. */
- if (size <= BLOCKSIZE / 2) {
- /* Small allocation to receive a fragment of a block.
- Determine the logarithm to base two of the fragment size. */
- size_t log = 1;
- --size;
- while ((size /= 2) != 0) {
- ++log;
- }
-
- /* Look in the fragment lists for a free fragment of the desired size. */
- if (xbt_swag_size(&mdp->fraghead[log])>0) {
- /* There are free fragments of this size; Get one of them and prepare to return it.
- Update the block's nfree and if no other free fragment, get out of the swag. */
-
- /* search a fragment that I could return as a result */
- malloc_info *candidate_info = xbt_swag_getFirst(&mdp->fraghead[log]);
- size_t candidate_block = (candidate_info - &(mdp->heapinfo[0]));
- size_t candidate_frag;
- for (candidate_frag=0;candidate_frag<(size_t) (BLOCKSIZE >> log);candidate_frag++)
- if (candidate_info->busy_frag.frag_size[candidate_frag] == -1)
- break;
- mmalloc_assert(candidate_frag < (size_t)(BLOCKSIZE >> log),
- "Block %zu was registered as containing free fragments of type %zu, but I can't find any",
- candidate_block, log);
-
- result = (void*) (((char*)ADDRESS(candidate_block)) + (candidate_frag << log));
-
- /* Remove this fragment from the list of free guys */
- candidate_info->busy_frag.nfree--;
- if (candidate_info->busy_frag.nfree == 0) {
- xbt_swag_remove(candidate_info,&mdp->fraghead[log]);
- }
-
- /* Update our metadata about this fragment */
- candidate_info->busy_frag.frag_size[candidate_frag] = requested_size;
- candidate_info->busy_frag.ignore[candidate_frag] = 0;
-
- /* Update the statistics. */
- mdp -> heapstats.chunks_used++;
- mdp -> heapstats.bytes_used += 1 << log;
- mdp -> heapstats.chunks_free--;
- mdp -> heapstats.bytes_free -= 1 << log;
-
- } else {
- /* No free fragments of the desired size, so get a new block
- and break it into fragments, returning the first. */
-
- result = mmalloc(mdp, BLOCKSIZE); // does not return NULL
- block = BLOCK(result);
-
- mdp->heapinfo[block].type = (int)log;
- /* Link all fragments but the first as free, and add the block to the swag of blocks containing free frags */
- size_t i;
- for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i) {
- mdp->heapinfo[block].busy_frag.frag_size[i] = -1;
- mdp->heapinfo[block].busy_frag.ignore[i] = 0;
- }
- mdp->heapinfo[block].busy_frag.nfree = i - 1;
- mdp->heapinfo[block].freehook.prev = NULL;
- mdp->heapinfo[block].freehook.next = NULL;
-
- xbt_swag_insert(&mdp->heapinfo[block], &(mdp->fraghead[log]));
-
- /* mark the fragment returned as busy */
- mdp->heapinfo[block].busy_frag.frag_size[0] = requested_size;
- mdp->heapinfo[block].busy_frag.ignore[0] = 0;
-
- /* update stats */
- mdp -> heapstats.chunks_free += (BLOCKSIZE >> log) - 1;
- mdp -> heapstats.bytes_free += BLOCKSIZE - (1 << log);
- mdp -> heapstats.bytes_used -= BLOCKSIZE - (1 << log);
- }
- } else {
- /* Large allocation to receive one or more blocks.
- Search the free list in a circle starting at the last place visited.
- If we loop completely around without finding a large enough
- space we will have to get more memory from the system. */
- size_t blocks = BLOCKIFY(size);
- size_t start = MALLOC_SEARCH_START;
- block = MALLOC_SEARCH_START;
- while (mdp->heapinfo[block].free_block.size < blocks) {
- if (mdp->heapinfo[block].type >=0) { // Don't trust xbt_die and friends in malloc-level library, you fool!
- fprintf(stderr,
- "Internal error: found a free block not marked as such (block=%zu type=%d). Please report this bug.\n",
- block, mdp->heapinfo[block].type);
- abort();
- }
-
- block = mdp->heapinfo[block].free_block.next;
- if (block == start) {
- /* Need to get more from the system. Check to see if
- the new core will be contiguous with the final free
- block; if so we don't need to get as much. */
- block = mdp->heapinfo[0].free_block.prev;
- size_t lastblocks = mdp->heapinfo[block].free_block.size;
- if (mdp->heaplimit != 0 &&
- block + lastblocks == mdp->heaplimit &&
- mmorecore(mdp, 0) == ADDRESS(block + lastblocks) &&
- (register_morecore(mdp, (blocks - lastblocks) * BLOCKSIZE)) != NULL) {
- /* Which block we are extending (the `final free
- block' referred to above) might have changed, if
- it got combined with a freed info table. */
- block = mdp->heapinfo[0].free_block.prev;
-
- mdp->heapinfo[block].free_block.size += (blocks - lastblocks);
- continue;
- }
- result = register_morecore(mdp, blocks * BLOCKSIZE);
-
- block = BLOCK(result);
- mmalloc_mark_used(mdp, block, blocks, requested_size);
-
- return result;
- }
- /* Need large block(s), but found some in the existing heap */
- }
-
- /* At this point we have found a suitable free list entry.
- Figure out how to remove what we need from the list. */
- result = ADDRESS(block);
- if (mdp->heapinfo[block].free_block.size > blocks) {
- /* The block we found has a bit left over,
- so relink the tail end back into the free list. */
- mdp->heapinfo[block + blocks].free_block.size
- = mdp->heapinfo[block].free_block.size - blocks;
- mdp->heapinfo[block + blocks].free_block.next
- = mdp->heapinfo[block].free_block.next;
- mdp->heapinfo[block + blocks].free_block.prev
- = mdp->heapinfo[block].free_block.prev;
- mdp->heapinfo[mdp->heapinfo[block].free_block.prev].free_block.next
- = mdp->heapinfo[mdp->heapinfo[block].free_block.next].free_block.prev
- = mdp->heapindex = block + blocks;
- } else {
- /* The block exactly matches our requirements,
- so just remove it from the list. */
- mdp->heapinfo[mdp->heapinfo[block].free_block.next].free_block.prev
- = mdp->heapinfo[block].free_block.prev;
- mdp->heapinfo[mdp->heapinfo[block].free_block.prev].free_block.next
- = mdp->heapindex = mdp->heapinfo[block].free_block.next;
- }
-
- mmalloc_mark_used(mdp, block, blocks, requested_size);
- mdp -> heapstats.bytes_free -= blocks * BLOCKSIZE;
-
- }
-
- return result;
-}
+++ /dev/null
-/* Copyright (c) 2010-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
- This file was then part of the GNU C Library. */
-
-#ifndef SIMGRID_MMALLOC_H
-#define SIMGRID_MMALLOC_H
-
-#include "src/internal_config.h"
-
-#include <stdio.h> /* for NULL */
-#include <sys/types.h> /* for size_t */
-
-SG_BEGIN_DECL
-
-/* Datatype representing a separate heap. The whole point of the mmalloc module is to allow several such heaps in the
- * process. It thus works by redefining all the classical memory management functions (malloc and friends) with an
- * extra first argument: the heap in which the memory is to be taken.
- *
- * The heap structure itself is an opaque object that shouldn't be messed with.
- */
-typedef struct mdesc s_xbt_mheap_t;
-typedef s_xbt_mheap_t* xbt_mheap_t;
-
-#if HAVE_MMALLOC
-/* Allocate SIZE bytes of memory (and memset it to 0). */
-XBT_PUBLIC void* mmalloc(xbt_mheap_t md, size_t size);
-
-/* Allocate SIZE bytes of memory (and don't mess with it) */
-void* mmalloc_no_memset(xbt_mheap_t mdp, size_t size);
-
-/* Re-allocate the previously allocated block in void*, making the new block SIZE bytes long. */
-XBT_PUBLIC void* mrealloc(xbt_mheap_t md, void* ptr, size_t size);
-
-/* Free a block allocated by `mmalloc', `mrealloc' or `mcalloc'. */
-XBT_PUBLIC void mfree(xbt_mheap_t md, void* ptr);
-
-#define XBT_MHEAP_OPTION_MEMSET 1
-
-XBT_PUBLIC xbt_mheap_t xbt_mheap_new(void* baseaddr, int options);
-
-XBT_PUBLIC void* xbt_mheap_destroy(xbt_mheap_t md);
-
-/* To get the heap used when using the legacy version malloc/free/realloc and such */
-xbt_mheap_t mmalloc_get_current_heap(void);
-
-/* Returns true if we are using the internal mmalloc, and false if we are using the libc's malloc */
-XBT_PUBLIC int malloc_use_mmalloc(void);
-
-#endif
-SG_END_DECL
-
-#endif /* SIMGRID_MMALLOC_H */
+++ /dev/null
-This is ./mmalloc.info, produced by makeinfo version 4.6 from
-mmalloc.texi.
-
-START-INFO-DIR-ENTRY
-* Mmalloc: (mmalloc). The GNU mapped-malloc package.
-END-INFO-DIR-ENTRY
-
- This file documents the GNU mmalloc (mapped-malloc) package, written
-by fnf@cygnus.com, based on GNU malloc written by mike@ai.mit.edu.
-
- Copyright (C) 1992 Free Software Foundation, Inc.
-
- Permission is granted to make and distribute verbatim copies of this
-manual provided the copyright notice and this permission notice are
-preserved on all copies.
-
- Permission is granted to copy and distribute modified versions of
-this manual under the conditions for verbatim copying, provided also
-that the entire resulting derived work is distributed under the terms
-of a permission notice identical to this one.
-
- Permission is granted to copy and distribute translations of this
-manual into another language, under the above conditions for modified
-versions.
-
-\1f
-File: mmalloc.info, Node: Top, Next: Overview, Prev: (dir), Up: (dir)
-
-mmalloc
-*******
-
-This file documents the GNU memory-mapped malloc package mmalloc.
-
-* Menu:
-
-* Overview:: Overall Description
-* Implementation:: Implementation
-
- --- The Detailed Node Listing ---
-
-Implementation
-
-* Compatibility:: Backwards Compatibility
-* Functions:: Function Descriptions
-
-\1f
-File: mmalloc.info, Node: Overview, Next: Implementation, Prev: Top, Up: Top
-
-Overall Description
-*******************
-
-This is a heavily modified version of GNU `malloc'. It uses `mmap' as
-the basic mechanism for obtaining memory from the system, rather than
-`sbrk'. This gives it several advantages over the more traditional
-malloc:
-
- * Several different heaps can be used, each of them growing or
- shinking under control of `mmap', with the `mmalloc' functions
- using a specific heap on a call by call basis.
-
- * By using `mmap', it is easy to create heaps which are intended to
- be persistent and exist as a filesystem object after the creating
- process has gone away.
-
- * Because multiple heaps can be managed, data used for a specific
- purpose can be allocated into its own heap, making it easier to
- allow applications to "dump" and "restore" initialized
- malloc-managed memory regions. For example, the "unexec" hack
- popularized by GNU Emacs could potentially go away.
-
-\1f
-File: mmalloc.info, Node: Implementation, Prev: Overview, Up: Top
-
-Implementation
-**************
-
-The `mmalloc' functions contain no internal static state. All
-`mmalloc' internal data is allocated in the mapped in region, along
-with the user data that it manages. This allows it to manage multiple
-such regions and to "pick up where it left off" when such regions are
-later dynamically mapped back in.
-
- In some sense, malloc has been "purified" to contain no internal
-state information and generalized to use multiple memory regions rather
-than a single region managed by `sbrk'. However the new routines now
-need an extra parameter which informs `mmalloc' which memory region it
-is dealing with (along with other information). This parameter is
-called the "malloc descriptor".
-
- The functions initially provided by `mmalloc' are:
-
- void *mmalloc_attach (int fd, void *baseaddr);
- void *mmalloc_detach (void *md);
- int mmalloc_errno (void *md);
- int mmalloc_setkey (void *md, int keynum, void *key);
- void *mmalloc_getkey (void *md, int keynum);
-
- void *mmalloc (void *md, size_t size);
- void *mrealloc (void *md, void *ptr, size_t size);
- void *mvalloc (void *md, size_t size);
- void mfree (void *md, void *ptr);
-
-* Menu:
-
-* Compatibility:: Backwards Compatibility
-* Functions:: Function Descriptions
-
-\1f
-File: mmalloc.info, Node: Compatibility, Next: Functions, Prev: Implementation, Up: Implementation
-
-Backwards Compatibility
-=======================
-
-To allow a single malloc package to be used in a given application,
-provision is made for the traditional `malloc', `realloc', and `free'
-functions to be implemented as special cases of the `mmalloc'
-functions. In particular, if any of the functions that expect malloc
-descriptors are called with a `NULL' pointer rather than a valid malloc
-descriptor, then they default to using an `sbrk' managed region. The
-`mmalloc' package provides compatible `malloc', `realloc', and `free'
-functions using this mechanism internally. Applications can avoid this
-extra interface layer by simply including the following defines:
-
- #define malloc(size) mmalloc ((void *)0, (size))
- #define realloc(ptr,size) mrealloc ((void *)0, (ptr), (size));
- #define free(ptr) mfree ((void *)0, (ptr))
-
-or replace the existing `malloc', `realloc', and `free' calls with the
-above patterns if using `#define' causes problems.
-
-\1f
-File: mmalloc.info, Node: Functions, Prev: Compatibility, Up: Implementation
-
-Function Descriptions
-=====================
-
-These are the details on the functions that make up the `mmalloc'
-package.
-
-`void *mmalloc_attach (int FD, void *BASEADDR);'
- Initialize access to a `mmalloc' managed region.
-
- If FD is a valid file descriptor for an open file, then data for
- the `mmalloc' managed region is mapped to that file. Otherwise
- `/dev/zero' is used and the data will not exist in any filesystem
- object.
-
- If the open file corresponding to FD is from a previous use of
- `mmalloc' and passes some basic sanity checks to ensure that it is
- compatible with the current `mmalloc' package, then its data is
- mapped in and is immediately accessible at the same addresses in
- the current process as the process that created the file.
-
- If BASEADDR is not `NULL', the mapping is established starting at
- the specified address in the process address space. If BASEADDR
- is `NULL', the `mmalloc' package chooses a suitable address at
- which to start the mapped region, which will be the value of the
- previous mapping if opening an existing file which was previously
- built by `mmalloc', or for new files will be a value chosen by
- `mmap'.
-
- Specifying BASEADDR provides more control over where the regions
- start and how big they can be before bumping into existing mapped
- regions or future mapped regions.
-
- On success, returns a malloc descriptor which is used in subsequent
- calls to other `mmalloc' package functions. It is explicitly
- `void *' (`char *' for systems that don't fully support `void') so
- that users of the package don't have to worry about the actual
- implementation details.
-
- On failure returns `NULL'.
-
-`void *mmalloc_detach (void *MD);'
- Terminate access to a `mmalloc' managed region identified by the
- descriptor MD, by closing the base file and unmapping all memory
- pages associated with the region.
-
- Returns `NULL' on success.
-
- Returns the malloc descriptor on failure, which can subsequently
- be used for further action (such as obtaining more information
- about the nature of the failure).
-
-`void *mmalloc (void *MD, size_t SIZE);'
- Given an `mmalloc' descriptor MD, allocate additional memory of
- SIZE bytes in the associated mapped region.
-
-`*mrealloc (void *MD, void *PTR, size_t SIZE);'
- Given an `mmalloc' descriptor MD and a pointer to memory
- previously allocated by `mmalloc' in PTR, reallocate the memory to
- be SIZE bytes long, possibly moving the existing contents of
- memory if necessary.
-
-`void *mvalloc (void *MD, size_t SIZE);'
- Like `mmalloc' but the resulting memory is aligned on a page
- boundary.
-
-`void mfree (void *MD, void *PTR);'
- Given an `mmalloc' descriptor MD and a pointer to memory previously
- allocated by `mmalloc' in PTR, free the previously allocated
- memory.
-
-`int mmalloc_errno (void *MD);'
- Given a `mmalloc' descriptor, if the last `mmalloc' operation
- failed for some reason due to a system call failure, then returns
- the associated `errno'. Returns 0 otherwise. (This function is
- not yet implemented).
-
-
-\1f
-Tag Table:
-Node: Top\7f937
-Node: Overview\7f1370
-Node: Implementation\7f2395
-Node: Compatibility\7f3785
-Node: Functions\7f4856
-\1f
-End Tag Table
+++ /dev/null
-\input texinfo @c -*- Texinfo -*-
-@setfilename mmalloc.info
-
-@ifinfo
-@format
-START-INFO-DIR-ENTRY
-* Mmalloc: (mmalloc). The GNU mapped-malloc package.
-END-INFO-DIR-ENTRY
-@end format
-
-This file documents the GNU mmalloc (mapped-malloc) package, written by
-fnf@@cygnus.com, based on GNU malloc written by mike@@ai.mit.edu.
-
-Copyright (C) 1992 Free Software Foundation, Inc.
-
-Permission is granted to make and distribute verbatim copies of
-this manual provided the copyright notice and this permission notice
-are preserved on all copies.
-
-@ignore
-Permission is granted to process this file through Tex and print the
-results, provided the printed document carries copying permission
-notice identical to this one except for the removal of this paragraph
-(this paragraph not being relevant to the printed manual).
-
-@end ignore
-Permission is granted to copy and distribute modified versions of this
-manual under the conditions for verbatim copying, provided also that the
-entire resulting derived work is distributed under the terms of a
-permission notice identical to this one.
-
-Permission is granted to copy and distribute translations of this manual
-into another language, under the above conditions for modified versions.
-@end ifinfo
-@iftex
-@c @finalout
-@setchapternewpage odd
-@settitle MMALLOC, the GNU memory-mapped malloc package
-@titlepage
-@title mmalloc
-@subtitle The GNU memory-mapped malloc package
-@author Fred Fish
-@author Cygnus Support
-@author Mike Haertel
-@author Free Software Foundation
-@page
-
-@tex
-\def\$#1${{#1}} % Kluge: collect RCS revision info without $...$
-\xdef\manvers{\$Revision: 1.4 $} % For use in headers, footers too
-{\parskip=0pt
-\hfill Cygnus Support\par
-\hfill fnf\@cygnus.com\par
-\hfill {\it MMALLOC, the GNU memory-mapped malloc package}, \manvers\par
-\hfill \TeX{}info \texinfoversion\par
-}
-@end tex
-
-@vskip 0pt plus 1filll
-Copyright @copyright{} 1992 Free Software Foundation, Inc.
-
-Permission is granted to make and distribute verbatim copies of
-this manual provided the copyright notice and this permission notice
-are preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of this
-manual under the conditions for verbatim copying, provided also that
-the entire resulting derived work is distributed under the terms of a
-permission notice identical to this one.
-
-Permission is granted to copy and distribute translations of this manual
-into another language, under the above conditions for modified versions.
-@end titlepage
-@end iftex
-
-@ifinfo
-@node Top, Overview, (dir), (dir)
-@top mmalloc
-This file documents the GNU memory-mapped malloc package mmalloc.
-
-@menu
-* Overview:: Overall Description
-* Implementation:: Implementation
-
- --- The Detailed Node Listing ---
-
-Implementation
-
-* Compatibility:: Backwards Compatibility
-* Functions:: Function Descriptions
-@end menu
-
-@end ifinfo
-
-@node Overview, Implementation, Top, Top
-@chapter Overall Description
-
-This is a heavily modified version of GNU @code{malloc}. It uses
-@code{mmap} as the basic mechanism for obtaining memory from the
-system, rather than @code{sbrk}. This gives it several advantages over the
-more traditional malloc:
-
-@itemize @bullet
-@item
-Several different heaps can be used, each of them growing
-or shinking under control of @code{mmap}, with the @code{mmalloc} functions
-using a specific heap on a call by call basis.
-
-@item
-By using @code{mmap}, it is easy to create heaps which are intended to
-be persistent and exist as a filesystem object after the creating
-process has gone away.
-
-@item
-Because multiple heaps can be managed, data used for a
-specific purpose can be allocated into its own heap, making
-it easier to allow applications to ``dump'' and ``restore'' initialized
-malloc-managed memory regions. For example, the ``unexec'' hack popularized
-by GNU Emacs could potentially go away.
-@end itemize
-
-@node Implementation, , Overview, Top
-@chapter Implementation
-
-The @code{mmalloc} functions contain no internal static state. All
-@code{mmalloc} internal data is allocated in the mapped in region, along
-with the user data that it manages. This allows it to manage multiple
-such regions and to ``pick up where it left off'' when such regions are
-later dynamically mapped back in.
-
-In some sense, malloc has been ``purified'' to contain no internal state
-information and generalized to use multiple memory regions rather than a
-single region managed by @code{sbrk}. However the new routines now need an
-extra parameter which informs @code{mmalloc} which memory region it is dealing
-with (along with other information). This parameter is called the
-@dfn{malloc descriptor}.
-
-The functions initially provided by @code{mmalloc} are:
-
-@example
-void *mmalloc_attach (int fd, void *baseaddr);
-void *mmalloc_detach (void *md);
-int mmalloc_errno (void *md);
-int mmalloc_setkey (void *md, int keynum, void *key);
-void *mmalloc_getkey (void *md, int keynum);
-
-void *mmalloc (void *md, size_t size);
-void *mrealloc (void *md, void *ptr, size_t size);
-void *mvalloc (void *md, size_t size);
-void mfree (void *md, void *ptr);
-@end example
-
-@menu
-* Compatibility:: Backwards Compatibility
-* Functions:: Function Descriptions
-@end menu
-
-@node Compatibility, Functions, Implementation, Implementation
-@section Backwards Compatibility
-
-To allow a single malloc package to be used in a given application,
-provision is made for the traditional @code{malloc}, @code{realloc}, and
-@code{free} functions to be implemented as special cases of the
-@code{mmalloc} functions. In particular, if any of the functions that
-expect malloc descriptors are called with a @code{NULL} pointer rather than a
-valid malloc descriptor, then they default to using an @code{sbrk} managed
-region.
-The @code{mmalloc} package provides compatible @code{malloc}, @code{realloc},
-and @code{free} functions using this mechanism internally.
-Applications can avoid this extra interface layer by simply including the
-following defines:
-
-@example
-#define malloc(size) mmalloc ((void *)0, (size))
-#define realloc(ptr,size) mrealloc ((void *)0, (ptr), (size));
-#define free(ptr) mfree ((void *)0, (ptr))
-@end example
-
-@noindent
-or replace the existing @code{malloc}, @code{realloc}, and @code{free}
-calls with the above patterns if using @code{#define} causes problems.
-
-@node Functions, , Compatibility, Implementation
-@section Function Descriptions
-
-These are the details on the functions that make up the @code{mmalloc}
-package.
-
-@table @code
-@item void *mmalloc_attach (int @var{fd}, void *@var{baseaddr});
-Initialize access to a @code{mmalloc} managed region.
-
-If @var{fd} is a valid file descriptor for an open file, then data for the
-@code{mmalloc} managed region is mapped to that file. Otherwise
-@file{/dev/zero} is used and the data will not exist in any filesystem object.
-
-If the open file corresponding to @var{fd} is from a previous use of
-@code{mmalloc} and passes some basic sanity checks to ensure that it is
-compatible with the current @code{mmalloc} package, then its data is
-mapped in and is immediately accessible at the same addresses in
-the current process as the process that created the file.
-
-If @var{baseaddr} is not @code{NULL}, the mapping is established
-starting at the specified address in the process address space. If
-@var{baseaddr} is @code{NULL}, the @code{mmalloc} package chooses a
-suitable address at which to start the mapped region, which will be the
-value of the previous mapping if opening an existing file which was
-previously built by @code{mmalloc}, or for new files will be a value
-chosen by @code{mmap}.
-
-Specifying @var{baseaddr} provides more control over where the regions
-start and how big they can be before bumping into existing mapped
-regions or future mapped regions.
-
-On success, returns a malloc descriptor which is used in subsequent
-calls to other @code{mmalloc} package functions. It is explicitly
-@samp{void *} (@samp{char *} for systems that don't fully support
-@code{void}) so that users of the package don't have to worry about the
-actual implementation details.
-
-On failure returns @code{NULL}.
-
-@item void *mmalloc_detach (void *@var{md});
-Terminate access to a @code{mmalloc} managed region identified by the
-descriptor @var{md}, by closing the base file and unmapping all memory
-pages associated with the region.
-
-Returns @code{NULL} on success.
-
-Returns the malloc descriptor on failure, which can subsequently
-be used for further action (such as obtaining more information about
-the nature of the failure).
-
-@item void *mmalloc (void *@var{md}, size_t @var{size});
-Given an @code{mmalloc} descriptor @var{md}, allocate additional memory of
-@var{size} bytes in the associated mapped region.
-
-@item *mrealloc (void *@var{md}, void *@var{ptr}, size_t @var{size});
-Given an @code{mmalloc} descriptor @var{md} and a pointer to memory
-previously allocated by @code{mmalloc} in @var{ptr}, reallocate the
-memory to be @var{size} bytes long, possibly moving the existing
-contents of memory if necessary.
-
-@item void *mvalloc (void *@var{md}, size_t @var{size});
-Like @code{mmalloc} but the resulting memory is aligned on a page boundary.
-
-@item void mfree (void *@var{md}, void *@var{ptr});
-Given an @code{mmalloc} descriptor @var{md} and a pointer to memory previously
-allocated by @code{mmalloc} in @var{ptr}, free the previously allocated memory.
-
-@item int mmalloc_errno (void *@var{md});
-Given a @code{mmalloc} descriptor, if the last @code{mmalloc} operation
-failed for some reason due to a system call failure, then
-returns the associated @code{errno}. Returns 0 otherwise.
-(This function is not yet implemented).
-@end table
-
-@bye
+++ /dev/null
-/* Support for an sbrk-like function that uses mmap. */
-
-/* Copyright (c) 2010-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1992, 2000 Free Software Foundation, Inc.
-
- Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com */
-
-#include <stdio.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/wait.h>
-#include <errno.h>
-
-#include "mmprivate.h"
-
-#ifndef MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-
-#define PAGE_ALIGN(addr) (void*)(((unsigned long)(addr) + mmalloc_pagesize - 1) & ~(mmalloc_pagesize - 1))
-
-/** @brief Add memory to this heap
- *
- * Get core for the memory region specified by MDP, using SIZE as the
- * amount to either add to or subtract from the existing region. Works
- * like sbrk(), but using mmap().
- *
- * It never returns NULL. Instead, it dies verbosely on errors.
- *
- * @param mdp The heap
- * @param size Bytes to allocate for this heap (or <0 to free memory from this heap)
- */
-void *mmorecore(struct mdesc *mdp, ssize_t size)
-{
- void* result; // please keep it uninitialized to track issues
- size_t mapbytes; /* Number of bytes to map */
- void* moveto; /* Address where we wish to move "break value" to */
- void* mapto; /* Address we actually mapped to */
-
- if (size == 0) {
- /* Just return the current "break" value. */
- return mdp->breakval;
- }
-
- static unsigned long mmalloc_pagesize = 0;
- if (!mmalloc_pagesize)
- mmalloc_pagesize = (unsigned long)sysconf(_SC_PAGESIZE);
-
- if (size < 0) {
- /* We are deallocating memory. If the amount requested would cause us to try to deallocate back past the base of
- * the mmap'd region then die verbosely. Otherwise, deallocate the memory and return the old break value. */
- if (((char*)mdp->breakval) + size >= (char*)mdp->base) {
- result = mdp->breakval;
- mdp->breakval = (char*)mdp->breakval + size;
- moveto = PAGE_ALIGN(mdp->breakval);
- munmap(moveto, (size_t)(((char*)mdp->top) - ((char*)moveto)) - 1);
- mdp->top = moveto;
- } else {
- fprintf(stderr,"Internal error: mmap was asked to deallocate more memory than it previously allocated. Bailling out now!\n");
- abort();
- }
- } else if ((char*)mdp->breakval + size > (char*)mdp->top) {
- /* The request would move us past the end of the currently mapped memory, so map in enough more memory to satisfy
- the request. This means we also have to grow the mapped-to file by an appropriate amount, since mmap cannot
- be used to extend a file. */
- moveto = PAGE_ALIGN((char*)mdp->breakval + size);
- mapbytes = (char*)moveto - (char*)mdp->top;
-
- /* Let's call mmap. Note that it is possible that mdp->top is 0. In this case mmap will choose the address for us.
- This call might very well overwrite an already existing memory mapping (leading to weird bugs).
- */
- mapto = mmap(mdp->top, mapbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
-
- if (mapto == MAP_FAILED) {
- char buff[1024];
- fprintf(stderr, "Internal error: mmap returned MAP_FAILED! pagesize:%lu error: %s\n", mmalloc_pagesize,
- strerror(errno));
- snprintf(buff, 1024, "cat /proc/%d/maps", getpid());
- int status = system(buff);
- if (status == -1 || !(WIFEXITED(status) && WEXITSTATUS(status) == 0))
- fprintf(stderr, "Something went wrong when trying to %s\n", buff);
- sleep(1);
- abort();
- }
-
- if (mdp->top == 0)
- mdp->base = mdp->breakval = mapto;
-
- mdp->top = PAGE_ALIGN((char*)mdp->breakval + size);
- result = mdp->breakval;
- mdp->breakval = (char*)mdp->breakval + size;
- } else {
- /* Memory is already mapped, we only need to increase the breakval: */
- result = mdp->breakval;
- mdp->breakval = (char*)mdp->breakval + size;
- }
- return result;
-}
+++ /dev/null
-/* Declarations for `mmalloc' and friends. */
-
-/* Copyright (c) 2010-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1990, 1991, 1992 Free Software Foundation
-
- Written May 1989 by Mike Haertel.
- Heavily modified Mar 1992 by Fred Fish. (fnf@cygnus.com) */
-
-#ifndef XBT_MMPRIVATE_H
-#define XBT_MMPRIVATE_H 1
-
-#include "src/internal_config.h"
-#include "src/xbt/mmalloc/mmalloc.h"
-#include "swag.h"
-
-#include <limits.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-// This macro is veery similar to xbt_assert, but with no dependency on XBT
-#define mmalloc_assert(cond, ...) \
- do { \
- if (!(cond)) { \
- fprintf(stderr, __VA_ARGS__); \
- abort(); \
- } \
- } while (0)
-
-XBT_PRIVATE xbt_mheap_t mmalloc_preinit(void);
-
-#define MMALLOC_MAGIC "mmalloc" /* Mapped file magic number */
-#define MMALLOC_MAGIC_SIZE 8 /* Size of magic number buf */
-#define MMALLOC_VERSION 2 /* Current mmalloc version */
-
-/* The allocator divides the heap into blocks of fixed size; large
- requests receive one or more whole blocks, and small requests
- receive a fragment of a block. Fragment sizes are powers of two,
- and all fragments of a block are the same size. When all the
- fragments in a block have been freed, the block itself is freed.
-
- FIXME: we are not targeting 16bits machines anymore; update values */
-
-#define INT_BIT (CHAR_BIT * sizeof(int))
-#define BLOCKLOG (INT_BIT > 16 ? 12 : 9)
-#define BLOCKSIZE ((unsigned int) 1 << BLOCKLOG)
-#define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)
-
-/* We keep fragment-specific meta-data for introspection purposes, and these
- * information are kept in fixed length arrays. Here is the computation of
- * that size.
- *
- * Never make SMALLEST_POSSIBLE_MALLOC too small because we need to enlist
- * the free fragments.
- *
- * FIXME: what's the correct size, actually? The used one is a guess.
- */
-
-#define SMALLEST_POSSIBLE_MALLOC (32 * sizeof(void*))
-#define MAX_FRAGMENT_PER_BLOCK (BLOCKSIZE / SMALLEST_POSSIBLE_MALLOC)
-
-/* The difference between two pointers is a signed int. On machines where
- the data addresses have the high bit set, we need to ensure that the
- difference becomes an unsigned int when we are using the address as an
- integral value. In addition, when using with the '%' operator, the
- sign of the result is machine dependent for negative values, so force
- it to be treated as an unsigned int. */
-
-#define ADDR2UINT(addr) ((uintptr_t) (addr))
-#define RESIDUAL(addr,bsize) ((uintptr_t) (ADDR2UINT (addr) % (bsize)))
-
-/* Determine the amount of memory spanned by the initial heap table
- (not an absolute limit). */
-
-#define HEAP (INT_BIT > 16 ? 4194304 : 65536)
-
-/* Number of contiguous free blocks allowed to build up at the end of
- memory before they will be returned to the system.
- FIXME: this is not used anymore: we never return memory to the system. */
-#define FINAL_FREE_BLOCKS 8
-
-/* Where to start searching the free list when looking for new memory.
- The two possible values are 0 and heapindex. Starting at 0 seems
- to reduce total memory usage, while starting at heapindex seems to
- run faster. */
-
-#define MALLOC_SEARCH_START mdp -> heapindex
-
-/* Address to block number and vice versa. */
-
-#define BLOCK(A) ((size_t)(((char*)(A) - (char*)mdp->heapbase) / BLOCKSIZE + 1))
-
-#define ADDRESS(B) ((void*) (((ADDR2UINT(B)) - 1) * BLOCKSIZE + (char*) mdp -> heapbase))
-
-SG_BEGIN_DECL
-
-/* Statistics available to the user. */
-struct mstats
-{
- size_t bytes_total; /* Total size of the heap. */
- size_t chunks_used; /* Chunks allocated by the user. */
- size_t bytes_used; /* Byte total of user-allocated chunks. */
- size_t chunks_free; /* Chunks in the free list. */
- size_t bytes_free; /* Byte total of chunks in the free list. */
-};
-
-#define MMALLOC_TYPE_HEAPINFO (-2)
-#define MMALLOC_TYPE_FREE (-1)
-#define MMALLOC_TYPE_UNFRAGMENTED 0
-/* >0 values are fragmented blocks */
-
-/* Data structure giving per-block information.
- *
- * There is one such structure in the mdp->heapinfo array per block used in that heap,
- * the array index is the block number.
- *
- * There is several types of blocks in memory:
- * - full busy blocks: used when we are asked to malloc a block which size is > BLOCKSIZE/2
- * In this situation, the full block is given to the malloc.
- *
- * - fragmented busy blocks: when asked for smaller amount of memory.
- * Fragment sizes are only power of 2. When looking for such a free fragment,
- * we get one from mdp->fraghead (that contains a linked list of blocks fragmented at that
- * size and containing a free fragment), or we get a fresh block that we fragment.
- *
- * - free blocks are grouped by clusters, that are chained together.
- * When looking for free blocks, we traverse the mdp->heapinfo looking
- * for a cluster of free blocks that would be large enough.
- *
- * The size of the cluster is only to be trusted in the first block of the cluster, not in the middle blocks.
- *
- * The type field is consistently updated for every blocks, even within clusters of blocks.
- * You can crawl the array and rely on that value.
- *
- */
-typedef struct {
- s_xbt_swag_hookup_t freehook; /* to register this block as having empty frags when needed */
- int type; /* 0: busy large block
- >0: busy fragmented (fragments of size 2^type bytes)
- <0: free block */
-
- union {
- /* Heap information for a busy block. */
- struct {
- size_t nfree; /* Free fragments in a fragmented block. */
- ssize_t frag_size[MAX_FRAGMENT_PER_BLOCK];
- int ignore[MAX_FRAGMENT_PER_BLOCK];
- } busy_frag;
- struct {
- size_t size; /* Size (in blocks) of a large cluster. */
- size_t busy_size; /* Actually used space, in bytes */
- int ignore;
- } busy_block;
- /* Heap information for a free block (that may be the first of a free cluster). */
- struct {
- size_t size; /* Size (in blocks) of a free cluster. */
- size_t next; /* Index of next free cluster. */
- size_t prev; /* Index of previous free cluster. */
- } free_block;
- };
-} malloc_info;
-
-/** @brief Descriptor of a mmalloc area
- *
- * Internal structure that defines the format of the malloc-descriptor.
- * This gets written to the base address of the region that mmalloc is
- * managing, and thus also becomes the file header for the mapped file,
- * if such a file exists.
- * */
-struct mdesc {
- /** @brief Chained lists of mdescs */
- struct mdesc *next_mdesc;
-
- /** @brief The "magic number" for an mmalloc file. */
- char magic[MMALLOC_MAGIC_SIZE];
-
- /** @brief The size in bytes of this structure
- *
- * Used as a sanity check when reusing a previously created mapped file.
- * */
- unsigned int headersize;
-
- /** @brief Version number of the mmalloc package that created this file. */
- unsigned char version;
-
- unsigned int options;
-
- /** @brief Some flag bits to keep track of various internal things. */
- unsigned int flags;
-
- /** @brief Number of info entries. */
- size_t heapsize;
-
- /** @brief Pointer to first block of the heap (base of the first block). */
- void *heapbase;
-
- /** @brief Current search index for the heap table.
- *
- * Search index in the info table.
- */
- size_t heapindex;
-
- /** @brief Limit of valid info table indices. */
- size_t heaplimit;
-
- /** @brief Block information table.
- *
- * Table indexed by block number giving per-block information.
- */
- malloc_info *heapinfo;
-
- /* @brief List of all blocks containing free fragments of a given size.
- *
- * The array index is the log2 of requested size.
- * Actually only the sizes 8->11 seem to be used, but who cares? */
- s_xbt_swag_t fraghead[BLOCKLOG];
-
- /* @brief Base address of the memory region for this malloc heap
- *
- * This is the location where the bookkeeping data for mmap and
- * for malloc begins.
- */
- void *base;
-
- /** @brief End of memory in use
- *
- * Some memory might be already mapped by the OS but not used
- * by the heap.
- * */
- void *breakval;
-
- /** @brief End of the current memory region for this malloc heap.
- *
- * This is the first location past the end of mapped memory.
- *
- * Compared to breakval, this value is rounded to the next memory page.
- */
- void *top;
-
- /* @brief Instrumentation */
- struct mstats heapstats;
-};
-
-/* Bits to look at in the malloc descriptor flags word */
-
-#define MMALLOC_DEVZERO (1 << 0) /* Have mapped to /dev/zero */
-#define MMALLOC_INITIALIZED (1 << 1) /* Initialized mmalloc */
-
-/* A default malloc descriptor for the single sbrk() managed region. */
-
-XBT_PUBLIC_DATA struct mdesc* __mmalloc_default_mdp;
-
-XBT_PUBLIC void* mmorecore(struct mdesc* mdp, ssize_t size);
-
-XBT_PRIVATE size_t mmalloc_get_bytes_used_remote(size_t heaplimit, const malloc_info* heapinfo);
-
-/* We call dlsym during mmalloc initialization, but dlsym uses malloc.
- * So during mmalloc initialization, any call to malloc is diverted to a private static buffer.
- */
-extern uint64_t* mmalloc_preinit_buffer;
-#ifdef __FreeBSD__ /* FreeBSD require more memory, other might */
-#define mmalloc_preinit_buffer_size 256
-#else /* Valid on: Linux */
-#define mmalloc_preinit_buffer_size 32
-#endif
-
-SG_END_DECL
-
-#endif
+++ /dev/null
-/* Change the size of a block allocated by `mmalloc'. */
-
-/* Copyright (c) 2010-2023. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Copyright 1990, 1991 Free Software Foundation
- Written May 1989 by Mike Haertel. */
-
-#include <string.h> /* Prototypes for memcpy, memmove, memset, etc */
-#include <stdlib.h> /* abort */
-
-#include "mmprivate.h"
-
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
-/* Resize the given region to the new size, returning a pointer to the (possibly moved) region. This is optimized for
- * speed; some benchmarks seem to indicate that greater compactness is achieved by unconditionally allocating and
- * copying to a new region. This module has incestuous knowledge of the internals of both mfree and mmalloc. */
-
-void *mrealloc(xbt_mheap_t mdp, void *ptr, size_t size)
-{
- void *result;
- size_t blocks;
-
- /* Only keep real realloc, and reroute hidden malloc and free to the relevant functions */
- if (size == 0) {
- mfree(mdp, ptr);
- return mmalloc(mdp, 0);
- } else if (ptr == NULL) {
- return mmalloc(mdp, size);
- }
-
- if ((char *) ptr < (char *) mdp->heapbase || BLOCK(ptr) > mdp->heapsize) {
- // Don't trust xbt_assert and friends in malloc-level library, you fool!
- fprintf(stderr, "This pointer is not mine, refusing to realloc it (maybe you wanted to malloc it instead?)\n");
- abort();
- }
-
- size_t requested_size = size; // The amount of memory requested by user, for real
-
- /* Work even if the user was stupid enough to ask a ridiculously small block (even 0-length),
- * ie return a valid block that can be realloced and freed.
- * glibc malloc does not use this trick but return a constant pointer, but we need to enlist the free fragments later on.
- */
- if (size < SMALLEST_POSSIBLE_MALLOC)
- size = SMALLEST_POSSIBLE_MALLOC;
-
- size_t block = BLOCK(ptr);
-
- int type = mdp->heapinfo[block].type;
-
- switch (type) {
- case MMALLOC_TYPE_HEAPINFO:
- fprintf(stderr, "Asked realloc a fragment coming from a heapinfo block. I'm confused.\n");
- abort();
- break;
-
- case MMALLOC_TYPE_FREE:
- fprintf(stderr, "Asked realloc a fragment coming from a *free* block. I'm puzzled.\n");
- abort();
- break;
-
- case MMALLOC_TYPE_UNFRAGMENTED:
- /* Maybe reallocate a large block to a small fragment. */
-
- if (size <= BLOCKSIZE / 2) { // Full block -> Fragment; no need to optimize for time
-
- result = mmalloc(mdp, size);
- memcpy(result, ptr, requested_size);
- mfree(mdp, ptr);
- return result;
- }
-
- /* Full blocks -> Full blocks; see if we can hold it in place. */
- blocks = BLOCKIFY(size);
- if (blocks < mdp->heapinfo[block].busy_block.size) {
- /* The new size is smaller; return excess memory to the free list. */
- for (size_t it = block + blocks; it < mdp->heapinfo[block].busy_block.size; it++) {
- mdp->heapinfo[it].type = MMALLOC_TYPE_UNFRAGMENTED; // FIXME that should be useless, type should already be 0 here
- mdp->heapinfo[it].busy_block.ignore = 0;
- mdp->heapinfo[it].busy_block.size = 0;
- mdp->heapinfo[it].busy_block.busy_size = 0;
- }
-
- mdp->heapinfo[block + blocks].busy_block.size = mdp->heapinfo[block].busy_block.size - blocks;
- mfree(mdp, ADDRESS(block + blocks));
-
- mdp->heapinfo[block].busy_block.size = blocks;
- mdp->heapinfo[block].busy_block.busy_size = requested_size;
- mdp->heapinfo[block].busy_block.ignore = 0;
-
- result = ptr;
- } else if (blocks == mdp->heapinfo[block].busy_block.size) {
-
- /* No block size change necessary; only update the requested size */
- result = ptr;
- mdp->heapinfo[block].busy_block.busy_size = requested_size;
- mdp->heapinfo[block].busy_block.ignore = 0;
-
- } else {
- /* Won't fit, so allocate a new region that will.
- Free the old region first in case there is sufficient adjacent free space to grow without moving.
- This trick mandates using a specific version of mmalloc that does not memset the memory to 0 after
- action for obvious reasons. */
- blocks = mdp->heapinfo[block].busy_block.size;
- /* Prevent free from actually returning memory to the system. */
- size_t oldlimit = mdp->heaplimit;
- mdp->heaplimit = 0;
- mfree(mdp, ptr);
- mdp->heaplimit = oldlimit;
-
- result = mmalloc_no_memset(mdp, requested_size);
-
- if (ptr != result)
- memmove(result, ptr, blocks * BLOCKSIZE);
- /* FIXME: we should memset the end of the recently area */
- }
- break;
-
- default: /* Fragment -> ??; type=logarithm to base two of the fragment size. */
-
- if (type <= 0) {
- fprintf(stderr, "Unknown mmalloc block type.\n");
- abort();
- }
-
- if (size > ((size_t)1 << (type - 1)) && size <= ((size_t)1 << type)) {
- /* The new size is the same kind of fragment. */
-
- result = ptr;
- uintptr_t frag_nb = RESIDUAL(result, BLOCKSIZE) >> type;
- mdp->heapinfo[block].busy_frag.frag_size[frag_nb] = requested_size;
- mdp->heapinfo[block].busy_frag.ignore[frag_nb] = 0;
-
- } else { /* fragment -> Either other fragment, or block */
- /* The new size is different; allocate a new space,
- and copy the lesser of the new size and the old. */
-
- result = mmalloc(mdp, requested_size);
-
- memcpy(result, ptr, MIN(requested_size, (size_t) 1 << type));
- mfree(mdp, ptr);
- }
- break;
- }
- return result;
-}
+++ /dev/null
-/* Copyright (c) 2004-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Warning, this module is done to be efficient and performs tons of
- cast and dirty things. So avoid using it unless you really know
- what you are doing. */
-
-/* This type should be added to a type that is to be used in such a swag */
-
-#include "swag.h"
-#include "mmprivate.h" // mmalloc_assert
-
-typedef s_xbt_swag_hookup_t *xbt_swag_hookup_t;
-typedef struct xbt_swag* xbt_swag_t;
-typedef const struct xbt_swag* const_xbt_swag_t;
-
-#define xbt_swag_getPrev(obj, offset) (((xbt_swag_hookup_t)(((char*)(obj)) + (offset)))->prev)
-#define xbt_swag_getNext(obj, offset) (((xbt_swag_hookup_t)(((char*)(obj)) + (offset)))->next)
-#define xbt_swag_belongs(obj, swag) (xbt_swag_getNext((obj), (swag)->offset) || (swag)->tail == (obj))
-
-static inline void *xbt_swag_getFirst(const_xbt_swag_t swag)
-{
- return swag->head;
-}
-
-/*
- * @brief Offset computation
- * @arg var a variable of type <tt>struct</tt> something
- * @arg field a field of <tt>struct</tt> something
- * @return the offset of @a field in <tt>struct</tt> something.
- * @hideinitializer
- *
- * It is very similar to offsetof except that is done at runtime and that you have to declare a variable. Why defining
- * such a macro then ? Because it is portable...
- */
-#define xbt_swag_offset(var, field) ((char*)&((var).field) - (char*)&(var))
-/* @} */
-
-/* Creates a new swag.
- * @param swag the swag to initialize
- * @param offset where the hookup is located in the structure
- * @see xbt_swag_offset
- *
- * Usage : xbt_swag_init(swag,&obj.setA-&obj);
- */
-static inline void xbt_swag_init(xbt_swag_t swag, size_t offset)
-{
- swag->tail = NULL;
- swag->head = NULL;
- swag->offset = offset;
- swag->count = 0;
-}
-
-/*
- * @param obj the object to insert in the swag
- * @param swag a swag
- *
- * insert (at the tail... you probably had a very good reason to do that, I hope you know what you're doing) @a obj in
- * @a swag
- */
-static inline void xbt_swag_insert(void *obj, xbt_swag_t swag)
-{
-
- mmalloc_assert(!xbt_swag_belongs(obj, swag) || swag->tail,
- "This object belongs to an empty swag! Did you correctly initialize the object's hookup?");
-
- if (!swag->head) {
- mmalloc_assert(!(swag->tail), "Inconsistent swag.");
- swag->head = obj;
- swag->tail = obj;
- swag->count++;
- } else if (obj != swag->tail && !xbt_swag_getNext(obj, swag->offset)) {
- xbt_swag_getPrev(obj, swag->offset) = swag->tail;
- xbt_swag_getNext(swag->tail, swag->offset) = obj;
- swag->tail = obj;
- swag->count++;
- }
-}
-
-/*
- * @param obj the object to remove from the swag
- * @param swag a swag
- * @return @a obj if it was in the @a swag and NULL otherwise
- *
- * removes @a obj from @a swag
- */
-static inline void *xbt_swag_remove(void *obj, xbt_swag_t swag)
-{
- if (!obj)
- return NULL;
-
- size_t offset = swag->offset;
- void* prev = xbt_swag_getPrev(obj, offset);
- void* next = xbt_swag_getNext(obj, offset);
-
- if (prev) {
- xbt_swag_getNext(prev, offset) = next;
- xbt_swag_getPrev(obj, offset) = NULL;
- if (next) {
- xbt_swag_getPrev(next, offset) = prev;
- xbt_swag_getNext(obj, offset) = NULL;
- } else {
- swag->tail = prev;
- }
- swag->count--;
- } else if (next) {
- xbt_swag_getPrev(next, offset) = NULL;
- xbt_swag_getNext(obj, offset) = NULL;
- swag->head = next;
- swag->count--;
- } else if (obj == swag->head) {
- swag->head = swag->tail = NULL;
- swag->count--;
- }
-
- return obj;
-}
-
-/*
- * @param swag a swag
- * @return the number of objects in @a swag
- */
-static inline int xbt_swag_size(const_xbt_swag_t swag)
-{
- return swag->count;
-}
+++ /dev/null
-/* Copyright (c) 2004-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Warning, this module is done to be efficient and performs tons of cast and dirty things. So avoid using it unless
- * you really know what you are doing. */
-
-#ifndef XBT_SWAG_H
-#define XBT_SWAG_H
-
-/*
- * XBT_swag: a O(1) set based on linked lists
- *
- * Warning, this module is done to be efficient and performs tons of cast and dirty things. So make sure you know what
- * you are doing while using it.
- * It is basically a fifo but with restrictions so that it can be used as a set. Any operation (add, remove, belongs)
- * is O(1) and no call to malloc/free is done.
- *
- * @deprecated If you are using C++, you might want to use `boost::intrusive::set` instead.
- */
-
-/* Swag types
- *
- * Specific set.
- *
- * These typedefs are public so that the compiler can do his job but believe me, you don't want to try to play with
- * those structs directly. Use them as an abstract datatype.
- */
-
-typedef struct xbt_swag_hookup {
- void *next;
- void *prev;
-} s_xbt_swag_hookup_t;
-
-/* This type should be added to a type that is to be used in a swag.
- *
- * Whenever a new object with this struct is created, all fields have to be set to NULL
- *
- * Here is an example like that :
-
-\code
-typedef struct foo {
- s_xbt_swag_hookup_t set1_hookup;
- s_xbt_swag_hookup_t set2_hookup;
-
- double value;
-} s_foo_t, *foo_t;
-...
-{
- s_foo_t elem;
- xbt_swag_t set1=NULL;
- xbt_swag_t set2=NULL;
-
- set1 = xbt_swag_new(xbt_swag_offset(elem, set1_hookup));
- set2 = xbt_swag_new(xbt_swag_offset(elem, set2_hookup));
-
-}
-\endcode
-*/
-
-struct xbt_swag {
- void *head;
- void *tail;
- size_t offset;
- int count;
-};
-typedef struct xbt_swag s_xbt_swag_t;
-
-#endif /* XBT_SWAG_H */
-if (NOT SIMGRID_HAVE_STATEFUL_MC)
- set(dwarf_disable 1)
- set(dwarf-expression_disable 1)
-endif()
-
-foreach(x dwarf dwarf-expression random-bug mutex-handling)
+foreach(x random-bug mutex-handling)
if(NOT DEFINED ${x}_sources)
set(${x}_sources ${x}/${x}.cpp)
ADD_TESH(tesh-mc-without-mutex-handling-dpor --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/mutex-handling --setenv srcdir=${CMAKE_HOME_DIRECTORY} --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/mutex-handling without-mutex-handling.tesh --cfg=model-check/reduction:dpor)
ADD_TESH(mc-random-bug --setenv platfdir=${CMAKE_HOME_DIRECTORY}/examples/platforms --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/random-bug --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/random-bug random-bug.tesh)
ENDIF()
-IF(SIMGRID_HAVE_STATEFUL_MC)
- ADD_TESH(tesh-mc-dwarf --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/dwarf --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/dwarf dwarf.tesh)
- ADD_TESH(tesh-mc-dwarf-expression --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/mc/dwarf-expression --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/mc/dwarf-expression dwarf-expression.tesh)
-ENDIF()
if(enable_coverage)
ADD_TEST(cover-mc-mutex-handling ${CMAKE_CURRENT_BINARY_DIR}/mutex-handling/mutex-handling ${CMAKE_HOME_DIRECTORY}/examples/platforms/small_platform.xml)
set_target_properties(mcmini-${x} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/mcmini)
target_link_libraries(mcmini-${x} PRIVATE Threads::Threads)
add_dependencies(tests-mc mcmini-${x})
- if(SIMGRID_HAVE_STATEFUL_MC) # Only needed to introspect the binary
- target_link_libraries(mcmini-${x} PUBLIC "-Wl,-znorelro -Wl,-znoseparate-code") # TODO: convert to target_link_option once CMAKE_VERSION is >3.13
- endif()
ADD_TESH(mc-mini-${x} --setenv libdir=${CMAKE_BINARY_DIR}/lib --setenv bindir=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/mcmini/${x}.tesh)
endif()
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifdef NDEBUG
-#undef NDEBUG
-#endif
-
-#include "src/mc/mc_private.hpp"
-
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/inspect/Type.hpp"
-#include "src/mc/inspect/Variable.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include <array>
-#include <cassert>
-#include <cstdlib>
-#include <limits>
-#include <xbt/random.hpp>
-
-static uintptr_t rnd_engine()
-{
- return simgrid::xbt::random::uniform_int(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
-}
-
-static uintptr_t eval_binary_operation(simgrid::dwarf::ExpressionContext const& state, uint8_t op, uintptr_t a,
- uintptr_t b)
-{
- std::array<Dwarf_Op, 15> ops;
- ops[0].atom = DW_OP_const8u;
- ops[0].number = a;
- ops[1].atom = DW_OP_const8u;
- ops[1].number = b;
- ops[2].atom = op;
-
- simgrid::dwarf::ExpressionStack stack;
- try {
- simgrid::dwarf::execute(ops.data(), 3, state, stack);
- } catch (const simgrid::dwarf::evaluation_error&) {
- fprintf(stderr,"Expression evaluation error");
- }
-
- assert(stack.size() == 1);
- return stack.top();
-}
-
-static void basic_test(simgrid::dwarf::ExpressionContext const& state)
-{
- std::array<Dwarf_Op, 60> ops;
-
- uintptr_t a = rnd_engine();
- uintptr_t b = rnd_engine();
-
- simgrid::dwarf::ExpressionStack stack;
-
- bool caught_ex = false;
- try {
- ops[0].atom = DW_OP_drop;
- simgrid::dwarf::execute(ops.data(), 1, state, stack);
- } catch (const simgrid::dwarf::evaluation_error&) {
- caught_ex = true;
- }
- if (not caught_ex)
- fprintf(stderr, "Exception expected");
-
- try {
- ops[0].atom = DW_OP_lit21;
- simgrid::dwarf::execute(ops.data(), 1, state, stack);
- assert(stack.size() == 1);
- assert(stack.top() == 21);
-
- ops[0].atom = DW_OP_const8u;
- ops[0].number = a;
- simgrid::dwarf::execute(ops.data(), 1, state, stack);
- assert(stack.size() == 2);
- assert(stack.top() == a);
-
- ops[0].atom = DW_OP_drop;
- ops[1].atom = DW_OP_drop;
- simgrid::dwarf::execute(ops.data(), 2, state, stack);
- assert(stack.empty());
-
- stack.clear();
- ops[0].atom = DW_OP_lit21;
- ops[1].atom = DW_OP_plus_uconst;
- ops[1].number = a;
- simgrid::dwarf::execute(ops.data(), 2, state, stack);
- assert(stack.size() == 1);
- assert(stack.top() == a + 21);
-
- stack.clear();
- ops[0].atom = DW_OP_const8u;
- ops[0].number = a;
- ops[1].atom = DW_OP_dup;
- ops[2].atom = DW_OP_plus;
- simgrid::dwarf::execute(ops.data(), 3, state, stack);
- assert(stack.size() == 1);
- assert(stack.top() == a + a);
-
- stack.clear();
- ops[0].atom = DW_OP_const8u;
- ops[0].number = a;
- ops[1].atom = DW_OP_const8u;
- ops[1].number = b;
- ops[2].atom = DW_OP_over;
- simgrid::dwarf::execute(ops.data(), 3, state, stack);
- assert(stack.size() == 3);
- assert(stack.top() == a);
- assert(stack.top(1) == b);
- assert(stack.top(2) == a);
-
- stack.clear();
- ops[0].atom = DW_OP_const8u;
- ops[0].number = a;
- ops[1].atom = DW_OP_const8u;
- ops[1].number = b;
- ops[2].atom = DW_OP_swap;
- simgrid::dwarf::execute(ops.data(), 3, state, stack);
- assert(stack.size() == 2);
- assert(stack.top() == a);
- assert(stack.top(1) == b);
- } catch (const simgrid::dwarf::evaluation_error&) {
- fprintf(stderr,"Expression evaluation error");
- }
-}
-
-static void test_deref(simgrid::dwarf::ExpressionContext const& state)
-{
- try {
- uintptr_t foo = 42;
-
- std::array<Dwarf_Op, 60> ops;
- ops[0].atom = DW_OP_const8u;
- ops[0].number = (uintptr_t)&foo;
- ops[1].atom = DW_OP_deref;
-
- simgrid::dwarf::ExpressionStack stack;
-
- simgrid::dwarf::execute(ops.data(), 2, state, stack);
- assert(stack.size() == 1);
- assert(stack.top() == foo);
- } catch (const simgrid::dwarf::evaluation_error&) {
- fprintf(stderr,"Expression evaluation error");
- }
-}
-
-int main()
-{
- auto* process = new simgrid::mc::RemoteProcessMemory(getpid(), nullptr);
-
- simgrid::dwarf::ExpressionContext state;
- state.address_space = (simgrid::mc::AddressSpace*) process;
-
- basic_test(state);
-
- for(int i=0; i!=100; ++i) {
- uintptr_t a = rnd_engine();
- uintptr_t b = rnd_engine();
- assert(eval_binary_operation(state, DW_OP_plus, a, b) == (a + b));
- }
-
- for(int i=0; i!=100; ++i) {
- uintptr_t a = rnd_engine();
- uintptr_t b = rnd_engine();
- assert(eval_binary_operation(state, DW_OP_or, a, b) == (a | b));
- }
-
- for(int i=0; i!=100; ++i) {
- uintptr_t a = rnd_engine();
- uintptr_t b = rnd_engine();
- assert(eval_binary_operation(state, DW_OP_and, a, b) == (a & b));
- }
-
- for(int i=0; i!=100; ++i) {
- uintptr_t a = rnd_engine();
- uintptr_t b = rnd_engine();
- assert(eval_binary_operation(state, DW_OP_xor, a, b) == (a ^ b));
- }
-
- test_deref(state);
-
- return 0;
-}
+++ /dev/null
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/dwarf-expression
+++ /dev/null
-/* Copyright (c) 2014-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#ifdef NDEBUG
-#undef NDEBUG
-#endif
-
-#include <simgrid/s4u/Engine.hpp>
-
-#include "src/mc/datatypes.h"
-#include "src/mc/mc.h"
-#include "src/mc/mc_private.hpp"
-
-#include "src/mc/inspect/ObjectInformation.hpp"
-#include "src/mc/inspect/Type.hpp"
-#include "src/mc/inspect/Variable.hpp"
-#include "src/mc/sosp/RemoteProcessMemory.hpp"
-
-#include <cassert>
-#include <cstring>
-
-#include <elfutils/version.h>
-#if _ELFUTILS_VERSION < 171
-// Elder elfutils/libdw broken with multi-dimensional arrays. See https://sourceware.org/bugzilla/show_bug.cgi?id=22546
-int test_some_array[4 * 5 * 6];
-#else
-int test_some_array[4][5][6];
-#endif
-
-struct some_struct {
- int first;
- int second[4][5];
-};
-some_struct test_some_struct;
-
-static simgrid::mc::Frame* find_function_by_name(
- simgrid::mc::ObjectInformation* info, const char* name)
-{
- for (auto& [_, entry] : info->subprograms)
- if (entry.name == name)
- return &entry;
- return nullptr;
-}
-
-static simgrid::mc::Variable* find_local_variable(
- simgrid::mc::Frame* frame, const char* argument_name)
-{
- for (simgrid::mc::Variable& variable : frame->variables)
- if(argument_name == variable.name)
- return &variable;
-
- for (simgrid::mc::Frame& scope : frame->scopes) {
- simgrid::mc::Variable* variable = find_local_variable(
- &scope, argument_name);
- if(variable)
- return variable;
- }
-
- return nullptr;
-}
-
-static void test_local_variable(simgrid::mc::ObjectInformation* info, const char* function, const char* variable,
- const void* address, unw_cursor_t* cursor)
-{
- simgrid::mc::Frame* subprogram = find_function_by_name(info, function);
- assert(subprogram);
- // TODO, Lookup frame by IP and test against name instead
-
- const simgrid::mc::Variable* var = find_local_variable(subprogram, variable);
- assert(var);
-
- void* frame_base = subprogram->frame_base(*cursor);
- simgrid::dwarf::Location location = simgrid::dwarf::resolve(var->location_list, info, cursor, frame_base, nullptr);
-
- xbt_assert(location.in_memory(), "Expected the variable %s of function %s to be in memory", variable, function);
- xbt_assert(location.address() == address, "Bad resolution of local variable %s of %s", variable, function);
-}
-
-static const simgrid::mc::Variable* test_global_variable(const simgrid::mc::RemoteProcessMemory& process,
- simgrid::mc::ObjectInformation* info, const char* name,
- void* address, long byte_size)
-{
- const simgrid::mc::Variable* variable = info->find_variable(name);
- xbt_assert(variable, "Global variable %s was not found", name);
- xbt_assert(variable->name == name, "Name mismatch for %s", name);
- xbt_assert(variable->global, "Variable %s is not global", name);
- xbt_assert(variable->address == address, "Address mismatch for %s : %p expected but %p found", name, address,
- variable->address);
-
- auto i = process.binary_info->types.find(variable->type_id);
- xbt_assert(i != process.binary_info->types.end(), "Missing type for %s", name);
- const simgrid::mc::Type* type = &i->second;
- xbt_assert(type->byte_size == byte_size, "Byte size mismatch for %s", name);
- return variable;
-}
-
-static const simgrid::mc::Member* find_member(const simgrid::mc::Type& type, const char* name)
-{
- for (const simgrid::mc::Member& member : type.members)
- if(member.name == name)
- return &member;
- return nullptr;
-}
-
-int some_local_variable = 0;
-
-struct s_foo {
- int i;
-};
-
-static void test_type_by_name(const simgrid::mc::RemoteProcessMemory& process, s_foo /*my_foo*/)
-{
- assert(process.binary_info->full_types_by_name.find("struct s_foo") != process.binary_info->full_types_by_name.end());
-}
-
-int main(int argc, char** argv)
-{
- simgrid::s4u::Engine::get_instance(&argc, argv);
-
- const simgrid::mc::Variable* var;
- const simgrid::mc::Type* type;
-
- simgrid::mc::RemoteProcessMemory process(getpid(), nullptr);
-
- test_global_variable(process, process.binary_info.get(), "some_local_variable", &some_local_variable, sizeof(int));
-
- var = test_global_variable(process, process.binary_info.get(), "test_some_array", &test_some_array,
- sizeof(test_some_array));
- auto i = process.binary_info->types.find(var->type_id);
- xbt_assert(i != process.binary_info->types.end(), "Missing type");
- type = &i->second;
- xbt_assert(type->element_count == 6 * 5 * 4, "element_count mismatch in test_some_array : %i / %i",
- type->element_count, 6 * 5 * 4);
-
- var = test_global_variable(process, process.binary_info.get(), "test_some_struct", &test_some_struct,
- sizeof(test_some_struct));
- i = process.binary_info->types.find(var->type_id);
- xbt_assert(i != process.binary_info->types.end(), "Missing type");
- type = &i->second;
-
- assert(type);
- assert(find_member(*type, "first")->offset() == 0);
- assert(find_member(*type, "second")->offset() ==
- ((const char*)&test_some_struct.second) - (const char*)&test_some_struct);
-
- unw_context_t context;
- unw_cursor_t cursor;
- unw_getcontext(&context);
- unw_init_local(&cursor, &context);
-
- test_local_variable(process.binary_info.get(), "main", "argc", &argc, &cursor);
-
- int lexical_block_variable = 50;
- test_local_variable(process.binary_info.get(), "main", "lexical_block_variable", &lexical_block_variable, &cursor);
-
- s_foo my_foo = {0};
- test_type_by_name(process, my_foo);
-
- return 0;
-}
+++ /dev/null
-#!/usr/bin/env tesh
-
-$ ${bindir:=.}/dwarf
! ignore .*LD_PRELOAD.*
! expect return 3
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-barber_shop_deadlock 5 3 0 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-barber_shop_deadlock 5 3 0 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_global/INFO] **************************
> [0.000000] [mc_global/INFO] *** DEADLOCK DETECTED ***
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-barber_shop_ok 3 2 0 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-barber_shop_ok 3 2 0 0
>
> SleepBarber.c
>
! ignore .*LD_PRELOAD.*
! expect return 3
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_mutex_deadlock 5 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_mutex_deadlock 5 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_global/INFO] **************************
> [0.000000] [mc_global/INFO] *** DEADLOCK DETECTED ***
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_mutex_ok 5 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_mutex_ok 5 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_dfs/INFO] DFS exploration ended. 14170 unique states visited; 2087 backtracks (45202 transition replays, 61459 states visited overall)
\ No newline at end of file
! ignore .*LD_PRELOAD.*
! expect return 3
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_semaphores_deadlock 3 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_semaphores_deadlock 3 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_global/INFO] **************************
> [0.000000] [mc_global/INFO] *** DEADLOCK DETECTED ***
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_semaphores_ok 3 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-philosophers_semaphores_ok 3 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_dfs/INFO] DFS exploration ended. 2268 unique states visited; 233 backtracks (3314 transition replays, 5815 states visited overall)
\ No newline at end of file
! ignore .*LD_PRELOAD.*
! expect return 3
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-producer_consumer_deadlock 5 3 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-producer_consumer_deadlock 5 3 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_global/INFO] **************************
> [0.000000] [mc_global/INFO] *** DEADLOCK DETECTED ***
# We ignore the LD_PRELOAD lines from the expected output because they contain the build path
! ignore .*LD_PRELOAD.*
-$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-producer_consumer_ok 2 2 2 1 0
+$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsthread.so ${bindir:=.}/mcmini/mcmini-producer_consumer_ok 2 2 2 1 0
> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor.
> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1953 unique states visited; 491 backtracks (12324 transition replays, 14768 states visited overall)
\ No newline at end of file
> [ 0.000000] (0:maestro@) Actor 1 in simcall Random([0;5] ~> 4)
> [ 0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1/3;1/4'
> [ 0.000000] (0:maestro@) DFS exploration ended. 27 unique states visited; 22 backtracks (19 transition replays, 68 states visited overall)
-> [ 0.000000] (0:maestro@) Stack trace not displayed because you passed --log=no_loc
$ $VALGRIND_NO_TRACE_CHILDREN ${bindir:=.}/../../../bin/simgrid-mc ${bindir:=.}/random-bug printf ${platfdir}/small_platform.xml "--log=root.fmt:[%10.6r]%e(%i:%a@%h)%e%m%n" --log=xbt_cfg.thresh:warning
> [ 0.000000] (0:maestro@) Behavior: printf
> [ 0.000000] (0:maestro@) Actor 1 in simcall Random([0;5] ~> 4)
> [ 0.000000] (0:maestro@) You can debug the problem (and see the whole details) by rerunning out of simgrid-mc with --cfg=model-check/replay:'1/3;1/4'
> [ 0.000000] (0:maestro@) DFS exploration ended. 27 unique states visited; 22 backtracks (19 transition replays, 68 states visited overall)
-> [ 0.000000] (0:maestro@) Stack trace not displayed because you passed --log=no_loc
set(teshsuite_src ${teshsuite_src} ${CMAKE_CURRENT_SOURCE_DIR}/${x}/${x}.cpp)
endforeach()
-if(HAVE_MMALLOC)
- add_executable (mmalloc_test EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/mmalloc/mmalloc_test.cpp)
- target_link_libraries(mmalloc_test simgrid sgmalloc)
- set_target_properties(mmalloc_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/mmalloc)
- set_property(TARGET mmalloc_test APPEND PROPERTY INCLUDE_DIRECTORIES "${INTERNAL_INCLUDES}")
- add_dependencies(tests mmalloc_test)
-endif()
-
-set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/log_usage/log_usage_ndebug.tesh
- ${CMAKE_CURRENT_SOURCE_DIR}/mmalloc/mmalloc_64.tesh
- ${CMAKE_CURRENT_SOURCE_DIR}/mmalloc/mmalloc_32.tesh PARENT_SCOPE)
-set(teshsuite_src ${teshsuite_src} ${CMAKE_CURRENT_SOURCE_DIR}/mmalloc/mmalloc_test.cpp PARENT_SCOPE)
+set(tesh_files ${tesh_files} ${CMAKE_CURRENT_SOURCE_DIR}/log_usage/log_usage_ndebug.tesh PARENT_SCOPE)
+set(teshsuite_src ${teshsuite_src} PARENT_SCOPE)
foreach(x cmdline log_large parallel_log_crashtest parmap_test)
ADD_TESH(tesh-xbt-${x} --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/xbt/${x} --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/xbt/${x} ${x}.tesh)
else()
ADD_TESH(tesh-xbt-log --cd ${CMAKE_BINARY_DIR}/teshsuite/xbt/log_usage ${CMAKE_HOME_DIRECTORY}/teshsuite/xbt/log_usage/log_usage_ndebug.tesh)
endif()
-
-if(HAVE_MMALLOC)
- if(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 bits
- ADD_TESH(tesh-xbt-mmalloc-32 --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/xbt/mmalloc --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/xbt/mmalloc mmalloc_32.tesh)
- else()
- ADD_TESH(tesh-xbt-mmalloc-64 --setenv bindir=${CMAKE_BINARY_DIR}/teshsuite/xbt/mmalloc --cd ${CMAKE_HOME_DIRECTORY}/teshsuite/xbt/mmalloc mmalloc_64.tesh)
- endif()
-endif()
+++ /dev/null
-$ ${bindir:=.}/mmalloc_test --log=root.fmt:%m%n
-> Allocating a new heap
-> HeapA allocated
-> 100 bytes allocated with offset 45000
-> 200 bytes allocated with offset 46000
-> 300 bytes allocated with offset 47000
-> 400 bytes allocated with offset 47200
-> 500 bytes allocated with offset 47400
-> 600 bytes allocated with offset 48000
-> 700 bytes allocated with offset 48400
-> 800 bytes allocated with offset 48800
-> 900 bytes allocated with offset 48c00
-> 1000 bytes allocated with offset 49000
-> 1100 bytes allocated with offset 4a000
-> 1200 bytes allocated with offset 4a800
-> 1300 bytes allocated with offset 4b000
-> 1400 bytes allocated with offset 4b800
-> 1500 bytes allocated with offset 4c000
-> 1600 bytes allocated with offset 4c800
-> 1700 bytes allocated with offset 4d000
-> 1800 bytes allocated with offset 4d800
-> 1900 bytes allocated with offset 4e000
-> 2000 bytes allocated with offset 4e800
-> 2100 bytes allocated with offset 4f000
-> 2200 bytes allocated with offset 50000
-> 2300 bytes allocated with offset 51000
-> 2400 bytes allocated with offset 52000
-> 2500 bytes allocated with offset 53000
-> 2600 bytes allocated with offset 54000
-> 2700 bytes allocated with offset 55000
-> 2800 bytes allocated with offset 56000
-> 2900 bytes allocated with offset 57000
-> 3000 bytes allocated with offset 58000
-> 3100 bytes allocated with offset 59000
-> 3200 bytes allocated with offset 5a000
-> 3300 bytes allocated with offset 5b000
-> 3400 bytes allocated with offset 5c000
-> 3500 bytes allocated with offset 5d000
-> 3600 bytes allocated with offset 5e000
-> 3700 bytes allocated with offset 5f000
-> 3800 bytes allocated with offset 60000
-> 3900 bytes allocated with offset 61000
-> 4000 bytes allocated with offset 62000
-> 4100 bytes allocated with offset 63000
-> 4200 bytes allocated with offset 65000
-> 4300 bytes allocated with offset 67000
-> 4400 bytes allocated with offset 69000
-> 4500 bytes allocated with offset 6b000
-> 4600 bytes allocated with offset 6d000
-> 4700 bytes allocated with offset 6f000
-> 4800 bytes allocated with offset 71000
-> 4900 bytes allocated with offset 73000
-> 5000 bytes allocated with offset 75000
-> 100 bytes allocated with offset 45080
-> 200 bytes allocated with offset 46100
-> 300 bytes allocated with offset 47600
-> 400 bytes allocated with offset 47800
-> 500 bytes allocated with offset 47a00
-> 600 bytes allocated with offset 49400
-> 700 bytes allocated with offset 49800
-> 800 bytes allocated with offset 49c00
-> 900 bytes allocated with offset 77000
-> 1000 bytes allocated with offset 77400
-> 1100 bytes allocated with offset 78000
-> 1200 bytes allocated with offset 78800
-> 1300 bytes allocated with offset 79000
-> 1400 bytes allocated with offset 79800
-> 1500 bytes allocated with offset 7a000
-> 1600 bytes allocated with offset 7a800
-> 1700 bytes allocated with offset 7b000
-> 1800 bytes allocated with offset 7b800
-> 1900 bytes allocated with offset 7c000
-> 2000 bytes allocated with offset 7c800
-> 2100 bytes allocated with offset 7d000
-> 2200 bytes allocated with offset 7e000
-> 2300 bytes allocated with offset 7f000
-> 2400 bytes allocated with offset 80000
-> 2500 bytes allocated with offset 81000
-> 2600 bytes allocated with offset 82000
-> 2700 bytes allocated with offset 83000
-> 2800 bytes allocated with offset 84000
-> 2900 bytes allocated with offset 85000
-> 3000 bytes allocated with offset 86000
-> 3100 bytes allocated with offset 87000
-> 3200 bytes allocated with offset 88000
-> 3300 bytes allocated with offset 89000
-> 3400 bytes allocated with offset 8a000
-> 3500 bytes allocated with offset 8b000
-> 3600 bytes allocated with offset 8c000
-> 3700 bytes allocated with offset 8d000
-> 3800 bytes allocated with offset 8e000
-> 3900 bytes allocated with offset 8f000
-> 4000 bytes allocated with offset 90000
-> 4100 bytes allocated with offset 91000
-> 4200 bytes allocated with offset 93000
-> 4300 bytes allocated with offset 95000
-> 4400 bytes allocated with offset 97000
-> 4500 bytes allocated with offset 99000
-> 4600 bytes allocated with offset 9b000
-> 4700 bytes allocated with offset 9d000
-> 4800 bytes allocated with offset 9f000
-> 4900 bytes allocated with offset a1000
-> 5000 bytes allocated with offset a3000
-> All blocks were correctly allocated. Free every second block
-> Memset every second block to zero (yeah, they are not currently allocated :)
-> Re-allocate every second block
-> free all blocks
-> Let's try different codepaths for mrealloc
-> Damnit, I cannot break mmalloc this time. That's SO disappointing.
+++ /dev/null
-$ ${bindir:=.}/mmalloc_test --log=root.fmt:%m%n
-> Allocating a new heap
-> HeapA allocated
-> 100 bytes allocated with offset 39000
-> 200 bytes allocated with offset 39100
-> 300 bytes allocated with offset 3a000
-> 400 bytes allocated with offset 3a200
-> 500 bytes allocated with offset 3a400
-> 600 bytes allocated with offset 3b000
-> 700 bytes allocated with offset 3b400
-> 800 bytes allocated with offset 3b800
-> 900 bytes allocated with offset 3bc00
-> 1000 bytes allocated with offset 3c000
-> 1100 bytes allocated with offset 3d000
-> 1200 bytes allocated with offset 3d800
-> 1300 bytes allocated with offset 3e000
-> 1400 bytes allocated with offset 3e800
-> 1500 bytes allocated with offset 3f000
-> 1600 bytes allocated with offset 3f800
-> 1700 bytes allocated with offset 40000
-> 1800 bytes allocated with offset 40800
-> 1900 bytes allocated with offset 41000
-> 2000 bytes allocated with offset 41800
-> 2100 bytes allocated with offset 42000
-> 2200 bytes allocated with offset 43000
-> 2300 bytes allocated with offset 44000
-> 2400 bytes allocated with offset 45000
-> 2500 bytes allocated with offset 46000
-> 2600 bytes allocated with offset 47000
-> 2700 bytes allocated with offset 48000
-> 2800 bytes allocated with offset 49000
-> 2900 bytes allocated with offset 4a000
-> 3000 bytes allocated with offset 4b000
-> 3100 bytes allocated with offset 4c000
-> 3200 bytes allocated with offset 4d000
-> 3300 bytes allocated with offset 4e000
-> 3400 bytes allocated with offset 4f000
-> 3500 bytes allocated with offset 50000
-> 3600 bytes allocated with offset 51000
-> 3700 bytes allocated with offset 52000
-> 3800 bytes allocated with offset 53000
-> 3900 bytes allocated with offset 54000
-> 4000 bytes allocated with offset 55000
-> 4100 bytes allocated with offset 56000
-> 4200 bytes allocated with offset 58000
-> 4300 bytes allocated with offset 5a000
-> 4400 bytes allocated with offset 5c000
-> 4500 bytes allocated with offset 5e000
-> 4600 bytes allocated with offset 60000
-> 4700 bytes allocated with offset 62000
-> 4800 bytes allocated with offset 64000
-> 4900 bytes allocated with offset 66000
-> 5000 bytes allocated with offset 68000
-> 100 bytes allocated with offset 39200
-> 200 bytes allocated with offset 39300
-> 300 bytes allocated with offset 3a600
-> 400 bytes allocated with offset 3a800
-> 500 bytes allocated with offset 3aa00
-> 600 bytes allocated with offset 3c400
-> 700 bytes allocated with offset 3c800
-> 800 bytes allocated with offset 3cc00
-> 900 bytes allocated with offset 6a000
-> 1000 bytes allocated with offset 6a400
-> 1100 bytes allocated with offset 6b000
-> 1200 bytes allocated with offset 6b800
-> 1300 bytes allocated with offset 6c000
-> 1400 bytes allocated with offset 6c800
-> 1500 bytes allocated with offset 6d000
-> 1600 bytes allocated with offset 6d800
-> 1700 bytes allocated with offset 6e000
-> 1800 bytes allocated with offset 6e800
-> 1900 bytes allocated with offset 6f000
-> 2000 bytes allocated with offset 6f800
-> 2100 bytes allocated with offset 70000
-> 2200 bytes allocated with offset 71000
-> 2300 bytes allocated with offset 72000
-> 2400 bytes allocated with offset 73000
-> 2500 bytes allocated with offset 74000
-> 2600 bytes allocated with offset 75000
-> 2700 bytes allocated with offset 76000
-> 2800 bytes allocated with offset 77000
-> 2900 bytes allocated with offset 78000
-> 3000 bytes allocated with offset 79000
-> 3100 bytes allocated with offset 7a000
-> 3200 bytes allocated with offset 7b000
-> 3300 bytes allocated with offset 7c000
-> 3400 bytes allocated with offset 7d000
-> 3500 bytes allocated with offset 7e000
-> 3600 bytes allocated with offset 7f000
-> 3700 bytes allocated with offset 80000
-> 3800 bytes allocated with offset 81000
-> 3900 bytes allocated with offset 82000
-> 4000 bytes allocated with offset 83000
-> 4100 bytes allocated with offset 84000
-> 4200 bytes allocated with offset 86000
-> 4300 bytes allocated with offset 88000
-> 4400 bytes allocated with offset 8a000
-> 4500 bytes allocated with offset 8c000
-> 4600 bytes allocated with offset 8e000
-> 4700 bytes allocated with offset 90000
-> 4800 bytes allocated with offset 92000
-> 4900 bytes allocated with offset 94000
-> 5000 bytes allocated with offset 96000
-> All blocks were correctly allocated. Free every second block
-> Memset every second block to zero (yeah, they are not currently allocated :)
-> Re-allocate every second block
-> free all blocks
-> Let's try different codepaths for mrealloc
-> Damnit, I cannot break mmalloc this time. That's SO disappointing.
+++ /dev/null
-/* Copyright (c) 2012-2023. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include "simgrid/Exception.hpp"
-#include "simgrid/engine.h"
-#include "src/xbt/mmalloc/mmalloc.h"
-#include "xbt.h"
-
-#include <array>
-#include <cassert>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <vector>
-
-XBT_LOG_NEW_DEFAULT_CATEGORY(test,"this test");
-
-constexpr int BUFFSIZE = 204800;
-constexpr int TESTSIZE = 100;
-
-#define size_of_block(i) ((((i) % 50) + 1) * 100)
-
-static void check_block(const unsigned char* p, unsigned char b, int n)
-{
- for (int i = 0; i < n; i++)
- xbt_assert(p[i] == b, "value mismatch: %p[%d] = %#hhx, expected %#hhx", p, i, p[i], b);
-}
-
-int main(int argc, char**argv)
-{
- xbt_mheap_t heapA = nullptr;
- std::array<void*, TESTSIZE> pointers;
- simgrid_init(&argc, argv);
-
- XBT_INFO("Allocating a new heap");
- unsigned long mask = ~((unsigned long)xbt_pagesize - 1);
- auto* addr = reinterpret_cast<void*>(((unsigned long)sbrk(0) + BUFFSIZE) & mask);
- heapA = xbt_mheap_new(addr, 0);
- if (heapA == nullptr) {
- perror("attach 1 failed");
- fprintf(stderr, "bye\n");
- exit(1);
- }
-
- XBT_INFO("HeapA allocated");
-
- int i;
- int size;
- for (i = 0; i < TESTSIZE; i++) {
- size = size_of_block(i);
- pointers[i] = mmalloc(heapA, size);
- XBT_INFO("%d bytes allocated with offset %zx", size, (size_t)((char*)pointers[i] - (char*)heapA));
- }
- XBT_INFO("All blocks were correctly allocated. Free every second block");
- for (i = 0; i < TESTSIZE; i+=2) {
- mfree(heapA, pointers[i]);
- }
- XBT_INFO("Memset every second block to zero (yeah, they are not currently allocated :)");
- for (i = 0; i < TESTSIZE; i+=2) {
- size = size_of_block(i);
- memset(pointers[i],0, size);
- }
- XBT_INFO("Re-allocate every second block");
- for (i = 0; i < TESTSIZE; i+=2) {
- size = size_of_block(i);
- pointers[i] = mmalloc(heapA, size);
- }
-
- XBT_INFO("free all blocks");
- for (i = 0; i < TESTSIZE; i++)
- mfree(heapA, pointers[i]);
-
- XBT_INFO("Let's try different codepaths for mrealloc");
- for (i = 0; i < TESTSIZE; i++) {
- const std::vector<std::pair<int, unsigned char>> requests = {
- {size_of_block(i) / 2, 0x77}, {size_of_block(i) * 2, 0xaa}, {1, 0xc0}, {0, 0}};
- pointers[i] = nullptr;
- for (unsigned k = 0; k < requests.size(); ++k) {
- size = requests[k].first;
- pointers[i] = mrealloc(heapA, pointers[i], size);
- if (k > 0)
- check_block(static_cast<unsigned char*>(pointers[i]), requests[k - 1].second,
- std::min(size, requests[k - 1].first));
- if (size > 0)
- memset(pointers[i], requests[k].second, size);
- }
- }
-
- XBT_INFO("Damnit, I cannot break mmalloc this time. That's SO disappointing.");
- return 0;
-}
set(bin_files ${bin_files} ${CMAKE_CURRENT_SOURCE_DIR}/fix-paje-trace.sh
- ${CMAKE_CURRENT_SOURCE_DIR}/generate-dwarf-functions
${CMAKE_CURRENT_SOURCE_DIR}/normalize-pointers.py
${CMAKE_CURRENT_SOURCE_DIR}/sg_xml_unit_converter.py
${CMAKE_CURRENT_SOURCE_DIR}/simgrid_update_xml.pl
SET(BUILDNAME "FULL_FLAGS" CACHE INTERNAL "Buildname" FORCE)
endif()
-if(SIMGRID_HAVE_STATEFUL_MC)
- SET(BUILDNAME "MODEL-CHECKING" CACHE INTERNAL "Buildname" FORCE)
-endif()
-
if(enable_memcheck)
SET(BUILDNAME "MEMCHECK" CACHE INTERNAL "Buildname" FORCE)
endif()
src/xbt/log_private.hpp
src/xbt/mallocator_private.h
src/xbt/parmap.hpp
-
- src/xbt/mmalloc/mmalloc.h
- src/xbt/mmalloc/mfree.c
- src/xbt/mmalloc/mm_legacy.c
- src/xbt/mmalloc/mm_module.c
- src/xbt/mmalloc/mmalloc.c
- src/xbt/mmalloc/mmalloc.info
- src/xbt/mmalloc/mmalloc.texi
- src/xbt/mmalloc/mmorecore.c
- src/xbt/mmalloc/mmprivate.h
- src/xbt/mmalloc/mrealloc.c
- src/xbt/mmalloc/swag.c
- src/xbt/mmalloc/swag.h
)
set(SMPI_SRC
src/xbt/utils/iter/LazyPowerset.hpp
)
-if(HAVE_MMALLOC)
- set(SGMALLOC_SRC src/xbt/mmalloc/mm.c)
- set(XBT_SRC ${XBT_SRC} src/xbt/mmalloc/mm.c)
-else()
- set(EXTRA_DIST ${EXTRA_DIST} src/xbt/mmalloc/mm.c)
-endif()
-
set(NS3_SRC
src/kernel/resource/models/network_ns3.cpp
src/kernel/resource/models/ns3/ns3_simulator.cpp
src/mc/explo/DFSExplorer.hpp
src/mc/explo/Exploration.cpp
src/mc/explo/Exploration.hpp
+ src/mc/explo/CommunicationDeterminismChecker.cpp
+
+ src/mc/explo/UdporChecker.cpp
+ src/mc/explo/UdporChecker.hpp
+
+ src/mc/explo/udpor/Comb.hpp
+ src/mc/explo/udpor/Configuration.hpp
+ src/mc/explo/udpor/Configuration.cpp
+ src/mc/explo/udpor/EventSet.cpp
+ src/mc/explo/udpor/EventSet.hpp
+ src/mc/explo/udpor/ExtensionSetCalculator.cpp
+ src/mc/explo/udpor/ExtensionSetCalculator.hpp
+ src/mc/explo/udpor/History.cpp
+ src/mc/explo/udpor/History.hpp
+ src/mc/explo/udpor/maximal_subsets_iterator.cpp
+ src/mc/explo/udpor/maximal_subsets_iterator.hpp
+ src/mc/explo/udpor/UnfoldingEvent.cpp
+ src/mc/explo/udpor/UnfoldingEvent.hpp
+ src/mc/explo/udpor/Unfolding.cpp
+ src/mc/explo/udpor/Unfolding.hpp
+ src/mc/explo/udpor/udpor_forward.hpp
+ src/mc/explo/udpor/udpor_tests_private.hpp
src/mc/explo/odpor/Execution.cpp
src/mc/explo/odpor/Execution.hpp
src/mc/transition/TransitionRandom.hpp
src/mc/transition/TransitionSynchro.cpp
src/mc/transition/TransitionSynchro.hpp
- )
-set(MC_SRC_STATEFUL
- src/mc/explo/CommunicationDeterminismChecker.cpp
- src/mc/explo/LivenessChecker.cpp
- src/mc/explo/LivenessChecker.hpp
- src/mc/explo/UdporChecker.cpp
- src/mc/explo/UdporChecker.hpp
-
- src/mc/explo/udpor/Comb.hpp
- src/mc/explo/udpor/Configuration.hpp
- src/mc/explo/udpor/Configuration.cpp
- src/mc/explo/udpor/EventSet.cpp
- src/mc/explo/udpor/EventSet.hpp
- src/mc/explo/udpor/ExtensionSetCalculator.cpp
- src/mc/explo/udpor/ExtensionSetCalculator.hpp
- src/mc/explo/udpor/History.cpp
- src/mc/explo/udpor/History.hpp
- src/mc/explo/udpor/maximal_subsets_iterator.cpp
- src/mc/explo/udpor/maximal_subsets_iterator.hpp
- src/mc/explo/udpor/UnfoldingEvent.cpp
- src/mc/explo/udpor/UnfoldingEvent.hpp
- src/mc/explo/udpor/Unfolding.cpp
- src/mc/explo/udpor/Unfolding.hpp
- src/mc/explo/udpor/udpor_forward.hpp
- src/mc/explo/udpor/udpor_tests_private.hpp
-
- src/mc/inspect/DwarfExpression.cpp
- src/mc/inspect/DwarfExpression.hpp
- src/mc/inspect/Frame.cpp
- src/mc/inspect/Frame.hpp
- src/mc/inspect/LocationList.cpp
- src/mc/inspect/LocationList.hpp
- src/mc/inspect/ObjectInformation.cpp
- src/mc/inspect/ObjectInformation.hpp
- src/mc/inspect/Type.hpp
- src/mc/inspect/Variable.hpp
- src/mc/inspect/mc_dwarf.cpp
- src/mc/inspect/mc_dwarf.hpp
- src/mc/inspect/mc_dwarf_attrnames.cpp
- src/mc/inspect/mc_dwarf_tagnames.cpp
- src/mc/inspect/mc_member.cpp
- src/mc/inspect/mc_unw.cpp
- src/mc/inspect/mc_unw.hpp
- src/mc/inspect/mc_unw_vmread.cpp
-
- src/mc/sosp/ChunkedData.cpp
- src/mc/sosp/ChunkedData.hpp
- src/mc/sosp/PageStore.cpp
- src/mc/sosp/PageStore.hpp
- src/mc/sosp/Region.cpp
- src/mc/sosp/Region.hpp
- src/mc/sosp/RemoteProcessMemory.cpp
- src/mc/sosp/RemoteProcessMemory.hpp
- src/mc/sosp/Snapshot.cpp
- src/mc/sosp/Snapshot.hpp
-
- src/mc/AddressSpace.hpp
- src/mc/VisitedState.cpp
- src/mc/VisitedState.hpp
- src/mc/compare.cpp
src/mc/mc_environ.h
src/mc/mc_exit.hpp
src/mc/mc_forward.hpp
src/mc/api/strategy/MinMatchComm.hpp
src/mc/api/strategy/Strategy.hpp
src/mc/api/strategy/UniformStrategy.hpp
-
- src/xbt/mmalloc/mm_interface.c
)
set(MC_SIMGRID_MC_SRC src/mc/explo/simgrid_mc.cpp)
if(SIMGRID_HAVE_MC)
set(simgrid_sources ${simgrid_sources} ${MC_SRC_STATELESS})
endif()
-if(SIMGRID_HAVE_STATEFUL_MC)
- set(simgrid_sources ${simgrid_sources} ${MC_SRC_STATEFUL})
-endif()
-set(EXTRA_DIST ${EXTRA_DIST} ${MC_SRC_STATELESS} ${MC_SRC_STATEFUL})
+set(EXTRA_DIST ${EXTRA_DIST} ${MC_SRC_STATELESS})
if(SIMGRID_HAVE_NS3)
set(headers_to_install ${headers_to_install} include/simgrid/plugins/ns3.hpp)
tools/cmake/MaintainerMode.cmake
tools/cmake/MakeLib.cmake
tools/cmake/Modules/FindGraphviz.cmake
- tools/cmake/Modules/FindLibdw.cmake
- tools/cmake/Modules/FindLibelf.cmake
tools/cmake/Modules/FindLibevent.cmake
- tools/cmake/Modules/FindLibunwind.cmake
tools/cmake/Modules/FindNS3.cmake
tools/cmake/Modules/FindPAPI.cmake
tools/cmake/Modules/FindValgrind.cmake
set(EXTRA_DIST ${EXTRA_DIST} ${STHREAD_SRC})
endif()
-if(HAVE_MMALLOC)
- add_library(sgmalloc SHARED ${SGMALLOC_SRC})
- set_property(TARGET sgmalloc
- APPEND PROPERTY INCLUDE_DIRECTORIES "${INTERNAL_INCLUDES}")
-endif()
-
if(SIMGRID_HAVE_MC)
add_executable(simgrid-mc ${MC_SIMGRID_MC_SRC})
target_link_libraries(simgrid-mc simgrid)
+++ /dev/null
-find_path(LIBDW_INCLUDE_DIR "elfutils/libdw.h"
- HINTS
- $ENV{SIMGRID_LIBDW_LIBRARY_PATH}
- $ENV{LD_LIBRARY_PATH}
- $ENV{LIBDW_LIBRARY_PATH}
- PATH_SUFFIXES include/
- PATHS
- /opt
- /opt/local
- /opt/csw
- /sw
- /usr)
-find_library(LIBDW_LIBRARY
- NAMES dw
- HINTS
- $ENV{SIMGRID_LIBDW_LIBRARY_PATH}
- $ENV{LD_LIBRARY_PATH}
- $ENV{LIBDW_LIBRARY_PATH}
- PATH_SUFFIXES lib/
- PATHS
- /opt
- /opt/local
- /opt/csw
- /sw
- /usr)
-set(LIBDW_LIBRARIES "${LIBDW_LIBRARY}")
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(
- Libdw
- DEFAULT_MSG
- LIBDW_LIBRARIES
- LIBDW_INCLUDE_DIR)
-mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARIES)
+++ /dev/null
-find_path(LIBELF_INCLUDE_DIR "libelf.h"
- HINTS
- $ENV{SIMGRID_LIBELF_LIBRARY_PATH}
- $ENV{LD_LIBRARY_PATH}
- $ENV{LIBELF_LIBRARY_PATH}
- PATH_SUFFIXES include/
- PATHS
- /opt
- /opt/local
- /opt/csw
- /sw
- /usr)
-find_library(LIBELF_LIBRARY
- NAMES elf
- HINTS
- $ENV{SIMGRID_LIBELF_LIBRARY_PATH}
- $ENV{LD_LIBRARY_PATH}
- $ENV{LIBELF_LIBRARY_PATH}
- PATH_SUFFIXES lib/
- PATHS
- /opt
- /opt/local
- /opt/csw
- /sw
- /usr)
-set(LIBELF_LIBRARIES "${LIBELF_LIBRARY}")
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(
- Libelf
- DEFAULT_MSG
- LIBELF_LIBRARIES
- LIBELF_INCLUDE_DIR)
-mark_as_advanced(LIBELF_INCLUDE_DIR LIBELF_LIBRARIES)
+++ /dev/null
-# Search for libunwind and components, both includes and libraries
-#
-# Copyright (C) 2003-2023 The SimGrid Team.
-# This is distributed under the LGPL licence but please contact us for
-# relicensing if you need. This is merely free software, no matter the licence.
-#
-#
-# Input environment variables:
-# LIBUNWIND_HINT: path to libunwind installation (e.g., /usr)
-# (only needed for non-standard installs)
-#
-# You can tune the needed components here.
-# TODO: we should take this as a parameter if I knew how to do so.
-
-# SimGrid needs unwind-ptrace on Linux and FreeBSD
-if("${CMAKE_SYSTEM}" MATCHES "Linux|FreeBSD")
- set(LIBUNWIND_COMPONENTS ${LIBUNWIND_COMPONENTS} unwind-ptrace unwind-generic)
-endif()
-
-#
-# Output variables:
-# HAVE_LIBUNWIND : if all components were found was found
-# LIBUNWIND_LIBRARIES: List of all libraries to load (-lunwind -lunwind-x86_64 and such)
-#
-# Other effects:
-# - Calls include_directories() on where libunwind.h lives
-# - Calls link_directories() on where the libs live
-
-# Of course also need the core lib
-set(LIBUNWIND_COMPONENTS ${LIBUNWIND_COMPONENTS} "unwind")
-
-message(STATUS "Looking for libunwind:")
-# Let's assume we have it, and invalidate if parts are missing
-SET(HAVE_LIBUNWIND 1)
-
-#
-# Search for the header file
-#
-
-find_path(PATH_LIBUNWIND_H "libunwind.h"
- HINTS
- $ENV{LIBUNWIND_HINT}
- $ENV{LD_LIBRARY_PATH}
- PATH_SUFFIXES include/
- PATHS /opt /opt/local /opt/csw /sw /usr)
-if(PATH_LIBUNWIND_H)
- string(REGEX REPLACE "/libunwind.h" "" PATH_LIBUNWIND_H "${PATH_LIBUNWIND_H}")
- message(" Found libunwind.h in ${PATH_LIBUNWIND_H}")
- include_directories(${PATH_LIBUNWIND_H})
-else()
- message(" NOT FOUND libunwind.h")
- SET(HAVE_LIBUNWIND 0)
-endif()
-mark_as_advanced(PATH_LIBUNWIND_H)
-
-#
-# Search for the library components
-#
-
-foreach(component ${LIBUNWIND_COMPONENTS})
- find_library(PATH_LIBUNWIND_LIB_${component}
- NAMES ${component}
- HINTS
- $ENV{LIBUNWIND_HINT}
- $ENV{LD_LIBRARY_PATH}
- PATH_SUFFIXES lib/ lib/system
- PATHS /opt /opt/local /opt/csw /sw /usr /usr/lib/)
- if(PATH_LIBUNWIND_LIB_${component})
- # message(" ${component} ${PATH_LIBUNWIND_LIB_${component}}")
- string(REGEX REPLACE "/lib${component}.*[.]${LIB_EXE}$" "" PATH_LIBUNWIND_LIB_${component} "${PATH_LIBUNWIND_LIB_${component}}")
- message(" Found lib${component}.${LIB_EXE} in ${PATH_LIBUNWIND_LIB_${component}}")
- link_directories(${PATH_LIBUNWIND_LIB_${component}})
-
- if(${component} STREQUAL "unwind" AND APPLE)
- # Apple forbids to link directly against its libunwind implementation
- # So let's comply to that stupid restriction and link against the System framework
- SET(LIBUNWIND_LIBRARIES "${LIBUNWIND_LIBRARIES} -lSystem")
- else()
- SET(LIBUNWIND_LIBRARIES "${LIBUNWIND_LIBRARIES} -l${component}")
- endif()
-
- else()
- message(" Looking for lib${component}.${LIB_EXE} - not found")
- SET(HAVE_LIBUNWIND 0)
- endif()
- mark_as_advanced(PATH_LIBUNWIND_LIB_${component})
-endforeach()
-unset(component)
-unset(LIBUNWIND_COMPONENTS)
-
-#
-# Conclude and cleanup
-#
-if(HAVE_LIBUNWIND)
- message(STATUS "Dependencies induced by libunwind: ${LIBUNWIND_LIBRARIES}")
-else()
- message(STATUS "Some libunwind components are missing")
- set(LIBUNWIND_LIBRARIES "")
-endif()
option(minimal-bindings "Whether to compile the Python bindings libraries with the minimal dependency set" off)
mark_as_advanced(minimal-bindings)
-option(enable_model-checking "Turn this on to experiment with our prototype of model-checker" off)
-option(enable-model-checking "Please set 'enable_model-checking' instead" off)
+option(enable_model-checking "Turn this on to experiment with our prototype of model-checker" on)
+option(enable-model-checking "Please set 'enable_model-checking' instead" on)
mark_as_advanced(enable-model-checking)
if(enable-model-checking)
SET(enable_model-checking ON CACHE BOOL "Whether to compile the model-checker" FORCE)
set(MC_UNIT_TESTS src/mc/explo/odpor/ClockVector_test.cpp
src/mc/explo/odpor/Execution_test.cpp
- src/mc/explo/odpor/WakeupTree_test.cpp)
-
-set(STATEFUL_MC_UNIT_TESTS src/mc/sosp/Snapshot_test.cpp
- src/mc/sosp/PageStore_test.cpp
- src/mc/explo/udpor/Unfolding_test.cpp
- src/mc/explo/udpor/UnfoldingEvent_test.cpp
- src/mc/explo/udpor/EventSet_test.cpp
- src/mc/explo/udpor/ExtensionSet_test.cpp
- src/mc/explo/udpor/History_test.cpp
- src/mc/explo/udpor/Configuration_test.cpp)
-
+ src/mc/explo/odpor/WakeupTree_test.cpp
+
+ src/mc/explo/udpor/Unfolding_test.cpp
+ src/mc/explo/udpor/UnfoldingEvent_test.cpp
+ src/mc/explo/udpor/EventSet_test.cpp
+ src/mc/explo/udpor/ExtensionSet_test.cpp
+ src/mc/explo/udpor/History_test.cpp
+ src/mc/explo/udpor/Configuration_test.cpp)
if (SIMGRID_HAVE_MC)
set(UNIT_TESTS ${UNIT_TESTS} ${MC_UNIT_TESTS})
else()
set(EXTRA_DIST ${EXTRA_DIST} ${MC_UNIT_TESTS})
endif()
-if (SIMGRID_HAVE_STATEFUL_MC)
- set(UNIT_TESTS ${UNIT_TESTS} ${STATEFUL_MC_UNIT_TESTS})
-else()
- set(EXTRA_DIST ${EXTRA_DIST} ${STATEFUL_MC_UNIT_TESTS})
-endif()
if (SIMGRID_HAVE_EIGEN3)
set(UNIT_TESTS ${UNIT_TESTS} src/kernel/lmm/bmf_test.cpp)
else()
python3-pip \
doxygen fig2dev \
chrpath \
- libdw-dev libevent-dev libunwind8-dev \
+ libevent-dev \
python3-sphinx python3-breathe python3-sphinx-rtd-theme && \
apt clean && apt autoclean
# - Get the tutorial files (with an empty makefile advising to run cmake before make, just in case)
# - Remove everything that was installed, and re-install what's needed by the SimGrid libraries before the Gran Final Cleanup
# - Keep g++ gcc gfortran as any MC user will use (some of) them
-RUN apt install -y g++ gcc git valgrind gfortran libboost-dev libeigen3-dev libboost-stacktrace-dev cmake dpkg-dev libunwind-dev libdw-dev libelf-dev libevent-dev python3-dev && \
+RUN apt install -y g++ gcc git valgrind gfortran libboost-dev libeigen3-dev libboost-stacktrace-dev cmake dpkg-dev libdw-dev libelf-dev libevent-dev python3-dev && \
mkdir /source/ && cd /source && git clone --depth=1 https://framagit.org/simgrid/simgrid.git simgrid.git && \
cd simgrid.git && \
cmake -DCMAKE_INSTALL_PREFIX=/usr/ -Denable_model-checking=ON -Denable_documentation=OFF -Denable_smpi=ON -Denable_compile_optimizations=ON . && \
git clone --depth=1 https://framagit.org/simgrid/tutorial-model-checking /source/tuto-mc.git && \
printf "ndet-receive-s4u:\n\t@echo \"Please run the following command before make:\";echo \" cmake .\"; exit 1" > /source/tuto-mc.git/Makefile &&\
mkdir debian/ && touch debian/control && dpkg-shlibdeps --ignore-missing-info lib/*.so -llib/ -O/tmp/deps && \
- apt remove -y dpkg-dev libunwind-dev libdw-dev libelf-dev libevent-dev && \
+ apt remove -y dpkg-dev libdw-dev libelf-dev libevent-dev && \
apt install -y `sed -e 's/shlibs:Depends=//' -e 's/([^)]*)//g' -e 's/,//g' /tmp/deps` && rm /tmp/deps && \
apt autoremove -y && apt autoclean && apt clean
+++ /dev/null
-#!/usr/bin/env sh
-# Generate files from a given dwarf.h
-# Usage: tools/generate-dwarf-functions /usr/include/dwarf.h
-
-HEADER="\
-/* Copyright (c) 2014-$(date +%Y). The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-/* Warning: autogenerated, do not edit! */
-
-#include \"src/mc/inspect/mc_dwarf.hpp\"
-
-#include <string>
-#include <unordered_map>"
-
-cat - > src/mc/inspect/mc_dwarf_tagnames.cpp <<EOF
-$HEADER
-
-namespace {
-const std::unordered_map<int, const char*> tagname_map = {
- {0x00, "DW_TAG_invalid"},
-$(sed -n 's/.*\(DW_TAG_[^ ]*\) = \(0x[0-9a-f]*\).*/ {\2, "\1"},/p' -- "$1")
-};
-}
-
-namespace simgrid::dwarf {
-
-/** @brief Get the name of a dwarf tag (DW_TAG_*) from its code
- *
- * @param tag tag code (see the DWARF specification)
- * @return name of the tag
- */
-XBT_PRIVATE
-const char* tagname(int tag)
-{
- auto name = tagname_map.find(tag);
- return name == tagname_map.end() ? "DW_TAG_unknown" : name->second;
-}
-
-} // namespace simgrid::dwarf
-EOF
-
-cat - > src/mc/inspect/mc_dwarf_attrnames.cpp << EOF
-$HEADER
-
-namespace {
-const std::unordered_map<int, const char*> attrname_map = {
-$(sed -n 's/.*\(DW_AT_[^ ]*\) = \(0x[0-9a-f]*\).*/ {\2, "\1"},/p' -- "$1")
-};
-}
-
-namespace simgrid::dwarf {
-
-/** @brief Get the name of an attribute (DW_AT_*) from its code
- *
- * @param attr attribute code (see the DWARF specification)
- * @return name of the attribute
- */
-XBT_PRIVATE
-const char* attrname(int attr)
-{
- auto name = attrname_map.find(attr);
- return name == attrname_map.end() ? "DW_AT_unknown" : name->second;
-}
-
-} // namespace simgrid::dwarf
-EOF
fun:agconcat
}
-# libunwind seems to be using msync poorly, thus triggering these
-# https://github.com/JuliaLang/julia/issues/4533
-{
- msync unwind
- Memcheck:Param
- msync(start)
- ...
- obj:*/libpthread*.so
- ...
-}
-
-{
- ignore unwind cruft
- Memcheck:Param
- rt_sigprocmask(set)
- ...
- obj:/usr/lib/x86_64-linux-gnu/libunwind.so.*
- ...
-}
-{
- ignore unwind cruft
- Memcheck:Param
- msync(start)
- ...
- obj:/usr/lib/x86_64-linux-gnu/libunwind.so.*
- ...
-}
-{
- ignore unwind cruft
- Memcheck:Param
- write(buf)
- ...
- fun:_ULx86_64_step
- obj:/usr/lib/x86_64-linux-gnu/libunwind.so.*
-}
-
-{
- ignore unwind invalid reads
- Memcheck:Addr8
- fun:_Ux86_64_setcontext
-}
-
# Ignore python cruft
{
ignore python cruft 1