Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/memory/build/test/gtest/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 18 kB image not shown  

Quelle  TestPHC.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "gtest/gtest.h"

#include "mozmemory.h"
#include "mozilla/Assertions.h"
#include "mozilla/mozalloc.h"
#include "mozilla/StaticPrefs_memory.h"
#include "PHC.h"

using namespace mozilla;

bool PHCInfoEq(phc::AddrInfo& aInfo, phc::AddrInfo::Kind aKind, void* aBaseAddr,
               size_t aUsableSize, bool aHasAllocStack, bool aHasFreeStack) {
  return aInfo.mKind == aKind && aInfo.mBaseAddr == aBaseAddr &&
         aInfo.mUsableSize == aUsableSize &&
         // Proper stack traces will have at least 3 elements.
         (aHasAllocStack ? (aInfo.mAllocStack->mLength > 2)
                         : (aInfo.mAllocStack.isNothing())) &&
         (aHasFreeStack ? (aInfo.mFreeStack->mLength > 2)
                        : (aInfo.mFreeStack.isNothing()));
}

bool JeInfoEq(jemalloc_ptr_info_t& aInfo, PtrInfoTag aTag, void* aAddr,
              size_t aSize, arena_id_t arenaId) {
  return aInfo.tag == aTag && aInfo.addr == aAddr && aInfo.size == aSize
#ifdef MOZ_DEBUG
         && aInfo.arenaId == arenaId
#endif
      ;
}

uint8_t* GetPHCAllocation(size_t aSize, size_t aAlignment = 1) {
  // A crude but effective way to get a PHC allocation.
  for (int i = 0; i < 2000000; i++) {
    void* p = (aAlignment == 1) ? moz_xmalloc(aSize)
                                : moz_xmemalign(aAlignment, aSize);
    if (mozilla::phc::IsPHCAllocation(p, nullptr)) {
      return (uint8_t*)p;
    }
    free(p);
  }
  return nullptr;
}

#if defined(XP_DARWIN) && defined(__aarch64__)
static const size_t kPageSize = 16384;
#else
static const size_t kPageSize = 4096;
#endif

TEST(PHC, TestPHCAllocations)
{
  mozilla::phc::SetPHCState(phc::PHCState::Enabled);

  // First, check that allocations of various sizes all get put at the end of
  // their page as expected. Also, check their sizes are as expected.

#define ASSERT_POS(n1, n2)                                      \
  p = (uint8_t*)moz_xrealloc(p, (n1));                          \
  ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)), \
            kPageSize - (n2));                                  \
  ASSERT_EQ(moz_malloc_usable_size(p), (n2));

  uint8_t* p = GetPHCAllocation(1);
  if (!p) {
    MOZ_CRASH("failed to get a PHC allocation");
  }

  // The smallest possible allocation is 16 bytes.
  ASSERT_POS(8U, 16U);
  for (unsigned i = 16; i <= kPageSize; i *= 2) {
    ASSERT_POS(i, i);
  }

  free(p);

#undef ASSERT_POS

  // Second, do similar checking with allocations of various alignments. Also
  // check that their sizes (which are different to allocations with normal
  // alignment) are the same as the sizes of equivalent non-PHC allocations.

#define ASSERT_ALIGN(a1, a2)                                    \
  p = (uint8_t*)GetPHCAllocation(8, (a1));                      \
  ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)), \
            kPageSize - (a2));                                  \
  ASSERT_EQ(moz_malloc_usable_size(p), (a2));                   \
  free(p);                                                      \
  p = (uint8_t*)moz_xmemalign((a1), 8);                         \
  ASSERT_EQ(moz_malloc_usable_size(p), (a2));                   \
  free(p);

  // The smallest possible allocation is 16 bytes.
  ASSERT_ALIGN(8U, 16U);
  for (unsigned i = 16; i <= kPageSize; i *= 2) {
#if defined(XP_DARWIN) && defined(__aarch64__)
    // On 16KB page systems, jemalloc may use the 4KB size class for 8KB-aligned
    // allocations (usable 4096), while PHC must place them at offset 8192 in
    // the page (usable 8192). The mismatch is unavoidable, so skip jemalloc
    // check.
    if (i == kPageSize / 2) {
      p = (uint8_t*)GetPHCAllocation(8, i);
      ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)),
                (size_t)(kPageSize - i));
      ASSERT_EQ(moz_malloc_usable_size(p), (size_t)i);
      free(p);
      continue;
    }
