class DiagnosticProcessor extends GridProcessorAdapter {
    /** @see IgniteSystemProperties#IGNITE_DUMP_PAGE_LOCK_ON_FAILURE */
    public static final boolean DFLT_DUMP_PAGE_LOCK_ON_FAILURE = true;

    /** Value of the system property that enables page locks dumping on failure. */
    private static final boolean IGNITE_DUMP_PAGE_LOCK_ON_FAILURE =
        IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_DUMP_PAGE_LOCK_ON_FAILURE,
            DFLT_DUMP_PAGE_LOCK_ON_FAILURE);

    /** Time formatter for dump file name. */
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'_'HH-mm-ss_SSS");

    /** Folder name for store diagnostic info. **/
    public static final String DEFAULT_TARGET_FOLDER = "diagnostic";

    /** Full path for store dubug info. */
    private final Path diagnosticPath;

    /** File I/O factory. */
    @Nullable private final FileIOFactory fileIOFactory;

    /**
     * Constructor.
     *
     * @param ctx Kernal context.
     */
    public DiagnosticProcessor(GridKernalContext ctx) throws IgniteCheckedException {
        super(ctx);

        diagnosticPath = U.resolveWorkDirectory(ctx.config().getWorkDirectory(), DEFAULT_TARGET_FOLDER, false)
            .toPath();

        fileIOFactory = isPersistenceEnabled(ctx.config()) ?
            ctx.config().getDataStorageConfiguration().getFileIOFactory() : null;
    }

    /**
     * Print diagnostic info about failure occurred on {@code ignite} instance.
     * Failure details is contained in {@code failureCtx}.
     *
     * @param failureCtx Failure context.
     */
    public void onFailure(FailureContext failureCtx) {
        // Dump data structures page locks.
        if (IGNITE_DUMP_PAGE_LOCK_ON_FAILURE)
            ctx.cache().context().diagnostic().pageLockTracker().dumpLocksToLog();

        CorruptedDataStructureException corruptedDataStructureEx =
            X.cause(failureCtx.error(), CorruptedDataStructureException.class);

        if (corruptedDataStructureEx != null && !F.isEmpty(corruptedDataStructureEx.pageIds()) && fileIOFactory != null) {
            File[] walDirs = walDirs(ctx);

            if (F.isEmpty(walDirs)) {
                if (log.isInfoEnabled())
                    log.info("Skipping dump diagnostic info due to WAL not configured");
            }
            else {
                try {
                    File corruptedPagesFile = corruptedPagesFile(
                        diagnosticPath,
                        fileIOFactory,
                        corruptedDataStructureEx.groupId(),
                        corruptedDataStructureEx.pageIds()
                    );

                    String walDirsStr = Arrays.stream(walDirs).map(File::getAbsolutePath)
                        .collect(joining(", ", "[", "]"));

                    String args = "walDir=" + walDirs[0].getAbsolutePath() + (walDirs.length == 1 ? "" :
                        " walArchiveDir=" + walDirs[1].getAbsolutePath());

                    if (ctx.config().getDataStorageConfiguration().getPageSize() != DFLT_PAGE_SIZE)
                        args += " pageSize=" + ctx.config().getDataStorageConfiguration().getPageSize();

                    args += " pages=" + corruptedPagesFile.getAbsolutePath();

                    log.warning(corruptedDataStructureEx.getClass().getSimpleName() + " has occurred. " +
                        "To diagnose it, make a backup of the following directories: " + walDirsStr + ". " +
                        "Then, run the following command: java -cp <classpath> " +
                        "org.apache.ignite.development.utils.IgniteWalConverter " + args);
                }
                catch (Throwable t) {
                    String pages = LongStream.of(corruptedDataStructureEx.pageIds())
                        .mapToObj(pageId -> corruptedDataStructureEx.groupId() + ":" + pageId)
                        .collect(joining("\n", "", ""));

                    log.error("Failed to dump diagnostic info of partition corruption. Page ids:\n" + pages, t);
                }
            }
        }
    }

    /**
     * Creation and filling of a file with pages that can be corrupted.
     * Pages are written on each line in format "grpId:pageId".
     * File name format "corruptedPages_yyyy-MM-dd'_'HH-mm-ss_SSS.txt".
     *
     * @param dirPath   Path to the directory where the file will be created.
     * @param ioFactory File I/O factory.
     * @param grpId     Cache group id.
     * @param pageIds   PageId's that can be corrupted.
     * @return Created and filled file.
     * @throws IOException If an I/O error occurs.
     */
    public static File corruptedPagesFile(
        Path dirPath,
        FileIOFactory ioFactory,
        int grpId,
        long... pageIds
    ) throws IOException {
        dirPath.toFile().mkdirs();

        File f = dirPath.resolve("corruptedPages_" + LocalDateTime.now().format(TIME_FORMATTER) + ".txt").toFile();

        assert !f.exists();

        try (FileIO fileIO = ioFactory.create(f)) {
            for (long pageId : pageIds) {
                byte[] bytes = (grpId + ":" + pageId + U.nl()).getBytes(UTF_8);

                int left = bytes.length;

                while ((left - fileIO.writeFully(bytes, bytes.length - left, left)) > 0)
                    ;
            }

            fileIO.force();
        }

        return f;
    }

    /**
     * Getting the WAL directories.
     * Note:
     * Index 0: WAL working directory.
     * Index 1: WAL archive directory (may be absent).
     *
     * @param ctx Kernal context.
     * @return WAL directories.
     */
    @Nullable static File[] walDirs(GridKernalContext ctx) {<FILL_FUNCTION_BODY>}
}

        IgniteWriteAheadLogManager walMgr = ctx.cache().context().wal();

        if (walMgr instanceof FileWriteAheadLogManager) {
            SegmentRouter sr = ((FileWriteAheadLogManager)walMgr).getSegmentRouter();

            if (sr != null) {
                File workDir = sr.getWalWorkDir();
                return sr.hasArchive() ? F.asArray(workDir, sr.getWalArchiveDir()) : F.asArray(workDir);
            }
        }

        return null;
    
