Module: Sperf::PProf
- Defined in:
- lib/sperf.rb
Overview
Hand-written protobuf encoder for pprof profile format. Only runs once at stop time, so performance is not critical.
Samples from C are: [[[path_str, label_str], …], weight] This encoder builds its own string table for pprof output.
Class Method Summary collapse
- .build_tables(merged) ⇒ Object
- .encode(data) ⇒ Object
- .encode_bytes(field, data) ⇒ Object
- .encode_int64(field, value) ⇒ Object
- .encode_message(field, data) ⇒ Object
- .encode_packed_int64(field, values) ⇒ Object
- .encode_packed_uint64(field, values) ⇒ Object
- .encode_uint64(field, value) ⇒ Object
- .encode_value_type(type_idx, unit_idx) ⇒ Object
-
.encode_varint(value) ⇒ Object
— Protobuf encoding helpers —.
Class Method Details
.build_tables(merged) ⇒ Object
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 |
# File 'lib/sperf.rb', line 540 def build_tables(merged) locations = {} functions = {} next_id = 1 merged.each do |frames, _weight| frames.each do |frame| unless locations.key?(frame) locations[frame] = next_id functions[frame] = next_id next_id += 1 end end end [locations, functions] end |
.encode(data) ⇒ Object
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
# File 'lib/sperf.rb', line 457 def encode(data) samples_raw = data[:samples] frequency = data[:frequency] interval_ns = 1_000_000_000 / frequency mode = data[:mode] || :cpu # Build string table: index 0 must be "" string_table = [""] string_index = { "" => 0 } intern = ->(s) { string_index[s] ||= begin idx = string_table.size string_table << s idx end } # Convert string frames to index frames and merge identical stacks merged = Hash.new(0) samples_raw.each do |frames, weight| key = frames.map { |path, label| [intern.(path), intern.(label)] } merged[key] += weight end merged = merged.to_a # Build location/function tables locations, functions = build_tables(merged) # Intern type label and unit type_label = mode == :wall ? "wall" : "cpu" type_idx = intern.(type_label) ns_idx = intern.("nanoseconds") # Encode Profile message buf = "".b # field 1: sample_type (repeated ValueType) buf << (1, encode_value_type(type_idx, ns_idx)) # field 2: sample (repeated Sample) merged.each do |frames, weight| sample_buf = "".b loc_ids = frames.map { |f| locations[f] } sample_buf << encode_packed_uint64(1, loc_ids) sample_buf << encode_packed_int64(2, [weight]) buf << (2, sample_buf) end # field 4: location (repeated Location) locations.each do |frame, loc_id| loc_buf = "".b loc_buf << encode_uint64(1, loc_id) line_buf = "".b func_id = functions[frame] line_buf << encode_uint64(1, func_id) loc_buf << (4, line_buf) buf << (4, loc_buf) end # field 5: function (repeated Function) functions.each do |frame, func_id| func_buf = "".b func_buf << encode_uint64(1, func_id) func_buf << encode_int64(2, frame[1]) # name (label_idx) func_buf << encode_int64(4, frame[0]) # filename (path_idx) buf << (5, func_buf) end # field 6: string_table (repeated string) string_table.each do |s| buf << encode_bytes(6, s.encode("UTF-8")) end # field 11: period_type (ValueType) buf << (11, encode_value_type(type_idx, ns_idx)) # field 12: period (int64) buf << encode_int64(12, interval_ns) buf end |
.encode_bytes(field, data) ⇒ Object
584 585 586 587 |
# File 'lib/sperf.rb', line 584 def encode_bytes(field, data) data = data.b if data.respond_to?(:b) encode_varint((field << 3) | 2) + encode_varint(data.bytesize) + data end |
.encode_int64(field, value) ⇒ Object
580 581 582 |
# File 'lib/sperf.rb', line 580 def encode_int64(field, value) encode_varint((field << 3) | 0) + encode_varint(value < 0 ? value + (1 << 64) : value) end |
.encode_message(field, data) ⇒ Object
589 590 591 |
# File 'lib/sperf.rb', line 589 def (field, data) encode_bytes(field, data) end |
.encode_packed_int64(field, values) ⇒ Object
602 603 604 605 |
# File 'lib/sperf.rb', line 602 def encode_packed_int64(field, values) inner = values.map { |v| encode_varint(v < 0 ? v + (1 << 64) : v) }.join encode_bytes(field, inner) end |
.encode_packed_uint64(field, values) ⇒ Object
597 598 599 600 |
# File 'lib/sperf.rb', line 597 def encode_packed_uint64(field, values) inner = values.map { |v| encode_varint(v) }.join encode_bytes(field, inner) end |
.encode_uint64(field, value) ⇒ Object
576 577 578 |
# File 'lib/sperf.rb', line 576 def encode_uint64(field, value) encode_varint((field << 3) | 0) + encode_varint(value) end |
.encode_value_type(type_idx, unit_idx) ⇒ Object
593 594 595 |
# File 'lib/sperf.rb', line 593 def encode_value_type(type_idx, unit_idx) encode_int64(1, type_idx) + encode_int64(2, unit_idx) end |
.encode_varint(value) ⇒ Object
— Protobuf encoding helpers —
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/sperf.rb', line 560 def encode_varint(value) value = value & 0xFFFFFFFF_FFFFFFFF if value < 0 buf = "".b loop do byte = value & 0x7F value >>= 7 if value > 0 buf << (byte | 0x80).chr else buf << byte.chr break end end buf end |