#endif
    ASSERT_ALIGN(i, i);
  }

#undef ASSERT_ALIGN
}

static void TestInUseAllocation(uint8_t* aPtr, size_t aSize) {
  phc::AddrInfo phcInfo;
  jemalloc_ptr_info_t jeInfo;

  // Test an in-use PHC allocation: first byte within it.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
                        true, false));
  ASSERT_EQ(moz_malloc_usable_size(aPtr), aSize);
  jemalloc_ptr_info(aPtr, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagLiveAlloc, aPtr, aSize, 0));

  // Test an in-use PHC allocation: last byte within it.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize - 1, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
                        true, false));
  ASSERT_EQ(moz_malloc_usable_size(aPtr + aSize - 1), aSize);
  jemalloc_ptr_info(aPtr + aSize - 1, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagLiveAlloc, aPtr, aSize, 0));

  // Test an in-use PHC allocation: last byte before it.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr - 1, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
                        true, false));
  ASSERT_EQ(moz_malloc_usable_size(aPtr - 1), 0ul);
  jemalloc_ptr_info(aPtr - 1, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test an in-use PHC allocation: first byte on its allocation page.
  ASSERT_TRUE(
      mozilla::phc::IsPHCAllocation(aPtr + aSize - kPageSize, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
                        true, false));
  jemalloc_ptr_info(aPtr + aSize - kPageSize, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test an in-use PHC allocation: first byte in the following guard page.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, false));
  jemalloc_ptr_info(aPtr + aSize, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test an in-use PHC allocation: last byte in the lower half of the
  // following guard page.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize + (kPageSize / 2 - 1),
                                            &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, false));
  jemalloc_ptr_info(aPtr + aSize + (kPageSize / 2 - 1), &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test an in-use PHC allocation: last byte in the preceding guard page.
  ASSERT_TRUE(
      mozilla::phc::IsPHCAllocation(aPtr + aSize - 1 - kPageSize, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, false));
  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test an in-use PHC allocation: first byte in the upper half of the
  // preceding guard page.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(
      aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1), &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, false));
  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1),
                    &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));
}

static void TestFreedAllocation(uint8_t* aPtr, size_t aSize) {
  phc::AddrInfo phcInfo;
  jemalloc_ptr_info_t jeInfo;

  // Test a freed PHC allocation: first byte within it.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagFreedAlloc, aPtr, aSize, 0));

  // Test a freed PHC allocation: last byte within it.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize - 1, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr + aSize - 1, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagFreedAlloc, aPtr, aSize, 0));

  // Test a freed PHC allocation: last byte before it.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr - 1, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr - 1, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test a freed PHC allocation: first byte on its allocation page.
  ASSERT_TRUE(
      mozilla::phc::IsPHCAllocation(aPtr + aSize - kPageSize, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr + aSize - kPageSize, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test a freed PHC allocation: first byte in the following guard page.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr + aSize, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test a freed PHC allocation: last byte in the lower half of the following
  // guard page.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize + (kPageSize / 2 - 1),
                                            &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr + aSize + (kPageSize / 2 - 1), &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test a freed PHC allocation: last byte in the preceding guard page.
  ASSERT_TRUE(
      mozilla::phc::IsPHCAllocation(aPtr + aSize - 1 - kPageSize, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize, &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));

  // Test a freed PHC allocation: first byte in the upper half of the preceding
  // guard page.
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(
      aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1), &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
                        true, true));
  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1),
                    &jeInfo);
  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 00));
}

TEST(PHC, TestPHCInfo)
{
  mozilla::phc::SetPHCState(phc::PHCState::Enabled);

  int stackVar = 0;
  phc::AddrInfo phcInfo;

  // Test a default AddrInfo.
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::Unknown, nullptr, 0ul,
                        falsefalse));

  // Test some non-PHC allocation addresses.
  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(nullptr, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::Unknown, nullptr, 0,
                        falsefalse));
  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(&stackVar, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::Unknown, nullptr, 0,
                        falsefalse));

  uint8_t* p = GetPHCAllocation(32);
  if (!p) {
    MOZ_CRASH("failed to get a PHC allocation");
  }

  TestInUseAllocation(p, 32);

  free(p);

  TestFreedAllocation(p, 32);

  // There are no tests for `mKind == NeverAllocatedPage` because it's not
  // possible to reliably get ahold of such a page.
}

