Chromium-WebRTC

WebRTC is an open source project providing Real-Time Communication between multiple devices. It was open sourced by google, and the specification is available in IETF.

One of the interesting features I’ve been using in WebRTC is screen-sharing. So we wanted to tinker with it and understand the architecture and flow on how it manages to avail this feature cross OS platform.

The below mentioned method may not be the easiest and efficient way to get to know the underlying flow of WebRTC. But for me personally ability to debug is always the first step towards understanding any new code base.

So I hope you’ll find this useful :)

Install Chromium Development Mode

The following instructions can be referred for building chromium in Linux(Please do keep in mind that it’s a 20GB Download!):

Chromium build instructions - Linux

Once you’re setup the last set of commands will build the source code:

autoninja -C out/Default chrome

The first build may take upto 2 hours, which is quite a lot of time. The documentation has a few steps to reduce the amount of time it takes to compile the code, one of the interesting option is to use Goma , which is an open source tool from Google which supports distributed compilation! Haven’t tried that out yet

Now you can run the Chromium version built from the source code using:

out/Default/chrome

VSCode Setup

  1. The following extensions will help in using VSCode along with Chromium source code:
        a. Toggle Header/Source
        b. you-complete-me
        c. Rewrap
        d. Highlighter Line

  2. Setup Chromium workspace
        a. From https://cs.chromium.org/chromium/src/tools/vscode/settings.json5 copy as .vscode/settings.json

        {
         // Default tab size of 2, for consistency with internal codebase.
         "editor.tabSize": 2,
         "chrome.outputDir": "${workspaceRoot}/out/Default",
         // Do not figure out tab size from opening a file.
         "editor.detectIndentation": false,
         // Add a line at 80 characters.
         "editor.rulers": [80],
         // Forces LF instead of "auto" which uses CRLF on Windows.
         "files.eol": "\n",
         // Trim tailing whitespace on save.
         "files.trimTrailingWhitespace": true,
         // Insert trimmed final new line.
         "files.insertFinalNewline": true,
         "files.trimFinalNewlines": true,
            
         "files.associations": {
         "*.grd": "xml",
         "*.gni": "javascript",
         "*.gn": "javascript",
         "ios": "cpp",
         "*.idl": "cpp"
         },
            
         "files.exclude": {
         // Ignore build output folders.
         "out*/**": false
         },
            
         "files.watcherExclude": {
         // Don't watch out*/ and third_party/ for changes to fix an issue
         // where vscode doesn't notice that files have changed.
         // https://github.com/Microsoft/vscode/issues/3998
         // There is currently another issue that requires a leading **/ for
         // watcherExlude. Beware that this pattern might affect other out* folders
         // like src/cc/output/.
         "**/out*/**": true,
         "**/third_party/**": true
         },
            
         // Wider author column for annotator extension.
         // https://marketplace.visualstudio.com/items?itemName=ryu1kn.annotator
         "annotator.annotationColumnWidth": "24em",
            
         // C++ clang format settings. |workspaceFolder| is assumed to be chromium/src.
         "C_Cpp.clang_format_path": "${workspaceFolder}/third_party/depot_tools/clang-format",
         "C_Cpp.clang_format_sortIncludes": true,
         "editor.formatOnSave": true,
            
         // YouCompleteMe settings. |workspaceFolder| is assumed to be chromium/src.
         "ycmd.path": "${HOME}/.ycmd", // Please replace this path!!
         "ycmd.global_extra_config": "${workspaceFolder}/tools/vim/chromium.ycm_extra_conf.py",
         "ycmd.confirm_extra_conf": false
         }
    

        b. https://cs.chromium.org/chromium/src/tools/vscode/tasks.json5 copy as tasks.json

     {
     // Note!
     // You can set the value used for ${config:chrome.outputDir} in your settings.json
     // file with a line like:
     // "chrome.outputDir": "/path/to/chromium/src/out/Debug",
     "version": "2.0.0",
     "runner": "terminal",
     // The default problem matcher matches build output, which is useful for most tasks.
     "problemMatcher": [
     {
         "owner": "cpp",
         "fileLocation": ["relative", "${config:chrome.outputDir}"],
         "pattern": {
         "regexp": "^(gen/.*):(\\d+):(\\d+):\\s+(warning|\\w*\\s?error):\\s+(.*)$",
         "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5
     }
     },
     {
         "owner": "cpp",
         "fileLocation": ["relative", "${workspaceRoot}"],
         "pattern": {
             "regexp": "^../../(.*):(\\d+):(\\d+):\\s+(warning|\\w*\\s?error):\\s+(.*)$",
             "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5
             }
     },
     {
         "owner": "cpp",
         "fileLocation": ["relative", "${config:chrome.outputDir}"],
         "pattern": {
             "regexp": "^(gen/.*?):(.*):\\s+(warning|\\w*\\s?error):\\s+(.*)$",
             "file": 1, "severity": 3, "message": 4
             }
     },
     {
         "owner": "cpp",
         "fileLocation": ["relative", "${workspaceRoot}"],
         "pattern": {
             "regexp": "^../../(.*?):(.*):\\s+(warning|\\w*\\s?error):\\s+(.*)$",
             "file": 1, "severity": 3, "message": 4
             }
     }
     ],
        
     "inputs": [
         {
             // See 'Set Chrome Output Directory'.
             "type": "pickString",
             "id": "chromeOutputDir",
             "description": "Chrome output directory:",
             // Configure this to point to all the output directories you use.
             "options": [
             "${workspaceRoot}/out/pc",
             "${workspaceRoot}/out/Debug",
             "${workspaceRoot}/Debug_x86"
             ]
         }
     ],
     "tasks": [
         {
           "label": "0-set_chrome_output_directory",
           "command": "rm -f ${workspaceFolder}/out/current_link; ln -s ${input:chromeOutputDir} ${workspaceFolder}/out/current_link",
           "type": "shell",
           // The default problem matcher doesn't make sense here, so remove it.
           "problemMatcher": [],
           "options": {
             "cwd": "${workspaceFolder}"
           }
         },
         // Some general-purpose build and test tasks. These all inherit the
         // problemMatcher at the top of the file.
         {
           "label": "1-build_chrome",
           "type": "shell",
           "command": "autoninja -C ${config:chrome.outputDir} chrome",
           "group": "test"
         },
         {
           "label": "2-build_all",
           "type": "shell",
           "command": "autoninja -C ${config:chrome.outputDir}"
         },
         {
           "label": "3-test_current_file",
           "type": "shell",
           "command": "${workspaceFolder}/tools/autotest.py -C ${config:chrome.outputDir} --run_all ${file}"
         },
         {
           "label": "4-test_current_directory",
           "type": "shell",
           "command": "${workspaceFolder}/tools/autotest.py -C ${config:chrome.outputDir} --run_all ${fileDirname}"
         },
         {
           "label": "5-build_current_file",
           "type": "shell",
           "command": "compile_single_file --build-dir=${config:chrome.outputDir} --file-path=${file}"
         },
         // Some more specific build tasks, which hard-code the output directory.
         {
           "label": "6-build_chrome_debug",
           "type": "shell",
           "command": "autoninja -C ${config:chrome.outputDir} chrome"
         },
         {
           "label": "7-build_chrome_release",
           "type": "shell",
           "command": "autoninja -C ${config:chrome.outputDir} chrome"
         },
         {
           "label": "8-build_test_debug",
           "type": "shell",
           "command": "autoninja -C ${config:chrome.outputDir} unit_tests components_unittests browser_tests"
         }
     ]
     }
    

        c. https://cs.chromium.org/chromium/src/tools/vscode/launch.json5 copy as .vscode/launch.json

     {
     "version": "0.2.0",
     "configurations": [
         {
             "name": "Chrome Debug",
             "type": "cppdbg", // "cppdbg" for GDB/LLDB, "cppvsdbg" for Windows Visual Studio debugger
             "request": "launch",
             "targetArchitecture": "x64",
             "program": "${workspaceRoot}/out/Default/chrome",
             "args": ["--no-xshm"], // Optional command line args
             "preLaunchTask": "6-build_chrome_debug",
             "stopAtEntry": false,
             "cwd": "${workspaceRoot}/out/Default/",
             "environment": [
             {"name":"workspaceRoot", "value":"${HOME}/datadir/electron/chromium/src"}
             ],
             "externalConsole": true,
             "setupCommands": [
             {
             "description": "Enable pretty printing for gdb",
             "text": "-enable-pretty-printing"
             },
             {
             "description": "Load Chromium gdb configuration",
             "text": "-interpreter-exec console \"source -v ${workspaceRoot}/tools/gdb/gdbinit\""
             },
             {
             "description": "Load Blink gdb configuration",
             "text": "-interpreter-exec console \"python import sys; sys.path.insert(0, '${workspaceRoot}/third_party/blink/tools/gdb'); import blink\""
             }
             ]
         },
         {
             "name": "Chrome Release",
             "type": "cppdbg", // "cppdbg" for GDB/LLDB, "cppvsdbg" for Windows Visual Studio debugger
             "request": "launch",
             "targetArchitecture": "x64",
             "program": "${workspaceRoot}/out/Release/chrome",
             "args": [], // Optional command line args
             "preLaunchTask": "7-build_chrome_release",
             "stopAtEntry": false,
             "cwd": "${workspaceRoot}/out/Release/",
             "environment": [],
             "externalConsole": true
         },
         {
             "name": "Custom Test Debug",
             "type": "cppdbg", // "cppdbg" for GDB/LLDB, "cppvsdbg" for Windows Visual Studio debugger
             "request": "launch",
             "targetArchitecture": "x64",
             "program": "${workspaceRoot}/out/Default/unit_tests",
             "args": [
             "--gtest_filter=*",
             "--single-process-tests",
             "--ui-test-action-max-timeout=1000000",
             "--test-launcher-timeout=1000000"
             ],
             "preLaunchTask": "8-build_test_debug",
             "stopAtEntry": false,
             "cwd": "${workspaceRoot}/out/Default/",
             "environment": [],
             "externalConsole": true
         },
         {
         "name": "Attach Debug",
         "type": "cppdbg", // "cppdbg" for GDB/LLDB, "cppvsdbg" for Windows Visual Studio debugger
         "request": "launch",
         "targetArchitecture": "x64",
         "program": "${workspaceRoot}/out/Default/chrome",
         "args": [
         "--remote-debugging-port=2224"
         ],
         "stopAtEntry": false,
         "cwd": "${workspaceRoot}/out/Default/",
         "environment": [],
         "externalConsole": false
         }
     ]
     }
    

