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
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 |
# File 'lib/sperf.rb', line 533 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
450 451 452 453 454 455 456 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 |
# File 'lib/sperf.rb', line 450 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
577 578 579 580 |
# File 'lib/sperf.rb', line 577 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
573 574 575 |
# File 'lib/sperf.rb', line 573 def encode_int64(field, value) encode_varint((field << 3) | 0) + encode_varint(value < 0 ? value + (1 << 64) : value) end |
.encode_message(field, data) ⇒ Object
582 583 584 |
# File 'lib/sperf.rb', line 582 def (field, data) encode_bytes(field, data) end |
.encode_packed_int64(field, values) ⇒ Object
595 596 597 598 |
# File 'lib/sperf.rb', line 595 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
590 591 592 593 |
# File 'lib/sperf.rb', line 590 def encode_packed_uint64(field, values) inner = values.map { |v| encode_varint(v) }.join encode_bytes(field, inner) end |
.encode_uint64(field, value) ⇒ Object
569 570 571 |
# File 'lib/sperf.rb', line 569 def encode_uint64(field, value) encode_varint((field << 3) | 0) + encode_varint(value) end |
.encode_value_type(type_idx, unit_idx) ⇒ Object
586 587 588 |
# File 'lib/sperf.rb', line 586 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 —
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 |
# File 'lib/sperf.rb', line 553 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 |