1# GStreamer 2# Copyright (C) 2018 Pengutronix, Michael Olbrich <m.olbrich@pengutronix.de> 3# 4# gst_gdb.py: gdb extension for GStreamer 5# 6# This library is free software; you can redistribute it and/or 7# modify it under the terms of the GNU Library General Public 8# License as published by the Free Software Foundation; either 9# version 2 of the License, or (at your option) any later version. 10# 11# This library is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14# Library General Public License for more details. 15# 16# You should have received a copy of the GNU Library General Public 17# License along with this library; if not, write to the 18# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19# Boston, MA 02110-1301, USA. 20 21import gdb 22import sys 23import re 24 25from glib_gobject_helper import g_type_to_name, g_type_name_from_instance, \ 26 g_type_to_typenode, g_quark_to_string 27 28if sys.version_info[0] >= 3: 29 long = int 30 31 32def is_gst_type(val, klass): 33 def _is_gst_type(type): 34 if str(type) == klass: 35 return True 36 37 while type.code == gdb.TYPE_CODE_TYPEDEF: 38 type = type.target() 39 40 if type.code != gdb.TYPE_CODE_STRUCT: 41 return False 42 43 fields = type.fields() 44 if len(fields) < 1: 45 return False 46 47 first_field = fields[0] 48 return _is_gst_type(first_field.type) 49 50 type = val.type 51 if type.code != gdb.TYPE_CODE_PTR: 52 return False 53 type = type.target() 54 return _is_gst_type(type) 55 56 57class GstMiniObjectPrettyPrinter: 58 "Prints a GstMiniObject instance pointer" 59 60 def __init__(self, val): 61 self.val = val 62 63 def to_string(self): 64 try: 65 inst = self.val.cast(gdb.lookup_type("GstMiniObject").pointer()) 66 gtype = inst["type"] 67 name = g_type_to_name(gtype) 68 return "0x%x [%s]" % (long(self.val), name) 69 except RuntimeError: 70 return "0x%x" % long(self.val) 71 72 73class GstObjectPrettyPrinter: 74 "Prints a GstObject instance" 75 76 def __init__(self, val): 77 self.val = val 78 79 def to_string(self): 80 try: 81 name = g_type_name_from_instance(self.val) 82 if not name: 83 name = str(self.val.type.target()) 84 if long(self.val) != 0: 85 inst = self.val.cast(gdb.lookup_type("GstObject").pointer()) 86 inst_name = inst["name"].string() 87 if inst_name: 88 name += "|" + inst_name 89 return ("0x%x [%s]") % (long(self.val), name) 90 except RuntimeError: 91 return "0x%x" % long(self.val) 92 93 94GST_SECOND = 1000000000 95GST_CLOCK_TIME_NONE = 2**64-1 96GST_CLOCK_STIME_NONE = -2**63 97 98 99def format_time(n, signed=False): 100 prefix = "" 101 invalid = False 102 if signed: 103 if n == GST_CLOCK_STIME_NONE: 104 invalid = True 105 prefix = "+" if n >= 0 else "-" 106 n = abs(n) 107 else: 108 if n == GST_CLOCK_TIME_NONE: 109 invalid = True 110 111 if invalid: 112 return "99:99:99.999999999" 113 114 return "%s%u:%02u:%02u.%09u" % ( 115 prefix, 116 n / (GST_SECOND * 60 * 60), 117 (n / (GST_SECOND * 60)) % 60, 118 (n / GST_SECOND) % 60, 119 n % GST_SECOND) 120 121 122def format_time_value(val): 123 return format_time(int(val), str(val.type) == "GstClockTimeDiff") 124 125 126class GstClockTimePrinter: 127 "Prints a GstClockTime / GstClockTimeDiff" 128 129 def __init__(self, val): 130 self.val = val 131 132 def to_string(self): 133 return "%d [%s]" % (int(self.val), format_time_value(self.val)) 134 135 136def gst_pretty_printer_lookup(val): 137 if is_gst_type(val, "GstMiniObject"): 138 return GstMiniObjectPrettyPrinter(val) 139 if is_gst_type(val, "GstObject"): 140 return GstObjectPrettyPrinter(val) 141 if str(val.type) == "GstClockTime" or str(val.type) == "GstClockTimeDiff": 142 return GstClockTimePrinter(val) 143 return None 144 145 146def save_memory_access(fallback): 147 def _save_memory_access(func): 148 def wrapper(*args, **kwargs): 149 try: 150 return func(*args, **kwargs) 151 except gdb.MemoryError: 152 return fallback 153 return wrapper 154 return _save_memory_access 155 156 157def save_memory_access_print(message): 158 def _save_memory_access_print(func): 159 def wrapper(*args, **kwargs): 160 try: 161 func(*args, **kwargs) 162 except gdb.MemoryError: 163 _gdb_write(args[1], message) 164 return wrapper 165 return _save_memory_access_print 166 167 168def _g_type_from_instance(instance): 169 if long(instance) != 0: 170 try: 171 inst = instance.cast(gdb.lookup_type("GTypeInstance").pointer()) 172 klass = inst["g_class"] 173 gtype = klass["g_type"] 174 return gtype 175 except RuntimeError: 176 pass 177 return None 178 179 180def g_inherits_type(val, typename): 181 if is_gst_type(val, "GstObject"): 182 gtype = _g_type_from_instance(val) 183 if gtype is None: 184 return False 185 typenode = g_type_to_typenode(gtype) 186 elif is_gst_type(val, "GstMiniObject"): 187 mini = val.cast(gdb.lookup_type("GstMiniObject").pointer()) 188 try: 189 typenode = mini["type"].cast(gdb.lookup_type("TypeNode").pointer()) 190 except gdb.MemoryError: 191 return False 192 else: 193 return False 194 195 for i in range(typenode["n_supers"]): 196 if g_type_to_name(typenode["supers"][i]) == typename: 197 return True 198 return False 199 200 201def gst_is_bin(val): 202 return g_inherits_type(val, "GstBin") 203 204 205def _g_array_iter(array, element_type): 206 if array == 0: 207 return 208 try: 209 item = array["data"].cast(element_type.pointer()) 210 for i in range(int(array["len"])): 211 yield item[i] 212 except gdb.MemoryError: 213 pass 214 215 216def _g_value_get_value(val): 217 typenode = g_type_to_typenode(val["g_type"]) 218 if not typenode: 219 return None 220 tname = g_quark_to_string(typenode["qname"]) 221 fname = g_type_to_name(typenode["supers"][int(typenode["n_supers"])]) 222 if fname in ("gchar", "guchar", "gboolean", "gint", "guint", "glong", 223 "gulong", "gint64", "guint64", "gfloat", "gdouble", 224 "gpointer", "GFlags"): 225 try: 226 t = gdb.lookup_type(tname).pointer() 227 except RuntimeError: 228 t = gdb.lookup_type(fname).pointer() 229 elif fname == "gchararray": 230 t = gdb.lookup_type("char").pointer().pointer() 231 elif fname == "GstBitmask": 232 t = gdb.lookup_type("guint64").pointer() 233 elif fname == "GstFlagSet": 234 t = gdb.lookup_type("guint").pointer().pointer() 235 return val["data"].cast(t) 236 elif fname == "GstFraction": 237 t = gdb.lookup_type("gint").pointer().pointer() 238 return val["data"].cast(t) 239 elif fname == "GstFractionRange": 240 t = gdb.lookup_type("GValue").pointer().pointer() 241 elif fname == "GstValueList": 242 t = gdb.lookup_type("GArray").pointer().pointer() 243 elif fname in ("GBoxed", "GObject"): 244 try: 245 t = gdb.lookup_type(tname).pointer().pointer() 246 except RuntimeError: 247 t = gdb.lookup_type(tname).pointer().pointer() 248 else: 249 return val["data"] 250 251 return val["data"].cast(t).dereference() 252 253 254def gst_object_from_value(value): 255 if value.type.code != gdb.TYPE_CODE_PTR: 256 value = value.address 257 258 if not is_gst_type(value, "GstObject"): 259 raise Exception("'%s' is not a GstObject" % args[0]) 260 261 return value.cast(gdb.lookup_type("GstObject").pointer()) 262 263 264def gst_object_pipeline(obj): 265 try: 266 while obj["parent"] != 0: 267 tmp = obj["parent"] 268 # sanity checks to handle memory corruption 269 if g_inherits_type(obj, "GstElement") and \ 270 GdbGstElement(obj) not in GdbGstElement(tmp).children(): 271 break 272 if g_inherits_type(obj, "GstPad"): 273 pad = GdbGstPad(obj) 274 if g_inherits_type(tmp, "GstElement"): 275 if pad not in GdbGstElement(tmp).pads(): 276 break 277 elif g_inherits_type(tmp, "GstProxyPad"): 278 t = gdb.lookup_type("GstProxyPad").pointer() 279 if pad != GdbGstPad(tmp.cast(t)["priv"]["internal"]): 280 break 281 obj = tmp 282 except gdb.MemoryError: 283 pass 284 285 if not g_inherits_type(obj, "GstElement"): 286 raise Exception("Toplevel parent is not a GstElement") 287 return obj.cast(gdb.lookup_type("GstElement").pointer()) 288 289 290def element_state_to_name(state): 291 names = [ 292 "VOID_PENDING", 293 "NULL", 294 "READY", 295 "PAUSED", 296 "PLAYING"] 297 return names[state] if state < len(names) else "UNKNOWN" 298 299 300def task_state_to_name(state): 301 names = [ 302 "STARTED", 303 "STOPPED", 304 "PAUSED"] 305 return names[state] if state < len(names) else "UNKNOWN" 306 307 308def _gdb_write(indent, text): 309 gdb.write("%s%s\n" % (" " * indent, text)) 310 311 312class GdbCapsFeatures: 313 def __init__(self, val): 314 self.val = val 315 316 def size(self): 317 if long(self.val) == 0: 318 return 0 319 return int(self.val["array"]["len"]) 320 321 def items(self): 322 if long(self.val) == 0: 323 return 324 for q in _g_array_iter(self.val["array"], gdb.lookup_type("GQuark")): 325 yield q 326 327 def __eq__(self, other): 328 if self.size() != other.size(): 329 return False 330 a1 = list(self.items()) 331 a2 = list(other.items()) 332 for item in a1: 333 if item not in a2: 334 return False 335 return True 336 337 def __str__(self): 338 if long(self.val) == 0: 339 return "" 340 count = self.size() 341 if int(self.val["is_any"]) == 1 and count == 0: 342 return "(ANY)" 343 if count == 0: 344 return "" 345 s = "" 346 for f in self.items(): 347 ss = g_quark_to_string(f) 348 if ss != "memory:SystemMemory" or count > 1: 349 s += ", " if s else "" 350 s += ss 351 return s 352 353 354class GdbGstCaps: 355 def __init__(self, val): 356 self.val = val.cast(gdb.lookup_type("GstCapsImpl").pointer()) 357 358 def size(self): 359 return int(self.val["array"]["len"]) 360 361 def items(self): 362 gdb_type = gdb.lookup_type("GstCapsArrayElement") 363 for f in _g_array_iter(self.val["array"], gdb_type): 364 yield(GdbCapsFeatures(f["features"]), 365 GdbGstStructure(f["structure"])) 366 367 def __eq__(self, other): 368 if self.size() != other.size(): 369 return False 370 a1 = list(self.items()) 371 a2 = list(other.items()) 372 for i in range(self.size()): 373 if a1[i] != a2[i]: 374 return False 375 return True 376 377 def dot(self): 378 if self.size() == 0: 379 return "ANY" 380 s = "" 381 for (features, structure) in self.items(): 382 s += structure.name() 383 tmp = str(features) 384 if tmp: 385 s += "(" + tmp + ")" 386 s += "\\l" 387 if structure.size() > 0: 388 s += "\\l".join(structure.value_strings(" %18s: %s")) + "\\l" 389 return s 390 391 @save_memory_access_print("<inaccessible memory>") 392 def print(self, indent, prefix=""): 393 items = list(self.items()) 394 if len(items) != 1: 395 _gdb_write(indent, prefix) 396 prefix = "" 397 for (features, structure) in items: 398 s = "%s %s" % (prefix, structure.name()) 399 tmp = str(features) 400 if tmp: 401 s += "(" + tmp + ")" 402 _gdb_write(indent, s) 403 for val in structure.value_strings("%s: %s", False): 404 _gdb_write(indent+1, val) 405 return s 406 407 408class GdbGValue: 409 def __init__(self, val): 410 self.val = val 411 412 def fundamental_typename(self): 413 typenode = g_type_to_typenode(self.val["g_type"]) 414 if not typenode: 415 return None 416 return g_type_to_name(typenode["supers"][int(typenode["n_supers"])]) 417 418 def value(self): 419 return _g_value_get_value(self.val) 420 421 def __str__(self): 422 try: 423 value = self.value() 424 tname = self.fundamental_typename() 425 gvalue_type = gdb.lookup_type("GValue") 426 if tname == "GstFraction": 427 v = "%d/%d" % (value[0], value[1]) 428 elif tname == "GstBitmask": 429 v = "0x%016x" % long(value) 430 elif tname == "gboolean": 431 v = "false" if int(value) == 0 else "true" 432 elif tname == "GstFlagSet": 433 v = "%x:%x" % (value[0], value[1]) 434 elif tname == "GstIntRange": 435 rmin = int(value[0]["v_uint64"]) >> 32 436 rmax = int(value[0]["v_uint64"]) & 0xffffffff 437 step = int(value[1]["v_int"]) 438 if step == 1: 439 v = "[ %d, %d ]" % (rmin, rmax) 440 else: 441 v = "[ %d, %d, %d ]" % (rmin*step, rmax*step, step) 442 elif tname == "GstFractionRange": 443 v = "[ %s, %s ]" % (GdbGValue(value[0]), GdbGValue(value[1])) 444 elif tname in ("GstValueList", "GstValueArray"): 445 if gvalue_type.fields()[1].type == value.type: 446 gdb_type = gdb.lookup_type("GArray").pointer() 447 value = value[0]["v_pointer"].cast(gdb_type) 448 v = "<" 449 for l in _g_array_iter(value, gvalue_type): 450 v += " " if v == "<" else ", " 451 v += str(GdbGValue(l)) 452 v += " >" 453 elif tname in ("GEnum"): 454 v = "%s(%s)" % ( 455 g_type_to_name(g_type_to_typenode(self.val["g_type"])), 456 value["v_int"]) 457 else: 458 try: 459 v = value.string() 460 except RuntimeError: 461 # it is not a string-like type 462 if gvalue_type.fields()[1].type == value.type: 463 # don't print the raw GValue union 464 v = "<unknown type: %s>" % tname 465 else: 466 v = str(value) 467 except gdb.MemoryError: 468 v = "<inaccessible memory at 0x%x>" % int(self.val) 469 return v 470 471 def __eq__(self, other): 472 return self.val == other.val 473 474 475class GdbGstStructure: 476 def __init__(self, val): 477 self.val = val.cast(gdb.lookup_type("GstStructureImpl").pointer()) 478 479 @save_memory_access("<inaccessible memory>") 480 def name(self): 481 return g_quark_to_string(self.val["s"]["name"]) 482 483 @save_memory_access(0) 484 def size(self): 485 return int(self.val["fields_len"]) 486 487 def values(self): 488 item = self.val["fields"].cast(gdb.lookup_type("GstStructureField").pointer()) 489 for i in range(self.size()): 490 f = item[i] 491 key = g_quark_to_string(f["name"]) 492 value = GdbGValue(f["value"]) 493 yield(key, value) 494 495 def value(self, key): 496 for (k, value) in self.values(): 497 if k == key: 498 return value 499 raise KeyError(key) 500 501 def __eq__(self, other): 502 if self.size() != other.size(): 503 return False 504 a1 = list(self.values()) 505 a2 = list(other.values()) 506 for (key, value) in a1: 507 if (key, value) not in a2: 508 return False 509 return True 510 511 def value_strings(self, pattern, elide=True): 512 s = [] 513 for (key, value) in self.values(): 514 v = str(value) 515 if elide and len(v) > 25: 516 if v[0] in "[(<\"": 517 v = v[:20] + "... " + v[-1:] 518 else: 519 v = v[:22] + "..." 520 s.append(pattern % (key, v)) 521 return s 522 523 @save_memory_access_print("<inaccessible memory>") 524 def print(self, indent, prefix=None): 525 if prefix is not None: 526 _gdb_write(indent, "%s: %s" % (prefix, self.name())) 527 else: 528 _gdb_write(indent, "%s:" % (self.name())) 529 for (key, value) in self.values(): 530 _gdb_write(indent+1, "%s: %s" % (key, str(value))) 531 532 533class GdbGstSegment: 534 def __init__(self, val): 535 self.val = val 536 self.fmt = str(self.val["format"]).split("_")[-1].lower() 537 538 def format_value(self, n): 539 if self.fmt == "time": 540 return format_time(n, False) 541 else: 542 return str(n) 543 544 def print_optional(self, indent, key, skip=None): 545 value = int(self.val[key]) 546 if skip is None or value != skip: 547 _gdb_write(indent, "%s:%s %s" % 548 (key, (8-len(key))*" ", self.format_value(value))) 549 550 def print(self, indent, seqnum=None): 551 s = "segment:" 552 if seqnum: 553 s += "(seqnum: %s)" % seqnum 554 _gdb_write(indent, s) 555 rate = float(self.val["rate"]) 556 applied_rate = float(self.val["applied_rate"]) 557 if applied_rate != 1.0: 558 applied = "(applied rate: %g)" % applied_rate 559 else: 560 applied = "" 561 _gdb_write(indent+1, "rate: %g%s" % (rate, applied)) 562 self.print_optional(indent+1, "base", 0) 563 self.print_optional(indent+1, "offset", 0) 564 self.print_optional(indent+1, "start") 565 self.print_optional(indent+1, "stop", GST_CLOCK_TIME_NONE) 566 self.print_optional(indent+1, "time") 567 self.print_optional(indent+1, "position") 568 self.print_optional(indent+1, "duration", GST_CLOCK_TIME_NONE) 569 570 571class GdbGstEvent: 572 def __init__(self, val): 573 self.val = val.cast(gdb.lookup_type("GstEventImpl").pointer()) 574 575 @save_memory_access("<inaccessible memory>") 576 def typestr(self): 577 t = self.val["event"]["type"] 578 (event_quarks, _) = gdb.lookup_symbol("event_quarks") 579 event_quarks = event_quarks.value() 580 i = 0 581 while event_quarks[i]["name"] != 0: 582 if t == event_quarks[i]["type"]: 583 return event_quarks[i]["name"].string() 584 i += 1 585 return None 586 587 def structure(self): 588 return GdbGstStructure(self.val["structure"]) 589 590 @save_memory_access_print("<inaccessible memory>") 591 def print(self, indent): 592 typestr = self.typestr() 593 seqnum = self.val["event"]["seqnum"] 594 if typestr == "caps": 595 caps = GdbGstCaps(self.structure().value("caps").value()) 596 caps.print(indent, "caps (seqnum: %s):" % seqnum) 597 elif typestr == "stream-start": 598 stream_id = self.structure().value("stream-id").value() 599 _gdb_write(indent, "stream-start: (seqnum %s)" % seqnum) 600 _gdb_write(indent + 1, "stream-id: %s" % stream_id.string()) 601 elif typestr == "segment": 602 segment = self.structure().value("segment").value() 603 GdbGstSegment(segment).print(indent, seqnum) 604 elif typestr == "tag": 605 struct = self.structure() 606 # skip 'GstTagList-' 607 name = struct.name()[11:] 608 t = gdb.lookup_type("GstTagListImpl").pointer() 609 s = struct.value("taglist").value().cast(t)["structure"] 610 structure = GdbGstStructure(s) 611 _gdb_write(indent, "tag: %s (seqnum: %s)" % (name, seqnum)) 612 for (key, value) in structure.values(): 613 _gdb_write(indent+1, "%s: %s" % (key, str(value))) 614 else: 615 self.structure().print(indent, "%s (seqnum: %s)" % (typestr, seqnum)) 616 617 618class GdbGstBuffer: 619 def __init__(self, val): 620 self.val = val.cast(gdb.lookup_type("GstBuffer").pointer()) 621 622 def print_optional(self, indent, key, skip=None, format_func=str): 623 value = int(self.val[key]) 624 if skip is None or value != skip: 625 _gdb_write(indent, "%s:%s %s" % 626 (key, (8-len(key))*" ", format_func(value))) 627 628 @save_memory_access_print("<inaccessible memory>") 629 def print(self, indent): 630 _gdb_write(indent, "GstBuffer: (0x%x)" % self.val) 631 indent += 1 632 self.print_optional(indent, "pool", 0) 633 self.print_optional(indent, "pts", GST_CLOCK_TIME_NONE, format_time) 634 self.print_optional(indent, "dts", GST_CLOCK_TIME_NONE, format_time) 635 self.print_optional(indent, "duration", GST_CLOCK_TIME_NONE, format_time) 636 self.print_optional(indent, "offset", GST_CLOCK_TIME_NONE) 637 self.print_optional(indent, "offset_end", GST_CLOCK_TIME_NONE) 638 639 impl = self.val.cast(gdb.lookup_type("GstBufferImpl").pointer()) 640 meta_item = impl['item'] 641 if meta_item: 642 _gdb_write(indent, "Metas:") 643 indent += 1 644 while meta_item: 645 meta = meta_item['meta'] 646 meta_type_name = g_type_to_name(meta['info']['type']) 647 _gdb_write(indent, "%s:" % meta_type_name) 648 indent += 1 649 meta_info = str(meta.cast(gdb.lookup_type(meta_type_name))) 650 for l in meta_info.split('\n'): 651 _gdb_write(indent, l) 652 indent -= 1 653 meta_item = meta_item['next'] 654 else: 655 _gdb_write(indent, "(No meta)") 656 657 658class GdbGstQuery: 659 def __init__(self, val): 660 self.val = val.cast(gdb.lookup_type("GstQueryImpl").pointer()) 661 662 @save_memory_access("<inaccessible memory>") 663 def typestr(self): 664 t = self.val["query"]["type"] 665 (query_quarks, _) = gdb.lookup_symbol("query_quarks") 666 query_quarks = query_quarks.value() 667 i = 0 668 while query_quarks[i]["name"] != 0: 669 if t == query_quarks[i]["type"]: 670 return query_quarks[i]["name"].string() 671 i += 1 672 return None 673 674 def structure(self): 675 return GdbGstStructure(self.val["structure"]) 676 677 @save_memory_access_print("<inaccessible memory>") 678 def print(self, indent): 679 typestr = self.typestr() 680 self.structure().print(indent, typestr) 681 682 683class GdbGstObject: 684 def __init__(self, klass, val): 685 self.val = val.cast(klass) 686 687 @save_memory_access("<inaccessible memory>") 688 def name(self): 689 obj = self.val.cast(gdb.lookup_type("GstObject").pointer()) 690 return obj["name"].string() 691 692 def full_name(self): 693 parent = self.parent_element() 694 return "%s%s" % (parent.name() + ":" if parent else "", self.name()) 695 696 def dot_name(self): 697 ptr = self.val.cast(gdb.lookup_type("void").pointer()) 698 return re.sub('[^a-zA-Z0-9<>]', '_', "%s_%s" % (self.name(), str(ptr))) 699 700 def parent(self): 701 obj = self.val.cast(gdb.lookup_type("GstObject").pointer()) 702 return obj["parent"] 703 704 def parent_element(self): 705 p = self.parent() 706 if p != 0 and g_inherits_type(p, "GstElement"): 707 element = p.cast(gdb.lookup_type("GstElement").pointer()) 708 return GdbGstElement(element) 709 return None 710 711 def parent_pad(self): 712 p = self.parent() 713 if p != 0 and g_inherits_type(p, "GstPad"): 714 pad = p.cast(gdb.lookup_type("GstPad").pointer()) 715 return GdbGstPad(pad) 716 return None 717 718 719class GdbGstPad(GdbGstObject): 720 def __init__(self, val): 721 gdb_type = gdb.lookup_type("GstPad").pointer() 722 super(GdbGstPad, self).__init__(gdb_type, val) 723 724 def __eq__(self, other): 725 return self.val == other.val 726 727 def is_linked(self): 728 return long(self.val["peer"]) != 0 729 730 def peer(self): 731 return GdbGstPad(self.val["peer"]) 732 733 def direction(self): 734 return str(self.val["direction"]) 735 736 def events(self): 737 if long(self.val["priv"]) == 0: 738 return 739 array = self.val["priv"]["events"] 740 for ev in _g_array_iter(array, gdb.lookup_type("PadEvent")): 741 yield GdbGstEvent(ev["event"]) 742 743 def caps(self): 744 for ev in self.events(): 745 if ev.typestr() != "caps": 746 continue 747 return GdbGstCaps(ev.structure().value("caps").value()) 748 return None 749 750 def template_caps(self): 751 tmp = self.val["padtemplate"] 752 return GdbGstCaps(tmp["caps"]) if int(tmp) != 0 else None 753 754 def mode(self): 755 m = str(self.val["mode"]).split("_")[-1].lower() 756 if m in ("push", "pull"): 757 return m 758 return None 759 760 def pad_type(self): 761 s = str(self.val["direction"]).split("_")[-1].capitalize() 762 if g_inherits_type(self.val, "GstGhostPad"): 763 s += "Ghost" 764 return s + "Pad" 765 766 @save_memory_access_print("Pad(<inaccessible memory>)") 767 def print(self, indent): 768 m = ", " + self.mode() if self.mode() else "" 769 _gdb_write(indent, "%s(%s%s) {" % (self.pad_type(), self.name(), m)) 770 first = True 771 for ev in self.events(): 772 if first: 773 _gdb_write(indent+1, "events:") 774 first = False 775 ev.print(indent+2) 776 777 if self.is_linked(): 778 real = self.peer().parent_pad() 779 _gdb_write(indent+1, "peer: %s" % 780 (real.full_name() if real else self.peer().full_name())) 781 782 if g_inherits_type(self.val, "GstGhostPad"): 783 t = gdb.lookup_type("GstProxyPad").pointer() 784 internal = GdbGstPad(self.val.cast(t)["priv"]["internal"]) 785 if internal and internal.peer(): 786 _gdb_write(indent+1, "inner peer: %s" % 787 internal.peer().full_name()) 788 789 task = self.val["task"] 790 if long(task) != 0: 791 _gdb_write(indent+1, "task: %s" % 792 task_state_to_name(int(task["state"]))) 793 794 offset = long(self.val["offset"]) 795 if offset != 0: 796 _gdb_write(indent+1, "offset: %d [%s]" % 797 (offset, format_time(offset, True))) 798 799 _gdb_write(indent, "}") 800 801 def _dot(self, color, pname, indent): 802 spc = " " * indent 803 activation_mode = "-><" 804 style = "filled,solid" 805 template = self.val["padtemplate"] 806 if template != 0: 807 presence = template["presence"] 808 if str(presence) == "GST_PAD_SOMETIMES": 809 style = "filled,dotted" 810 if str(presence) == "GST_PAD_REQUEST": 811 style = "filled,dashed" 812 task_mode = "" 813 task = self.val["task"] 814 if long(task) != 0: 815 task_state = int(task["state"]) 816 if task_state == 0: # started 817 task_mode = "[T]" 818 if task_state == 2: # paused 819 task_mode = "[t]" 820 f = int(self.val["object"]["flags"]) 821 flags = "B" if f & 16 else "b" # GST_PAD_FLAG_BLOCKED 822 flags += "F" if f & 32 else "f" # GST_PAD_FLAG_FLUSHING 823 flags += "B" if f & 16 else "b" # GST_PAD_FLAG_BLOCKING 824 825 s = "%s %s_%s [color=black, fillcolor=\"%s\", " \ 826 "label=\"%s%s\\n[%c][%s]%s\", height=\"0.2\", style=\"%s\"];\n" % \ 827 (spc, pname, self.dot_name(), color, self.name(), "", 828 activation_mode[int(self.val["mode"])], flags, task_mode, style) 829 return s 830 831 def dot(self, indent): 832 spc = " " * indent 833 direction = self.direction() 834 element = self.parent_element() 835 ename = element.dot_name() if element else "" 836 s = "" 837 if g_inherits_type(self.val, "GstGhostPad"): 838 if direction == "GST_PAD_SRC": 839 color = "#ffdddd" 840 elif direction == "GST_PAD_SINK": 841 color = "#ddddff" 842 else: 843 color = "#ffffff" 844 845 t = gdb.lookup_type("GstProxyPad").pointer() 846 other = GdbGstPad(self.val.cast(t)["priv"]["internal"]) 847 if other: 848 s += other._dot(color, "", indent) 849 pname = self.dot_name() 850 other_element = other.parent_element() 851 other_ename = other_element.dot_name() if other_element else "" 852 other_pname = other.dot_name() 853 if direction == "GST_PAD_SRC": 854 s += "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n" % \ 855 (spc, other_ename, other_pname, ename, pname) 856 else: 857 s += "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n" % \ 858 (spc, ename, pname, other_ename, other_pname) 859 else: 860 if direction == "GST_PAD_SRC": 861 color = "#ffaaaa" 862 elif direction == "GST_PAD_SINK": 863 color = "#aaaaff" 864 else: 865 color = "#cccccc" 866 867 s += self._dot(color, ename, indent) 868 return s 869 870 def link_dot(self, indent, element): 871 spc = " " * indent 872 873 peer = self.peer() 874 peer_element = peer.parent_element() 875 876 caps = self.caps() 877 if not caps: 878 caps = self.template_caps() 879 peer_caps = peer.caps() 880 if not peer_caps: 881 peer_caps = peer.template_caps() 882 883 pname = self.dot_name() 884 ename = element.dot_name() if element else "" 885 peer_pname = peer.dot_name() 886 peer_ename = peer_element.dot_name() if peer_element else "" 887 888 if caps and peer_caps and caps == peer_caps: 889 s = "%s%s_%s -> %s_%s [label=\"%s\"]\n" % \ 890 (spc, ename, pname, peer_ename, peer_pname, caps.dot()) 891 elif caps and peer_caps and caps != peer_caps: 892 s = "%s%s_%s -> %s_%s [labeldistance=\"10\", labelangle=\"0\", " \ 893 % (spc, ename, pname, peer_ename, peer_pname) 894 s += "label=\"" + " "*50 + "\", " 895 if self.direction() == "GST_PAD_SRC": 896 media_src = caps.dot() 897 media_dst = peer_caps.dot() 898 else: 899 media_src = peer_caps.dot() 900 media_dst = caps.dot() 901 s += "taillabel=\"%s\", headlabel=\"%s\"]\n" % \ 902 (media_src, media_dst) 903 else: 904 s = "%s%s_%s -> %s_%s\n" % \ 905 (spc, ename, pname, peer_ename, peer_pname) 906 return s 907 908 909class GdbGstElement(GdbGstObject): 910 def __init__(self, val): 911 gdb_type = gdb.lookup_type("GstElement").pointer() 912 super(GdbGstElement, self).__init__(gdb_type, val) 913 self.is_bin = gst_is_bin(self.val) 914 915 def __eq__(self, other): 916 return self.val == other.val 917 918 def children(self): 919 if not self.is_bin: 920 return 921 b = self.val.cast(gdb.lookup_type("GstBin").pointer()) 922 link = b["children"] 923 while link != 0: 924 yield GdbGstElement(link["data"]) 925 link = link["next"] 926 927 def has_pads(self, pad_group="pads"): 928 return self.val[pad_group] != 0 929 930 def pads(self, pad_group="pads"): 931 link = self.val[pad_group] 932 while link != 0: 933 yield GdbGstPad(link["data"]) 934 link = link["next"] 935 936 def _state_dot(self): 937 icons = "~0-=>" 938 current = int(self.val["current_state"]) 939 pending = int(self.val["pending_state"]) 940 if pending == 0: 941 # GST_ELEMENT_FLAG_LOCKED_STATE == 16 942 locked = (int(self.val["object"]["flags"]) & 16) != 0 943 return "\\n[%c]%s" % (icons[current], "(locked)" if locked else "") 944 return "\\n[%c] -> [%c]" % (icons[current], icons[pending]) 945 946 @save_memory_access_print("Element(<inaccessible memory>)") 947 def print(self, indent): 948 _gdb_write(indent, "%s(%s) {" % 949 (g_type_name_from_instance(self.val), self.name())) 950 for p in self.pads(): 951 p.print(indent+2) 952 953 first = True 954 for child in self.children(): 955 if first: 956 _gdb_write(indent+2, "children:") 957 first = False 958 _gdb_write(indent+3, child.name()) 959 960 current_state = self.val["current_state"] 961 s = "state: %s" % element_state_to_name(current_state) 962 for var in ("pending", "target"): 963 state = self.val[var + "_state"] 964 if state > 0 and state != current_state: 965 s += ", %s: %s" % (var, element_state_to_name(state)) 966 _gdb_write(indent+2, s) 967 968 _gdb_write(indent+2, "base_time: %s" % 969 format_time_value(self.val["base_time"])) 970 _gdb_write(indent+2, "start_time: %s" % 971 format_time_value(self.val["start_time"])) 972 973 _gdb_write(indent, "}") 974 975 @save_memory_access_print("<inaccessible memory>") 976 def print_tree(self, indent): 977 _gdb_write(indent, "%s(%s)" % (self.name(), self.val)) 978 for child in self.children(): 979 child.print_tree(indent+1) 980 981 def _dot(self, indent=0): 982 spc = " " * indent 983 984 s = "%ssubgraph cluster_%s {\n" % (spc, self.dot_name()) 985 s += "%s fontname=\"Bitstream Vera Sans\";\n" % spc 986 s += "%s fontsize=\"8\";\n" % spc 987 s += "%s style=\"filled,rounded\";\n" % spc 988 s += "%s color=black;\n" % spc 989 s += "%s label=\"%s\\n%s%s%s\";\n" % \ 990 (spc, g_type_name_from_instance(self.val), self.name(), 991 self._state_dot(), "") 992 993 sink_name = None 994 if self.has_pads("sinkpads"): 995 (ss, sink_name) = self._dot_pads(indent+1, "sinkpads", 996 self.dot_name() + "_sink") 997 s += ss 998 src_name = None 999 if self.has_pads("srcpads"): 1000 (ss, src_name) = self._dot_pads(indent+1, "srcpads", 1001 self.dot_name() + "_src") 1002 s += ss 1003 if sink_name and src_name: 1004 name = self.dot_name() 1005 s += "%s %s_%s -> %s_%s [style=\"invis\"];\n" % \ 1006 (spc, name, sink_name, name, src_name) 1007 1008 if gst_is_bin(self.val): 1009 s += "%s fillcolor=\"#ffffff\";\n" % spc 1010 s += self.dot(indent+1) 1011 else: 1012 if src_name and not sink_name: 1013 s += "%s fillcolor=\"#ffaaaa\";\n" % spc 1014 elif not src_name and sink_name: 1015 s += "%s fillcolor=\"#aaaaff\";\n" % spc 1016 elif src_name and sink_name: 1017 s += "%s fillcolor=\"#aaffaa\";\n" % spc 1018 else: 1019 s += "%s fillcolor=\"#ffffff\";\n" % spc 1020 s += "%s}\n\n" % spc 1021 1022 for p in self.pads(): 1023 if not p.is_linked(): 1024 continue 1025 if p.direction() == "GST_PAD_SRC": 1026 s += p.link_dot(indent, self) 1027 else: 1028 pp = p.peer() 1029 if not g_inherits_type(pp.val, "GstGhostPad") and \ 1030 g_inherits_type(pp.val, "GstProxyPad"): 1031 s += pp.link_dot(indent, None) 1032 return s 1033 1034 def _dot_pads(self, indent, pad_group, cluster_name): 1035 spc = " " * indent 1036 s = "%ssubgraph cluster_%s {\n" % (spc, cluster_name) 1037 s += "%s label=\"\";\n" % spc 1038 s += "%s style=\"invis\";\n" % spc 1039 name = None 1040 for p in self.pads(pad_group): 1041 s += p.dot(indent) 1042 if not name: 1043 name = p.dot_name() 1044 s += "%s}\n\n" % spc 1045 return(s, name) 1046 1047 def dot(self, indent): 1048 s = "" 1049 for child in self.children(): 1050 try: 1051 s += child._dot(indent) 1052 except gdb.MemoryError: 1053 gdb.write("warning: inaccessible memory in element 0x%x\n" % 1054 long(child.val)) 1055 return s 1056 1057 def pipeline_dot(self): 1058 t = g_type_name_from_instance(self.val) 1059 1060 s = "digraph pipeline {\n" 1061 s += " rankdir=LR;\n" 1062 s += " fontname=\"sans\";\n" 1063 s += " fontsize=\"10\";\n" 1064 s += " labelloc=t;\n" 1065 s += " nodesep=.1;\n" 1066 s += " ranksep=.2;\n" 1067 s += " label=\"<%s>\\n%s%s%s\";\n" % (t, self.name(), "", "") 1068 s += " node [style=\"filled,rounded\", shape=box, fontsize=\"9\", " \ 1069 "fontname=\"sans\", margin=\"0.0,0.0\"];\n" 1070 s += " edge [labelfontsize=\"6\", fontsize=\"9\", " \ 1071 "fontname=\"monospace\"];\n" 1072 s += " \n" 1073 s += " legend [\n" 1074 s += " pos=\"0,0!\",\n" 1075 s += " margin=\"0.05,0.05\",\n" 1076 s += " style=\"filled\",\n" 1077 s += " label=\"Legend\\lElement-States: [~] void-pending, " \ 1078 "[0] null, [-] ready, [=] paused, [>] playing\\l" \ 1079 "Pad-Activation: [-] none, [>] push, [<] pull\\l" \ 1080 "Pad-Flags: [b]locked, [f]lushing, [b]locking, [E]OS; " \ 1081 "upper-case is set\\lPad-Task: [T] has started task, " \ 1082 "[t] has paused task\\l\",\n" 1083 s += " ];" 1084 s += "\n" 1085 1086 s += self.dot(1) 1087 1088 s += "}\n" 1089 1090 return s 1091 1092 1093class GstDot(gdb.Command): 1094 """\ 1095Create a pipeline dot file as close as possible to the output of 1096GST_DEBUG_BIN_TO_DOT_FILE. This command will find the top-level parent 1097for the given gstreamer object and create the dot for that element. 1098 1099Usage: gst-dot <gst-object> <file-name>""" 1100 def __init__(self): 1101 super(GstDot, self).__init__("gst-dot", gdb.COMMAND_DATA) 1102 1103 def invoke(self, arg, from_tty): 1104 self.dont_repeat() 1105 args = gdb.string_to_argv(arg) 1106 if len(args) != 2: 1107 raise Exception("Usage: gst-dot <gst-object> <file>") 1108 1109 value = gdb.parse_and_eval(args[0]) 1110 if not value: 1111 raise Exception("'%s' is not a valid object" % args[0]) 1112 1113 value = gst_object_from_value(value) 1114 value = gst_object_pipeline(value) 1115 1116 dot = GdbGstElement(value).pipeline_dot() 1117 file = open(args[1], "w") 1118 file.write(dot) 1119 file.close() 1120 1121 def complete(self, text, word): 1122 cmd = gdb.string_to_argv(text) 1123 if len(cmd) == 0 or(len(cmd) == 1 and len(word) > 0): 1124 return gdb.COMPLETE_SYMBOL 1125 return gdb.COMPLETE_FILENAME 1126 1127 1128class GstPrint(gdb.Command): 1129 """\ 1130Print high-level information for GStreamer objects 1131 1132Usage gst-print <gstreamer-object>""" 1133 def __init__(self): 1134 super(GstPrint, self).__init__("gst-print", gdb.COMMAND_DATA, 1135 gdb.COMPLETE_SYMBOL) 1136 1137 def invoke(self, arg, from_tty): 1138 value = gdb.parse_and_eval(arg) 1139 if not value: 1140 raise Exception("'%s' is not a valid object" % arg) 1141 1142 if value.type.code != gdb.TYPE_CODE_PTR: 1143 value = value.address 1144 1145 if g_inherits_type(value, "GstElement"): 1146 obj = GdbGstElement(value) 1147 elif g_inherits_type(value, "GstPad"): 1148 obj = GdbGstPad(value) 1149 elif g_inherits_type(value, "GstCaps"): 1150 obj = GdbGstCaps(value) 1151 elif g_inherits_type(value, "GstEvent"): 1152 obj = GdbGstEvent(value) 1153 elif g_inherits_type(value, "GstQuery"): 1154 obj = GdbGstQuery(value) 1155 elif g_inherits_type(value, "GstBuffer"): 1156 obj = GdbGstBuffer(value) 1157 elif is_gst_type(value, "GstStructure"): 1158 obj = GdbGstStructure(value) 1159 else: 1160 raise Exception("'%s' has an unknown type (%s)" % (arg, value)) 1161 1162 obj.print(0) 1163 1164 1165class GstPipelineTree(gdb.Command): 1166 """\ 1167Usage: gst-pipeline-tree <gst-object>""" 1168 def __init__(self): 1169 super(GstPipelineTree, self).__init__("gst-pipeline-tree", 1170 gdb.COMPLETE_SYMBOL) 1171 1172 def invoke(self, arg, from_tty): 1173 self.dont_repeat() 1174 args = gdb.string_to_argv(arg) 1175 if len(args) != 1: 1176 raise Exception("Usage: gst-pipeline-tree <gst-object>") 1177 1178 value = gdb.parse_and_eval(args[0]) 1179 if not value: 1180 raise Exception("'%s' is not a valid object" % args[0]) 1181 1182 value = gst_object_from_value(value) 1183 value = gst_object_pipeline(value) 1184 GdbGstElement(value).print_tree(0) 1185 1186 1187GstDot() 1188GstPrint() 1189GstPipelineTree() 1190 1191 1192class GstPipeline(gdb.Function): 1193 """\ 1194Find the top-level pipeline for the given element""" 1195 1196 def __init__(self): 1197 super(GstPipeline, self).__init__("gst_pipeline") 1198 1199 def invoke(self, arg): 1200 value = gst_object_from_value(arg) 1201 return gst_object_pipeline(value) 1202 1203 1204class GstBinGet(gdb.Function): 1205 """\ 1206Find a child element with the given name""" 1207 1208 def __init__(self): 1209 super(GstBinGet, self).__init__("gst_bin_get") 1210 1211 def find(self, obj, name, recurse): 1212 for child in obj.children(): 1213 if child.name() == name: 1214 return child.val 1215 if recurse: 1216 result = self.find(child, name, recurse) 1217 if result is not None: 1218 return result 1219 1220 def invoke(self, element, arg): 1221 value = gst_object_from_value(element) 1222 if not g_inherits_type(value, "GstElement"): 1223 raise Exception("'%s' is not a GstElement" % 1224 str(value.address)) 1225 1226 try: 1227 name = arg.string() 1228 except gdb.error: 1229 raise Exception("Usage: $gst_bin_get(<gst-object>, \"<name>\")") 1230 1231 obj = GdbGstElement(value) 1232 child = self.find(obj, name, False) 1233 if child is None: 1234 child = self.find(obj, name, True) 1235 if child is None: 1236 raise Exception("No child named '%s' found." % name) 1237 return child 1238 1239 1240class GstElementPad(gdb.Function): 1241 """\ 1242Get the pad with the given name""" 1243 1244 def __init__(self): 1245 super(GstElementPad, self).__init__("gst_element_pad") 1246 1247 def invoke(self, element, arg): 1248 value = gst_object_from_value(element) 1249 if not g_inherits_type(value, "GstElement"): 1250 raise Exception("'%s' is not a GstElement" % 1251 str(value.address)) 1252 1253 try: 1254 name = arg.string() 1255 except gdb.error: 1256 raise Exception("Usage: $gst_element_pad(<gst-object>, \"<pad-name>\")") 1257 1258 obj = GdbGstElement(value) 1259 for pad in obj.pads(): 1260 if pad.name() == name: 1261 return pad.val 1262 1263 raise Exception("No pad named '%s' found." % name) 1264 1265 1266GstPipeline() 1267GstBinGet() 1268GstElementPad() 1269 1270 1271def register(obj): 1272 if obj is None: 1273 obj = gdb 1274 1275 # Make sure this is always used before the glib lookup function. 1276 # Otherwise the gobject pretty printer is used for GstObjects 1277 obj.pretty_printers.insert(0, gst_pretty_printer_lookup) 1278