* Decompress the log file that was open()-ed and print the log messages out * in chronological order. * * \param outputFd * The file descriptor to print the log messages to * * \return * The number of log messages encountered. A negative value indicates error */
| 1792 | * The number of log messages encountered. A negative value indicates error |
| 1793 | */ |
| 1794 | int64_t |
| 1795 | Log::Decoder::decompressTo(FILE* outputFd) |
| 1796 | { |
| 1797 | if (filename.empty() || !inputFd) |
| 1798 | return -1; |
| 1799 | |
| 1800 | // In ordered decompression, we must sort the entries by time which means |
| 1801 | // we need to buffer in 3 rounds of NanoLog output. We need more than one |
| 1802 | // round of output because the compression is non-quiescent, which means |
| 1803 | // that as we're outputting the nth buffer, new entries may be added to |
| 1804 | // n-1 and n, and the entries in n could logically come /before/ the new |
| 1805 | // entries added to n-1. The reason why we need at least 3 is due to an |
| 1806 | // implementation detail in StagingBuffer whereby one peek() does not |
| 1807 | // return all the data and at least 2 peek()'s are needed to deplete a |
| 1808 | // buffer. |
| 1809 | static const uint32_t stagesToBuffer = 3; |
| 1810 | std::vector<BufferFragment*> stages[stagesToBuffer]; |
| 1811 | |
| 1812 | // Running number of stages being kept in stages |
| 1813 | uint32_t stagesBuffered = 0; |
| 1814 | |
| 1815 | // Indicates that all stages must be depleted before continuing |
| 1816 | // processing the log file. This should only be true when we detect |
| 1817 | // the start of a new execution(s) log appended to inputFd or we |
| 1818 | // reached the end of the current file |
| 1819 | bool mustDepleteAllStages = false; |
| 1820 | |
| 1821 | LogMessage logArguments; |
| 1822 | while (!feof(inputFd) && good) { |
| 1823 | |
| 1824 | // Step 1: Read in up to a certain number of "stages" of BufferFragments |
| 1825 | mustDepleteAllStages = false; |
| 1826 | while (!feof(inputFd) && good && !mustDepleteAllStages) { |
| 1827 | EntryType entry = peekEntryType(inputFd); |
| 1828 | bool newStage = false; |
| 1829 | |
| 1830 | switch (entry) { |
| 1831 | case EntryType::BUFFER_EXTENT: |
| 1832 | { |
| 1833 | BufferFragment *bf = allocateBufferFragment(); |
| 1834 | good = bf->readBufferExtent(inputFd, &newStage); |
| 1835 | ++numBufferFragmentsRead; |
| 1836 | |
| 1837 | if (good) |
| 1838 | stages[stagesBuffered].push_back(bf); |
| 1839 | |
| 1840 | break; |
| 1841 | } |
| 1842 | case EntryType::CHECKPOINT: |
| 1843 | // New logical start to the logs detected, at this point |
| 1844 | // we should make sure we've printed all the buffered logs |
| 1845 | // before continuing to parse the next logical start. |
| 1846 | if (!stages[0].empty()) { |
| 1847 | mustDepleteAllStages = true; |
| 1848 | break; |
| 1849 | } |
| 1850 | |
| 1851 | // We're safe, all the stages are empty |