Skip to content

Commit

Permalink
doctor: check statistics start/end time (#507)
Browse files Browse the repository at this point in the history
**Public-Facing Changes**
Updated `mcap doctor` to check the correctness of Statistics message_start_time/message_end_time.

**Description**
Fixes #501.

Reports new errors on the file from #501:
```
Examining /Users/jacob/Downloads/whattimeisit.mcap
Statistics has message start time 1659409665222474798, but the minimum chunk start time is 0
Statistics has message end time 9900000000, but the maximum chunk end time is 1659409665222474798
```
  • Loading branch information
jtbandes authored Aug 3, 2022
1 parent 38f85ab commit 7f8ac8f
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions go/cli/mcap/cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ type mcapDoctor struct {

// Map from chunk offset to chunk index
chunkIndexes map[uint64]*mcap.ChunkIndex

messageCount uint64
minLogTime uint64
maxLogTime uint64
statistics *mcap.Statistics
}

func (doctor *mcapDoctor) warn(format string, v ...any) {
Expand Down Expand Up @@ -172,6 +177,8 @@ func (doctor *mcapDoctor) examineChunk(chunk *mcap.Chunk) {
maxLogTime = message.LogTime
}

doctor.messageCount++

default:
doctor.error("Illegal record in chunk: %d", tokenType)
}
Expand All @@ -186,6 +193,13 @@ func (doctor *mcapDoctor) examineChunk(chunk *mcap.Chunk) {
doctor.error("Chunk.message_end_time %d does not match the latest message log time %d",
chunk.MessageEndTime, maxLogTime)
}

if minLogTime < doctor.minLogTime {
doctor.minLogTime = minLogTime
}
if maxLogTime > doctor.maxLogTime {
doctor.maxLogTime = maxLogTime
}
}

func (doctor *mcapDoctor) Examine() {
Expand Down Expand Up @@ -219,7 +233,7 @@ func (doctor *mcapDoctor) Examine() {
}

if header.Library == "" {
doctor.warn("Header.library field should be non-empty. Its good to include a library field for reference.")
doctor.warn("Header.library field should be non-empty. The library field should be set to a value that identifies the software which produced the file.")
}

var customProfile = strings.HasPrefix(header.Profile, "x-")
Expand Down Expand Up @@ -272,6 +286,16 @@ func (doctor *mcapDoctor) Examine() {
message.LogTime, channel.Topic, lastMessageTime)
}
lastMessageTime = message.LogTime

if message.LogTime < doctor.minLogTime {
doctor.minLogTime = message.LogTime
}
if message.LogTime > doctor.maxLogTime {
doctor.maxLogTime = message.LogTime
}

doctor.messageCount++

case mcap.TokenChunk:
chunk, err := mcap.ParseChunk(data)
if err != nil {
Expand Down Expand Up @@ -304,10 +328,14 @@ func (doctor *mcapDoctor) Examine() {
doctor.error("Failed to parse attachment index:", err)
}
case mcap.TokenStatistics:
_, err := mcap.ParseStatistics(data)
statistics, err := mcap.ParseStatistics(data)
if err != nil {
doctor.error("Failed to parse statistics:", err)
}
if doctor.statistics != nil {
doctor.error("File contains multiple Statistics records")
}
doctor.statistics = statistics
case mcap.TokenMetadata:
_, err := mcap.ParseMetadata(data)
if err != nil {
Expand Down Expand Up @@ -369,6 +397,20 @@ func (doctor *mcapDoctor) Examine() {
doctor.error("Chunk at offset %d has uncompressed size %d, but its chunk index has uncompressed size %d", chunkOffset, chunk.UncompressedSize, chunkIndex.UncompressedSize)
}
}

if doctor.statistics != nil {
if doctor.messageCount > 0 {
if doctor.statistics.MessageStartTime != doctor.minLogTime {
doctor.error("Statistics has message start time %d, but the minimum message start time is %d", doctor.statistics.MessageStartTime, doctor.minLogTime)
}
if doctor.statistics.MessageEndTime != doctor.maxLogTime {
doctor.error("Statistics has message end time %d, but the maximum message end time is %d", doctor.statistics.MessageEndTime, doctor.maxLogTime)
}
}
if doctor.statistics.MessageCount != doctor.messageCount {
doctor.error("Statistics has message count %d, but actual number of messages is %d", doctor.statistics.MessageCount, doctor.messageCount)
}
}
}

func newMcapDoctor(reader io.ReadSeeker) *mcapDoctor {
Expand All @@ -377,6 +419,7 @@ func newMcapDoctor(reader io.ReadSeeker) *mcapDoctor {
channels: make(map[uint16]*mcap.Channel),
schemas: make(map[uint16]*mcap.Schema),
chunkIndexes: make(map[uint64]*mcap.ChunkIndex),
minLogTime: math.MaxUint64,
}
}

Expand Down

0 comments on commit 7f8ac8f

Please sign in to comment.