Debug with VSCode

  • Editor with all the plugins and settings installed Editor
  • You can use the option Run And Debug > Chrome Debug to open a new Chromium window with the debugger attached Debug
  • It might take a little while to open, once the window is open you should be able to see the following
    • Call stack + Threads VSCode Debug
    • Chromium Window with debugger attached Debug

With this you should be able to debug the chromium application

Debugging WebRTC Library

Since I’m using Linux, the code for capturing screenshot uses X.org Library, which is located in chromium source code at third_party/webrtc/modules/desktop_capture/linux WebRTC

The breakpoint for analyzing a single screen capture can be kept at function: XServerPixelBuffer::CaptureRect
from third_party/webrtc/modules/desktop_capture/linux/x_server_pixel_buffer.cc:335

Once the breakpoint is set the following link can be opened in the Chromium window to trigger the breakpoint - Link or you can use the Chromium Console with the following code:

var displayMediaStreamConstraints = {
    "aspectRatio": 1.7777777777777777,
	"deviceId": "screen:389:0",
	"frameRate": 15,
	"height": 1080,
	"resizeMode": "crop-and-scale",
	"width": 1920,
	"cursor": "always",
	"displaySurface": "monitor",
	"logicalSurface": true
};

if (navigator.mediaDevices.getDisplayMedia) {
    navigator.mediaDevices.getDisplayMedia(displayMediaStreamConstraints).then(success).catch(error);
} else {
    navigator.getDisplayMedia(displayMediaStreamConstraints).then(success).catch(error);
}

The breakpoint would be triggered with the following screen: WebRTC

With this setup we’d be able to effectively debug, identify and modify code from chromium with VSCode. Those of you who remember previously chromium was not supporting sharing a single screen from a multi screen setup before last year(2020) that issue was resolved with the following fixes. - Link, Fix Link.

That’s it folks, Happy Debugging!!

References

  1. Debug Chromium with VSCode - gyuyoung
  2. Chromium Linux Build Instructions