Ticket #4242: rejointest_v2.patch

File rejointest_v2.patch, 15.2 KB (added by elexis, 8 years ago)

Rebased

  • binaries/system/readme.txt

    Advanced / diagnostic:  
    5656                    files created in sim_log within the game's log folder. NOTE: game will
    5757                    run much slower with this option!
    5858-serializationtest  checks simulation state each turn for serialization errors; on test
    5959                    failure, error is displayed and logs created in oos_log within the
    6060                    game's log folder. NOTE: game will run much slower with this option!
     61-rejointest=N       simulates a rejoin and checks simulation state each turn for serialization
     62                    errors; this is similar to a serialization test but much faster and
     63                    less complete. It should be enough for debugging most rejoin OOSes.
    6164
    6265Windows-specific:
    6366-wQpcTscSafe        allow timing via QueryPerformanceCounter despite the fact
    6467                    that it's using TSC and it may be unsafe. has no effect if
    6568                    a better timer (i.e. the HPET) is available.
  • source/main.cpp

    static void RunGameOrAtlas(int argc, con  
    469469        MountMods(paths, GetMods(args, INIT_MODS));
    470470
    471471        {
    472472            CReplayPlayer replay;
    473473            replay.Load(replayFile);
    474             replay.Replay(args.Has("serializationtest"), args.Has("ooslog"));
     474            replay.Replay(
     475                args.Has("serializationtest"),
     476                args.Has("rejointest") ? args.Get("rejointest").ToInt() : -1,
     477                args.Has("ooslog"));
    475478        }
    476479
    477480        g_VFS.reset();
    478481
    479482        CXeromyces::Terminate();
  • source/ps/GameSetup/Config.cpp

    static void ProcessCommandLineArgs(const  
    167167    if (args.Has("ooslog"))
    168168        g_ConfigDB.SetValueString(CFG_COMMAND, "ooslog", "true");
    169169
    170170    if (args.Has("serializationtest"))
    171171        g_ConfigDB.SetValueString(CFG_COMMAND, "serializationtest", "true");
     172
     173    if (args.Has("rejointest"))
     174        g_ConfigDB.SetValueString(CFG_COMMAND, "rejointest", args.Get("rejointest"));
    172175}
    173176
    174177
    175178void CONFIG_Init(const CmdLineArgs& args)
    176179{
  • source/ps/Replay.cpp

    void CReplayPlayer::Load(const std::stri  
    112112
    113113    m_Stream = new std::ifstream(path.c_str());
    114114    ENSURE(m_Stream->good());
    115115}
    116116
    117 void CReplayPlayer::Replay(bool serializationtest, bool ooslog)
     117void CReplayPlayer::Replay(bool serializationtest, int rejointestturn, bool ooslog)
    118118{
    119119    ENSURE(m_Stream);
    120120
    121121    new CProfileViewer;
    122122    new CProfileManager;
    void CReplayPlayer::Replay(bool serializ  
    128128    g_ScriptRuntime = ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime>(), runtimeSize, heapGrowthBytesGCTrigger);
    129129
    130130    g_Game = new CGame(true, false);
    131131    if (serializationtest)
    132132        g_Game->GetSimulation2()->EnableSerializationTest();
     133    if (rejointestturn > 0)
     134        g_Game->GetSimulation2()->EnableRejoinTest(rejointestturn);
    133135    if (ooslog)
    134136        g_Game->GetSimulation2()->EnableOOSLog();
    135137
    136138    // Need some stuff for terrain movement costs:
    137139    // (TODO: this ought to be independent of any graphics code)
  • source/ps/Replay.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
    class CReplayPlayer  
    9595public:
    9696    CReplayPlayer();
    9797    ~CReplayPlayer();
    9898
    9999    void Load(const std::string& path);
    100     void Replay(bool serializationtest, bool ooslog);
     100    void Replay(bool serializationtest, int rejointestturn, bool ooslog);
    101101
    102102private:
    103103    std::istream* m_Stream;
    104104};
    105105
  • source/simulation2/Simulation2.cpp

     
    4949class CSimulation2Impl
    5050{
    5151public:
    5252    CSimulation2Impl(CUnitManager* unitManager, shared_ptr<ScriptRuntime> rt, CTerrain* terrain) :
    5353        m_SimContext(), m_ComponentManager(m_SimContext, rt),
    54         m_EnableOOSLog(false), m_EnableSerializationTest(false),
     54        m_EnableOOSLog(false), m_EnableSerializationTest(false), m_RejoinTestTurn(-1), m_TestingRejoin(false),
     55        m_SecondaryTerrain(nullptr), m_SecondaryContext(nullptr), m_SecondaryComponentManager(nullptr), m_SecondaryLoadedScripts(nullptr),
    5556        m_MapSettings(rt->m_rt), m_InitAttributes(rt->m_rt)
    5657    {
    5758        m_SimContext.m_UnitManager = unitManager;
    5859        m_SimContext.m_Terrain = terrain;
    5960        m_ComponentManager.LoadComponentTypes();
    public:  
    6364        // Tests won't have config initialised
    6465        if (CConfigDB::IsInitialised())
    6566        {
    6667            CFG_GET_VAL("ooslog", m_EnableOOSLog);
    6768            CFG_GET_VAL("serializationtest", m_EnableSerializationTest);
     69            CFG_GET_VAL("rejointest", m_RejoinTestTurn);
     70            if (m_RejoinTestTurn <= 0) // Handle bogus values of the arg
     71                m_RejoinTestTurn = -1;
    6872        }
    6973
    7074        if (m_EnableOOSLog)
    7175        {
    7276            m_OOSLogPath = createDateIndexSubdirectory(psLogDir() / "oos_logs");
    public:  
    7478        }
    7579    }
    7680
    7781    ~CSimulation2Impl()
    7882    {
     83        delete m_SecondaryTerrain;
     84        delete m_SecondaryContext;
     85        delete m_SecondaryComponentManager;
     86        delete m_SecondaryLoadedScripts;
     87
    7988        UnregisterFileReloadFunc(ReloadChangedFileCB, this);
    8089    }
    8190
    8291    void ResetState(bool skipScriptedComponents, bool skipAI)
    8392    {
    public:  
    128137    OsPath m_OOSLogPath;
    129138
    130139    // Functions and data for the serialization test mode: (see Update() for relevant comments)
    131140
    132141    bool m_EnableSerializationTest;
     142    int m_RejoinTestTurn;
     143    bool m_TestingRejoin;
     144
     145    // Secondary simulation
     146    CTerrain* m_SecondaryTerrain;
     147    CSimContext* m_SecondaryContext;
     148    CComponentManager* m_SecondaryComponentManager;
     149    std::set<VfsPath>* m_SecondaryLoadedScripts;
    133150
    134151    struct SerializationTestState
    135152    {
    136153        std::stringstream state;
    137154        std::stringstream debug;
    void CSimulation2Impl::Update(int turnLe  
    330347     * We serialize that again and compare to the original serialization (to check that
    331348     * serialize->deserialize->serialize is equivalent to serialize).
    332349     * Then we run the update on the secondary context, and check that its new serialized
    333350     * state matches the primary context after the update (to check that the simulation doesn't depend
    334351     * on anything that's not serialized).
     352     *
     353     * In rejoin test mode, the secondary simulation is initialized from serialized data at turn N, then both
     354     * simulations run independantly while comparing their states each turn. This is way faster than a
     355     * complete serialization test and allows us to reproduce OOSes on rejoin.
    335356     */
    336357
    337358    const bool serializationTestDebugDump = false; // set true to save human-readable state dumps before an error is detected, for debugging (but slow)
    338359    const bool serializationTestHash = true; // set true to save and compare hash of state
    339360
    340361    SerializationTestState primaryStateBefore;
    341362    ScriptInterface& scriptInterface = m_ComponentManager.GetScriptInterface();
    342363
    343     if (m_EnableSerializationTest)
     364    if (m_RejoinTestTurn == m_TurnNumber)
     365        m_TestingRejoin = true;
     366
     367    if (m_EnableSerializationTest || m_TestingRejoin)
    344368    {
    345369        ENSURE(m_ComponentManager.SerializeState(primaryStateBefore.state));
    346370        if (serializationTestDebugDump)
    347371            ENSURE(m_ComponentManager.DumpDebugState(primaryStateBefore.debug, false));
    348372        if (serializationTestHash)
    349373            ENSURE(m_ComponentManager.ComputeStateHash(primaryStateBefore.hash, false));
    350374    }
    351375
    352376    UpdateComponents(m_SimContext, turnLengthFixed, commands);
    353377
    354 
    355     if (m_EnableSerializationTest)
     378    if (m_EnableSerializationTest || m_RejoinTestTurn == m_TurnNumber)
    356379    {
    357         // Initialise the secondary simulation
    358         CTerrain secondaryTerrain;
    359         CSimContext secondaryContext;
    360         secondaryContext.m_Terrain = &secondaryTerrain;
    361         CComponentManager secondaryComponentManager(secondaryContext, scriptInterface.GetRuntime());
    362         secondaryComponentManager.LoadComponentTypes();
    363         std::set<VfsPath> secondaryLoadedScripts;
    364         ENSURE(LoadDefaultScripts(secondaryComponentManager, &secondaryLoadedScripts));
    365         ResetComponentState(secondaryComponentManager, false, false);
     380        // Initialize the secondary simulation
     381        delete m_SecondaryTerrain;
     382        m_SecondaryTerrain = new CTerrain();
     383
     384        delete m_SecondaryContext;
     385        m_SecondaryContext = new CSimContext();
     386        m_SecondaryContext->m_Terrain = m_SecondaryTerrain;
     387
     388        delete m_SecondaryComponentManager;
     389        m_SecondaryComponentManager = new CComponentManager(*m_SecondaryContext, scriptInterface.GetRuntime());
     390        m_SecondaryComponentManager->LoadComponentTypes();
     391
     392        delete m_SecondaryLoadedScripts;
     393        m_SecondaryLoadedScripts = new std::set<VfsPath>();
     394        ENSURE(LoadDefaultScripts(*m_SecondaryComponentManager, m_SecondaryLoadedScripts));
     395        ResetComponentState(*m_SecondaryComponentManager, false, false);
    366396
    367397        // Load the trigger scripts after we have loaded the simulation.
    368398        {
    369             JSContext* cx2 = secondaryComponentManager.GetScriptInterface().GetContext();
     399            JSContext* cx2 = m_SecondaryComponentManager->GetScriptInterface().GetContext();
    370400            JSAutoRequest rq2(cx2);
    371401            JS::RootedValue mapSettingsCloned(cx2,
    372                 secondaryComponentManager.GetScriptInterface().CloneValueFromOtherContext(
     402                m_SecondaryComponentManager->GetScriptInterface().CloneValueFromOtherContext(
    373403                    scriptInterface, m_MapSettings));
    374             ENSURE(LoadTriggerScripts(secondaryComponentManager, mapSettingsCloned, &secondaryLoadedScripts));
     404            ENSURE(LoadTriggerScripts(*m_SecondaryComponentManager, mapSettingsCloned, m_SecondaryLoadedScripts));
    375405        }
    376406
    377407        // Load the map into the secondary simulation
    378408
    379409        LDR_BeginRegistering();
    void CSimulation2Impl::Update(int turnLe  
    391421            std::wstring mapFile;
    392422            scriptInterface.GetProperty(m_InitAttributes, "map", mapFile);
    393423
    394424            VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp");
    395425            mapReader->LoadMap(mapfilename, scriptInterface.GetJSRuntime(), JS::UndefinedHandleValue,
    396                 &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
    397                 NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
     426                m_SecondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
     427                NULL, NULL, m_SecondaryContext, INVALID_PLAYER, true); // throws exception on failure
    398428        }
    399429
    400430        LDR_EndRegistering();
    401431        ENSURE(LDR_NonprogressiveLoad() == INFO::OK);
     432        ENSURE(m_SecondaryComponentManager->DeserializeState(primaryStateBefore.state));
     433    }
    402434
    403         ENSURE(secondaryComponentManager.DeserializeState(primaryStateBefore.state));
    404 
     435    if (m_EnableSerializationTest || m_TestingRejoin)
     436    {
    405437        SerializationTestState secondaryStateBefore;
    406         ENSURE(secondaryComponentManager.SerializeState(secondaryStateBefore.state));
     438        ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateBefore.state));
    407439        if (serializationTestDebugDump)
    408             ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateBefore.debug, false));
     440            ENSURE(m_SecondaryComponentManager->DumpDebugState(secondaryStateBefore.debug, false));
    409441        if (serializationTestHash)
    410             ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateBefore.hash, false));
     442            ENSURE(m_SecondaryComponentManager->ComputeStateHash(secondaryStateBefore.hash, false));
    411443
    412444        if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() ||
    413445            primaryStateBefore.hash != secondaryStateBefore.hash)
    414446        {
    415447            ReportSerializationFailure(&primaryStateBefore, NULL, &secondaryStateBefore, NULL);
    void CSimulation2Impl::Update(int turnLe  
    418450        SerializationTestState primaryStateAfter;
    419451        ENSURE(m_ComponentManager.SerializeState(primaryStateAfter.state));
    420452        if (serializationTestHash)
    421453            ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false));
    422454
    423         UpdateComponents(secondaryContext, turnLengthFixed,
    424             CloneCommandsFromOtherContext(scriptInterface, secondaryComponentManager.GetScriptInterface(), commands));
     455        UpdateComponents(*m_SecondaryContext, turnLengthFixed,
     456            CloneCommandsFromOtherContext(scriptInterface, m_SecondaryComponentManager->GetScriptInterface(), commands));
    425457        SerializationTestState secondaryStateAfter;
    426         ENSURE(secondaryComponentManager.SerializeState(secondaryStateAfter.state));
     458        ENSURE(m_SecondaryComponentManager->SerializeState(secondaryStateAfter.state));
    427459        if (serializationTestHash)
    428             ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateAfter.hash, false));
     460            ENSURE(m_SecondaryComponentManager->ComputeStateHash(secondaryStateAfter.hash, false));
    429461
    430462        if (primaryStateAfter.state.str() != secondaryStateAfter.state.str() ||
    431463            primaryStateAfter.hash != secondaryStateAfter.hash)
    432464        {
    433465            // Only do the (slow) dumping now we know we're going to need to report it
    434466            ENSURE(m_ComponentManager.DumpDebugState(primaryStateAfter.debug, false));
    435             ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateAfter.debug, false));
     467            ENSURE(m_SecondaryComponentManager->DumpDebugState(secondaryStateAfter.debug, false));
    436468
    437469            ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, &secondaryStateBefore, &secondaryStateAfter);
    438470        }
    439471    }
    440472
    CSimulation2::~CSimulation2()  
    586618    delete m;
    587619}
    588620
    589621// Forward all method calls to the appropriate CSimulation2Impl/CComponentManager methods:
    590622
     623void CSimulation2::EnableSerializationTest()
     624{
     625    m->m_EnableSerializationTest = true;
     626}
     627
     628void CSimulation2::EnableRejoinTest(int rejoinTestTurn)
     629{
     630    m->m_RejoinTestTurn = rejoinTestTurn;
     631}
     632
    591633void CSimulation2::EnableOOSLog()
    592634{
    593635    if (m->m_EnableOOSLog)
    594636        return;
    595637
    void CSimulation2::EnableOOSLog()  
    597639    m->m_OOSLogPath = createDateIndexSubdirectory(psLogDir() / "oos_logs");
    598640
    599641    debug_printf("Writing ooslogs to %s\n", m->m_OOSLogPath.string8().c_str());
    600642}
    601643
    602 void CSimulation2::EnableSerializationTest()
    603 {
    604     m->m_EnableSerializationTest = true;
    605 }
    606 
    607644entity_id_t CSimulation2::AddEntity(const std::wstring& templateName)
    608645{
    609646    return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewEntity());
    610647}
    611648
  • source/simulation2/Simulation2.h

    public:  
    5252    // TODO: CUnitManager should probably be handled automatically by this
    5353    // module, but for now we'll have it passed in externally instead
    5454    CSimulation2(CUnitManager* unitManager, shared_ptr<ScriptRuntime> rt, CTerrain* terrain);
    5555    ~CSimulation2();
    5656
    57     void EnableOOSLog();
    5857    void EnableSerializationTest();
     58    void EnableRejoinTest(int rejoinTestTurn);
     59    void EnableOOSLog();
    5960
    6061    /**
    6162     * Load all scripts in the specified directory (non-recursively),
    6263     * so they can register new component types and functions. This
    6364     * should be called immediately after constructing the CSimulation2 object.