TEST(PHC, TestPHCDisablingThread)
{
  mozilla::phc::SetPHCState(phc::PHCState::Enabled);

  uint8_t* p = GetPHCAllocation(32);
  uint8_t* q = GetPHCAllocation(32);
  if (!p || !q) {
    MOZ_CRASH("failed to get a PHC allocation");
  }

  ASSERT_TRUE(mozilla::phc::IsPHCEnabledOnCurrentThread());
  mozilla::phc::DisablePHCOnCurrentThread();
  ASSERT_FALSE(mozilla::phc::IsPHCEnabledOnCurrentThread());

  // Test realloc() on a PHC allocation while PHC is disabled on the thread.
  uint8_t* p2 = (uint8_t*)realloc(p, 128);
  // The small realloc is fulfilled within the same page, but it does move.
  ASSERT_TRUE(p2 == p - 96);
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(p2, nullptr));
  uint8_t* p3 = (uint8_t*)realloc(p2, 2 * kPageSize);
  // The big realloc is not in-place, and the result is not a PHC allocation.
  ASSERT_TRUE(p3 != p2);
  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(p3, nullptr));
  free(p3);

  // Test free() on a PHC allocation while PHC is disabled on the thread.
  free(q);

  // These must not be PHC allocations.
  uint8_t* r = GetPHCAllocation(32);  // This will fail.
  ASSERT_FALSE(!!r);

  mozilla::phc::ReenablePHCOnCurrentThread();
  ASSERT_TRUE(mozilla::phc::IsPHCEnabledOnCurrentThread());

  // If it really was reenabled we should be able to get PHC allocations
  // again.
  uint8_t* s = GetPHCAllocation(32);  // This should succeed.
  ASSERT_TRUE(!!s);
  free(s);
}

TEST(PHC, TestPHCDisablingGlobal)
{
  mozilla::phc::SetPHCState(phc::PHCState::Enabled);

  uint8_t* p1 = GetPHCAllocation(32);
  uint8_t* p2 = GetPHCAllocation(32);
  uint8_t* q = GetPHCAllocation(32);
  if (!p1 || !p2 || !q) {
    MOZ_CRASH("failed to get a PHC allocation");
  }

  mozilla::phc::SetPHCState(phc::PHCState::OnlyFree);

  // Test realloc() on a PHC allocation while PHC is disabled on the thread.
  uint8_t* p3 = (uint8_t*)realloc(p1, 128);
  // The small realloc is evicted from PHC because in "OnlyFree" state PHC
  // tries to reduce its memory impact.
  ASSERT_TRUE(p3 != p1);
  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(p3, nullptr));
  free(p3);
  uint8_t* p4 = (uint8_t*)realloc(p2, 2 * kPageSize);
  // The big realloc is not in-place, and the result is not a PHC
  // allocation, regardless of PHC's state.
  ASSERT_TRUE(p4 != p2);
  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(p4, nullptr));
  free(p4);

  // Test free() on a PHC allocation while PHC is disabled on the thread.
  free(q);

  // These must not be PHC allocations.
  uint8_t* r = GetPHCAllocation(32);  // This will fail.
  ASSERT_FALSE(!!r);

  mozilla::phc::SetPHCState(phc::PHCState::Enabled);

  // If it really was reenabled we should be able to get PHC allocations
  // again.
  uint8_t* s = GetPHCAllocation(32);  // This should succeed.
  ASSERT_TRUE(!!s);
  free(s);
}

size_t GetNumAvailable() {
  mozilla::phc::PHCStats stats;
  GetPHCStats(stats);
  return stats.mSlotsFreed + stats.mSlotsUnused;
}

#ifndef ANDROID
static void TestExhaustion(std::vector<uint8_t*>& aAllocs) {
  // Disable the reuse delay to make the test more reliable.  At the same
  // time lower the other probabilities to speed up this test, but much
  // lower and the test runs more slowly maybe because of how PHC
  // optimises for multithreading.
  mozilla::phc::SetPHCProbabilities(64640);

  for (auto& a : aAllocs) {
    a = GetPHCAllocation(128);
    ASSERT_TRUE(a);
    TestInUseAllocation(a, 128);
  }

  // No more PHC slots are available.
  ASSERT_EQ(0ul, GetNumAvailable());
  // We should now fail to get an allocation.
  ASSERT_FALSE(GetPHCAllocation(128));

  // Restore defaults.
  mozilla::phc::SetPHCProbabilities(
      StaticPrefs::memory_phc_avg_delay_first(),
      StaticPrefs::memory_phc_avg_delay_normal(),
      StaticPrefs::memory_phc_avg_delay_page_reuse());
}