<methods>public void collectGridNodeData(org.apache.ignite.spi.discovery.DiscoveryDataBag) ,public void collectJoiningNodeData(org.apache.ignite.spi.discovery.DiscoveryDataBag) ,public org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType discoveryDataType() ,public void onDisconnected(IgniteFuture<?>) throws org.apache.ignite.IgniteCheckedException,public void onGridDataReceived(org.apache.ignite.spi.discovery.DiscoveryDataBag.GridDiscoveryData) ,public void onJoiningNodeDataReceived(org.apache.ignite.spi.discovery.DiscoveryDataBag.JoiningNodeDiscoveryData) ,public void onKernalStart(boolean) throws org.apache.ignite.IgniteCheckedException,public void onKernalStop(boolean) ,public IgniteInternalFuture<?> onReconnected(boolean) throws org.apache.ignite.IgniteCheckedException,public void printMemoryStats() ,public void start() throws org.apache.ignite.IgniteCheckedException,public void stop(boolean) throws org.apache.ignite.IgniteCheckedException,public java.lang.String toString() ,public org.apache.ignite.spi.IgniteNodeValidationResult validateNode(org.apache.ignite.cluster.ClusterNode) ,public org.apache.ignite.spi.IgniteNodeValidationResult validateNode(org.apache.ignite.cluster.ClusterNode, org.apache.ignite.spi.discovery.DiscoveryDataBag.JoiningNodeDiscoveryData) <variables>private static final java.lang.String DIAGNOSTIC_LOG_CATEGORY,protected final non-sealed org.apache.ignite.internal.GridKernalContext ctx,protected final non-sealed org.apache.ignite.IgniteLogger diagnosticLog,protected final non-sealed org.apache.ignite.IgniteLogger log