static void ReleaseVector(std::vector<uint8_t*>& aAllocs) {
  for (auto& a : aAllocs) {
    free(a);
    TestFreedAllocation(a, 128);
  }
}

TEST(PHC, TestExhaustion)
{
  // PHC hardcodes the amount of allocations to track.
  size_t num_allocations = GetNumAvailable();
  mozilla::phc::DisablePHCOnCurrentThread();
  std::vector<uint8_t*> allocations(num_allocations);
  mozilla::phc::ReenablePHCOnCurrentThread();

  TestExhaustion(allocations);
  ReleaseVector(allocations);

  // And now that we've released those allocations the number of available
  // slots will be non-zero.
  ASSERT_TRUE(GetNumAvailable() != 0);
  // And we should be able to get new allocations again.
  uint8_t* r = GetPHCAllocation(128);
  ASSERT_TRUE(!!r);
  free(r);
}

static uint8_t* VectorMax(std::vector<uint8_t*>& aVec) {
  uint8_t* max = 0;
  for (auto a : aVec) {
    max = a > max ? a : max;
  }
  return max;
}

TEST(PHC, TestBounds)
{
  // PHC reserves a large area of virtual memory and depending on runtime
  // configuration allocates a smaller range of memory within it.  This test
  // tests that boundary.

  size_t num_allocations = GetNumAvailable();
  mozilla::phc::DisablePHCOnCurrentThread();
  std::vector<uint8_t*> allocations(num_allocations);
  mozilla::phc::ReenablePHCOnCurrentThread();

  TestExhaustion(allocations);

  uint8_t* max = VectorMax(allocations);
  ASSERT_TRUE(!!max);

  // Verify that the next page is a guard page.
  phc::AddrInfo phcInfo;
  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(max + 128 + 1, &phcInfo));
  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, max, 128, true,
                        false));

  // But the page after that is Nothing.
  ASSERT_TRUE(!mozilla::phc::IsPHCAllocation(max + kPageSize * 2, &phcInfo));

  ReleaseVector(allocations);
}
#endif

size_t GetNumSlotsTotal() {
  mozilla::phc::PHCStats stats;
  GetPHCStats(stats);
  return stats.mSlotsAllocated + stats.mSlotsFreed + stats.mSlotsUnused;
}

#ifndef ANDROID
TEST(PHC, TestPHCGrow)
{
  size_t batch_1_num = GetNumAvailable();
  mozilla::phc::DisablePHCOnCurrentThread();
  std::vector<uint8_t*> batch_1(batch_1_num);
  mozilla::phc::ReenablePHCOnCurrentThread();
  TestExhaustion(batch_1);

  size_t total_before = GetNumSlotsTotal();
  size_t requested = total_before + 1024;
  mozilla::phc::SetPHCSize(requested * kPageSize);

  ASSERT_TRUE(GetNumSlotsTotal() == requested);

  size_t batch_2_num = GetNumAvailable();
  ASSERT_TRUE(!!batch_2_num);
  ASSERT_TRUE(batch_2_num == 1024ul);
  mozilla::phc::DisablePHCOnCurrentThread();
  std::vector<uint8_t*> batch_2(batch_2_num);
  mozilla::phc::ReenablePHCOnCurrentThread();
  TestExhaustion(batch_2);

  ReleaseVector(batch_1);
  ReleaseVector(batch_2);
}
#endif

TEST(PHC, TestPHCLimit)
{
  // Test the virtual address limit compiled into PHC.

  size_t start = GetNumSlotsTotal();
  mozilla::phc::SetPHCSize(SIZE_MAX);

  ASSERT_TRUE(GetNumSlotsTotal() > start);

  // This needs to match the limit compiled into phc
  // (see kPhcVirtualReservation)
  size_t limit =
#ifdef HAVE_64BIT_BUILD
#  if defined(XP_DARWIN) && defined(__aarch64__)
      512 * 1024 * 1024;
#  else
      128 * 1024 * 1024;
#  endif
#else
      2 * 1024 * 1024;
#endif
  ASSERT_TRUE(GetNumSlotsTotal() == limit / kPageSize / 2 - 1);

  mozilla::phc::SetPHCSize(start);
}

Messung V0.5 in Prozent
C=81 H=94 G=87

¤ Dauer der Verarbeitung: 0.8 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.