From cf7cc066ed86f36f46b2726e97c6c745eed21a51 Mon Sep 17 00:00:00 2001 From: Zhao Junwang Date: Thu, 1 Aug 2024 13:49:53 +0000 Subject: [PATCH v1] official devcontainer config Signed-off-by: Zhao Junwang --- .devcontainer/.gdbinit | 32 +++ .devcontainer/.psqlrc | 7 + .devcontainer/Dockerfile | 95 +++++++ .devcontainer/devcontainer.json | 46 ++++ .devcontainer/gdbpg.py | 386 +++++++++++++++++++++++++++++ .devcontainer/launch.json | 20 ++ .devcontainer/postCreateCommand.sh | 86 +++++++ .devcontainer/tasks.json | 219 ++++++++++++++++ .gitignore | 3 + 9 files changed, 894 insertions(+) create mode 100644 .devcontainer/.gdbinit create mode 100644 .devcontainer/.psqlrc create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/gdbpg.py create mode 100644 .devcontainer/launch.json create mode 100644 .devcontainer/postCreateCommand.sh create mode 100644 .devcontainer/tasks.json diff --git a/.devcontainer/.gdbinit b/.devcontainer/.gdbinit new file mode 100644 index 0000000000..00ac2119d9 --- /dev/null +++ b/.devcontainer/.gdbinit @@ -0,0 +1,32 @@ +# gdbpg.py contains scripts to nicely print the postgres datastructures +# while in a gdb session. Since the vscode debugger is based on gdb this +# actually also works when debugging with vscode. +source /root/gdbpg.py + +# when debugging postgres it is convenient to _always_ have a breakpoint +# trigger when an error is logged. Because .gdbinit is sourced before gdb +# is fully attached and has the sources loaded. To make sure the breakpoint +# is added when the library is loaded we temporary set the breakpoint pending +# to on. After we have added out breakpoint we revert back to the default +# configuration for breakpoint pending. +# The breakpoint is hard to read, but at entry of the function we don't have +# the level loaded in elevel. Instead we hardcode the location where the +# level of the current error is stored. Also gdb doesn't understand the +# ERROR symbol so we hardcode this to the value of ERROR. It is very unlikely +# this value will ever change in postgres, but if it does we might need to +# find a way to conditionally load the correct breakpoint. +set breakpoint pending on +break elog.c:errfinish if errordata[errordata_stack_depth].elevel == 21 +set breakpoint pending auto + +echo \n +echo ----------------------------------------------------------------------------------\n +echo when attaching to a postgres backend a breakpoint will be set on elog.c:errfinish \n +echo it will only break on errors being raised in postgres \n +echo \n +echo to disable this breakpoint from vscode run `-exec disable 1` in the debug console \n +echo this assumes it's the first breakpoint loaded as it is loaded from .gdbinit \n +echo this can be verified with `-exec info break`, enabling can be done with \n +echo `-exec enable 1` \n +echo ----------------------------------------------------------------------------------\n +echo \n diff --git a/.devcontainer/.psqlrc b/.devcontainer/.psqlrc new file mode 100644 index 0000000000..7642a97149 --- /dev/null +++ b/.devcontainer/.psqlrc @@ -0,0 +1,7 @@ +\timing on +\pset linestyle unicode +\pset border 2 +\setenv PAGER 'pspg --no-mouse -bX --no-commandbar --no-topbar' +\set HISTSIZE 100000 +\set PROMPT1 '\n%[%033[1m%]%M %n@%/:%>-%p%R%[%033[0m%]%# ' +\set PROMPT2 ' ' diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..3a09855faf --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,95 @@ +FROM debian:bookworm AS base + +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# install build tools(alphabetic order) +RUN apt update && apt install -y \ + bash-completion \ + bison \ + bzip2 \ + ccache \ + cpanminus \ + cron \ + curl \ + flex \ + fswatch \ + gcc g++ \ + gdb \ + git \ + htop \ + iproute2 \ + libcurl4-gnutls-dev \ + libgdal-dev \ + libgeos-dev \ + libicu-dev \ + libkrb5-dev \ + liblz4-dev \ + libpam0g-dev \ + libperl-dev \ + libproj-dev \ + libprotobuf-c-dev \ + libreadline-dev \ + libselinux1-dev \ + libssl-dev \ + libxml2-dev \ + libxslt-dev \ + libzstd-dev \ + linux-perf \ + locales \ + lsb-release \ + lsof \ + make \ + man \ + meson \ + neovim \ + ninja-build \ + perl \ + pkg-config \ + procps \ + protobuf-c-compiler \ + pspg \ + python3 python3-pip \ + strace \ + sudo \ + tmux \ + tree \ + unzip \ + uuid-dev \ + valgrind \ + wget \ + zlib1g-dev \ + && apt remove libpq-dev -y \ + && apt autoremove -y \ + && apt clean + +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +RUN cpanm install IPC::Run +RUN cpanm https://cpan.metacpan.org/authors/id/S/SH/SHANCOCK/Perl-Tidy-20230309.tar.gz + +# Since gdb will run in the context of the root user when debugging, we need add +# both .gdbinit and gdbpg.py script into root home +# gdbpg.py is adapted from https://github.com/tvondra/gdbpg +COPY --chown=root:root .gdbinit /root/ +COPY --chown=root:root gdbpg.py /root/ + +# add the postgres user to sudoers and allow all sudoers to login without a password prompt +RUN useradd -ms /bin/bash postgres \ + && usermod -aG sudo postgres \ + && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +WORKDIR /home/postgres +USER postgres + +COPY --chown=postgres:postgres .psqlrc . + +RUN sudo mkdir -p /opt/freedom \ + && sudo chown -R postgres:postgres /opt/freedom + +ENV PATH="/home/postgres/pgsql/bin:$PATH" +ENV PGPORT=5432 +EXPOSE 5432 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..f92d116bcf --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,46 @@ +{ + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": [ + "--ulimit=core=-1", + "--cap-add=SYS_ADMIN", + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined", + "--privileged" + ], + "mounts": [ + // You have to make sure source directory is avaliable on your host file system + "source=${localEnv:HOME}/freedom,target=/opt/freedom,type=bind,consistency=cached" + ], + "workspaceMount": "source=${localWorkspaceFolder},target=/home/postgres/postgres,type=bind,consistency=cached", + "workspaceFolder": "/home/postgres/postgres", + "forwardPorts": [ + 5432 + ], + "customizations": { + "vscode": { + "extensions": [ + "eamodio.gitlens", + "github.vscode-github-actions", + "GitHub.copilot", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.hexeditor", + "ms-vsliveshare.vsliveshare", + "rioj7.command-variable", + "74th.scrollkey" + ], + "settings": { + "files.exclude": { + "**/*.o": true, + "**/.deps/": true + }, + "C_Cpp.default.cStandard": "c99", + "C_Cpp.default.cppStandard": "c++17", + "C_Cpp.default.browse.databaseFilename": "${workspaceFolder}/.vscode/.browse.c_cpp.db" + } + } + }, + "postCreateCommand": "bash ./.devcontainer/postCreateCommand.sh" +} diff --git a/.devcontainer/gdbpg.py b/.devcontainer/gdbpg.py new file mode 100644 index 0000000000..776570b2ce --- /dev/null +++ b/.devcontainer/gdbpg.py @@ -0,0 +1,386 @@ +import gdb + +def format_plan_tree(tree, indent=0): + 'formats a plan (sub)tree, with custom indentation' + + # if the pointer is NULL, just return (null) string + if (str(tree) == '0x0'): + return '-> (NULL)' + + # format all the important fields (similarly to EXPLAIN) + retval = ''' +-> %(type)s (cost=%(startup).3f...%(total).3f rows=%(rows)s width=%(width)s) +\ttarget list: +%(target)s +\t%(left)s +\t%(right)s''' % { + 'type' : format_type(tree['type']), # type of the Node + 'startup' : float(tree['startup_cost']), # startup cost + 'total' : float(tree['total_cost']), # total cost + 'rows' : str(tree['plan_rows']), # number of rows + 'width' : str(tree['plan_width']), # tuple width (no header) + + # format target list + 'target' : format_node_list(tree['targetlist'], 2, True), + + # left subtree + 'left' : format_plan_tree(tree['lefttree'], 0), + + # right subtree + 'right' : format_plan_tree(tree['righttree'], 0) + } + + return add_indent(retval, indent+1) + + +def format_type(t, indent=0): + 'strip the leading T_ from the node type tag' + + t = str(t) + + if t.startswith('T_'): + t = t[2:] + + return add_indent(t, indent) + + +def format_int_list(lst, indent=0): + 'format list containing integer values directly (not warapped in Node)' + + # handle NULL pointer (for List we return NIL + if (str(lst) == '0x0'): + return '(NIL)' + + # we'll collect the formatted items into a Python list + tlist = [] + item = lst['head'] + + # walk the list until we reach the last item + while str(item) != '0x0': + + # get item from the list and just grab 'int_value as int' + tlist.append(int(item['data']['int_value'])) + + # next item + item = item['next'] + + return add_indent(str(tlist), indent) + + +def format_oid_list(lst, indent=0): + 'format list containing Oid values directly (not warapped in Node)' + + # handle NULL pointer (for List we return NIL) + if (str(lst) == '0x0'): + return '(NIL)' + + # we'll collect the formatted items into a Python list + tlist = [] + item = lst['head'] + + # walk the list until we reach the last item + while str(item) != '0x0': + + # get item from the list and just grab 'oid_value as int' + tlist.append(int(item['data']['oid_value'])) + + # next item + item = item['next'] + + return add_indent(str(tlist), indent) + + +def format_node_list(lst, indent=0, newline=False): + 'format list containing Node values' + + # handle NULL pointer (for List we return NIL) + if (str(lst) == '0x0'): + return '(NIL)' + + # we'll collect the formatted items into a Python list + tlist = [] + item = lst['head'] + + # walk the list until we reach the last item + while str(item) != '0x0': + + # we assume the list contains Node instances, so grab a reference + # and cast it to (Node*) + node = cast(item['data']['ptr_value'], 'Node') + + # append the formatted Node to the result list + tlist.append(format_node(node)) + + # next item + item = item['next'] + + retval = str(tlist) + if newline: + retval = "\n".join([str(t) for t in tlist]) + + return add_indent(retval, indent) + + +def format_char(value): + '''convert the 'value' into a single-character string (ugly, maybe there's a better way''' + + str_val = str(value.cast(gdb.lookup_type('char'))) + + # remove the quotes (start/end) + return str_val.split(' ')[1][1:-1] + + +def format_relids(relids): + return '(not implemented)' + + +def format_node_array(array, start_idx, length, indent=0): + + items = [] + for i in range(start_idx,start_idx + length - 1): + items.append(str(i) + " => " + format_node(array[i])) + + return add_indent(("\n".join(items)), indent) + + +def format_node(node, indent=0): + 'format a single Node instance (only selected Node types supported)' + + if str(node) == '0x0': + return add_indent('(NULL)', indent) + + retval = ''; + type_str = str(node['type']) + + if is_a(node, 'TargetEntry'): + + # we assume the list contains Node instances (probably safe for Plan fields) + node = cast(node, 'TargetEntry') + + name_ptr = node['resname'].cast(gdb.lookup_type('char').pointer()) + name = "(NULL)" + if str(name_ptr) != '0x0': + name = '"' + (name_ptr.string()) + '"' + + retval = 'TargetEntry (resno=%(resno)s resname=%(name)s origtbl=%(tbl)s origcol=%(col)s junk=%(junk)s expr=[%(expr)s])' % { + 'resno' : node['resno'], + 'name' : name, + 'tbl' : node['resorigtbl'], + 'col' : node['resorigcol'], + 'junk' : (int(node['resjunk']) == 1), + 'expr' : format_node(node['expr']) + } + + elif is_a(node, 'Var'): + + # we assume the list contains Node instances (probably safe for Plan fields) + node = cast(node, 'Var') + + retval = 'Var (varno=%(no)s varattno=%(attno)s levelsup=%(levelsup)s)' % { + 'no' : node['varno'], + 'attno' : node['varattno'], + 'levelsup' : node['varlevelsup'] + } + + elif is_a(node, 'RangeTblRef'): + + node = cast(node, 'RangeTblRef') + + retval = 'RangeTblRef (rtindex=%d)' % (int(node['rtindex']),) + + elif is_a(node, 'RelOptInfo'): + + node = cast(node, 'RelOptInfo') + + retval = 'RelOptInfo (kind=%(kind)s relids=%(relids)s rtekind=%(rtekind)s relid=%(relid)s rows=%(rows)s width=%(width)s fk=%(fk)s)' % { + 'kind' : node['reloptkind'], + 'rows' : node['rows'], + 'width' : node['width'], + 'relid' : node['relid'], + 'relids' : format_relids(node['relids']), + 'rtekind' : node['rtekind'], + 'fk' : (int(node['has_fk_join']) == 1) + } + + elif is_a(node, 'RangeTblEntry'): + + node = cast(node, 'RangeTblEntry') + + retval = 'RangeTblEntry (kind=%(rtekind)s relid=%(relid)s relkind=%(relkind)s)' % { + 'relid' : node['relid'], + 'rtekind' : node['rtekind'], + 'relkind' : format_char(node['relkind']) + } + + elif is_a(node, 'PlannerInfo'): + + retval = format_planner_info(node) + + elif is_a(node, 'PlannedStmt'): + + retval = format_planned_stmt(node) + + elif is_a(node, 'List'): + + retval = format_node_list(node, 0, True) + + elif is_a(node, 'Plan'): + + retval = format_plan_tree(node) + + elif is_a(node, 'RestrictInfo'): + + node = cast(node, 'RestrictInfo') + + retval = '''RestrictInfo (pushed_down=%(push_down)s can_join=%(can_join)s delayed=%(delayed)s) +%(clause)s +%(orclause)s''' % { + 'clause' : format_node(node['clause'], 1), + 'orclause' : format_node(node['orclause'], 1), + 'push_down' : (int(node['is_pushed_down']) == 1), + 'can_join' : (int(node['can_join']) == 1), + 'delayed' : (int(node['outerjoin_delayed']) == 1) + } + + elif is_a(node, 'OpExpr'): + + node = cast(node, 'OpExpr') + + retval = format_op_expr(node) + + elif is_a(node, 'BoolExpr'): + + node = cast(node, 'BoolExpr') + + print node + + retval = format_bool_expr(node) + + else: + # default - just print the type name + retval = format_type(type_str) + + return add_indent(str(retval), indent) + + +def format_planner_info(info, indent=0): + + # Query *parse; /* the Query being planned */ + # *glob; /* global info for current planner run */ + # Index query_level; /* 1 at the outermost Query */ + # struct PlannerInfo *parent_root; /* NULL at outermost Query */ + # List *plan_params; /* list of PlannerParamItems, see below */ + + retval = '''rel: +%(rel)s +rte: +%(rte)s +''' % {'rel' : format_node_array(info['simple_rel_array'], 1, int(info['simple_rel_array_size'])), + 'rte' : format_node_array(info['simple_rte_array'], 1, int(info['simple_rel_array_size']))} + + return add_indent(retval, indent) + + +def format_planned_stmt(plan, indent=0): + + retval = ''' type: %(type)s + query ID: %(qid)s + param exec: %(nparam)s + returning: %(has_returning)s + modifying CTE: %(has_modify_cte)s + can set tag: %(can_set_tag)s + transient: %(transient)s + row security: %(row_security)s + + plan tree: %(tree)s + range table: +%(rtable)s + relation OIDs: %(relation_oids)s + result rels: %(result_rels)s + utility stmt: %(util_stmt)s + subplans: %(subplans)s''' % { + 'type' : plan['commandType'], + 'qid' : plan['queryId'], + 'nparam' : plan['nParamExec'], + 'has_returning' : (int(plan['hasReturning']) == 1), + 'has_modify_cte' : (int(plan['hasModifyingCTE']) == 1), + 'can_set_tag' : (int(plan['canSetTag']) == 1), + 'transient' : (int(plan['transientPlan']) == 1), + 'row_security' : (int(plan['hasRowSecurity']) == 1), + 'tree' : format_plan_tree(plan['planTree']), + 'rtable' : format_node_list(plan['rtable'], 1, True), + 'relation_oids' : format_oid_list(plan['relationOids']), + 'result_rels' : format_int_list(plan['resultRelations']), + 'util_stmt' : format_node(plan['utilityStmt']), + 'subplans' : format_node_list(plan['subplans'], 1, True) + } + + return add_indent(retval, indent) + +def format_op_expr(node, indent=0): + + return """OpExpr [opno=%(opno)s] +%(clauses)s""" % { 'opno' : node['opno'], + 'clauses' : format_node_list(node['args'], 1, True)} + +def format_bool_expr(node, indent=0): + + return """BoolExpr [op=%(op)s] +%(clauses)s""" % { 'op' : node['boolop'], + 'clauses' : format_node_list(node['args'], 1, True)} + +def is_a(n, t): + '''checks that the node has type 't' (just like IsA() macro)''' + + if not is_node(n): + return False + + return (str(n['type']) == ('T_' + t)) + + +def is_node(l): + '''return True if the value looks like a Node (has 'type' field)''' + + try: + x = l['type'] + return True + except: + return False + +def cast(node, type_name): + '''wrap the gdb cast to proper node type''' + + # lookup the type with name 'type_name' and cast the node to it + t = gdb.lookup_type(type_name) + return node.cast(t.pointer()) + + +def add_indent(val, indent): + + return "\n".join([(("\t"*indent) + l) for l in val.split("\n")]) + + +class PgPrintCommand(gdb.Command): + "print PostgreSQL structures" + + def __init__ (self): + super (PgPrintCommand, self).__init__ ("pgprint", + gdb.COMMAND_SUPPORT, + gdb.COMPLETE_NONE, False) + + def invoke (self, arg, from_tty): + + arg_list = gdb.string_to_argv(arg) + if len(arg_list) != 1: + print "usage: pgprint var" + return + + l = gdb.parse_and_eval(arg_list[0]) + + if not is_node(l): + print "not a node type" + + print format_node(l) + + +PgPrintCommand() diff --git a/.devcontainer/launch.json b/.devcontainer/launch.json new file mode 100644 index 0000000000..a3c0ae98fc --- /dev/null +++ b/.devcontainer/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Attach", + "type": "cppdbg", + "request": "attach", + "processId": "${command:pickProcess}", + "program": "/home/postgres/pgsql/bin/postgres", + "additionalSOLibSearchPath": "/home/postgres/pgsql/lib", + "setupCommands": [ + { + "text": "handle SIGUSR1 noprint nostop pass", + "description": "let gdb not stop when SIGUSR1 is sent to process", + "ignoreFailures": true + } + ] + } + ] +} diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh new file mode 100644 index 0000000000..ef84027fc4 --- /dev/null +++ b/.devcontainer/postCreateCommand.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +function configure_coredumps { + sudo sh -c "echo 'core.%p.sig%s.%ts' > /proc/sys/kernel/core_pattern" +} + +function configure_git { + git config --global user.email email@example.com + git config --global user.name "Your Name" + git config --global core.editor vim + + # alias + git config --global alias.br "branch -vv" + git config --global alias.ci "commit -s" + git config --global alias.co checkout + git config --global alias.cob "checkout -b" + git config --global alias.cp cherry-pick + git config --global alias.dh "diff --no-prefix HEAD" + git config --global alias.ps push + git config --global alias.st status + git config --global alias.lg "log --graph --format='%C(auto)%h%C(reset) %C(dim white)%an%C(reset) %C(green)%ai%C(reset) %C(auto)%d%C(reset)%n %s'" + git config --global alias.lg10 "log --graph --pretty=format:'%C(yellow)%h%C(auto)%d%Creset %s %C(white)- %an, %ar%Creset' -10" + git config --global alias.lg20 "log --graph --pretty=format:'%C(yellow)%h%C(auto)%d%Creset %s %C(white)- %an, %ar%Creset' -20" + git config --global alias.lg30 "log --graph --pretty=format:'%C(yellow)%h%C(auto)%d%Creset %s %C(white)- %an, %ar%Creset' -30" + git config --global alias.fp "format-patch --stdout --no-prefix" + + git config --global --add safe.directory /home/postgres/postgres +} + +function configure_vscode { + mkdir -p .vscode + cat < ".vscode/c_cpp_properties.json" +{ + "configurations": [ + { + "name": "Postgres Development Configuration", + "includePath": [ + "\${workspaceFolder}/**", + "\${workspaceFolder}/src/include/", + "\${workspaceFolder}/../build/src/include/" + ], + "cStandard": "c99", + "configurationProvider": "ms-vscode.makefile-tools" + } + ], + "version": 4 +} +EOL + + # Copy the launch.json and tasks.json files if they don't exist + if [ ! -f .vscode/launch.json ]; then + cp .devcontainer/launch.json .vscode/ + fi + if [ ! -f .vscode/tasks.json ]; then + cp .devcontainer/tasks.json .vscode/ + fi +} + +function configure_perf { + sudo sh -c "echo 0 > /proc/sys/kernel/kptr_restrict" + sudo su -c "echo -1 > /proc/sys/kernel/perf_event_paranoid" + + if [ ! -d /opt/freedom/tools/FlameGraph ]; then + git clone https://github.com/brendangregg/FlameGraph.git /opt/freedom/tools/FlameGraph + fi +} + +function configure_pg_plugins { + if [ ! -d /opt/freedom/extensions/pg_plugins ]; then + git clone https://github.com/michaelpq/pg_plugins.git /opt/freedom/extensions/pg_plugins + fi +} + +function main { + mkdir -p /opt/freedom/extensions + mkdir -p /opt/freedom/patches + mkdir -p /opt/freedom/tools + + configure_coredumps + configure_git + configure_perf + configure_pg_plugins + configure_vscode +} + +main $@ diff --git a/.devcontainer/tasks.json b/.devcontainer/tasks.json new file mode 100644 index 0000000000..9759b8ee4b --- /dev/null +++ b/.devcontainer/tasks.json @@ -0,0 +1,219 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + // See PostgreSQL meson doc link https://wiki.postgresql.org/wiki/Meson + "version": "2.0.0", + "tasks": [ + { + "label": "meson setup", + "type": "shell", + "command": "meson", + "args": [ + "setup", + "../build", // out of source tree + "--prefix=/home/postgres/pgsql", + "--buildtype=debug", // default is debugoptimized + "-Dc_args=-fno-inline-functions -fno-omit-frame-pointer -DCOPY_PARSE_PLAN_TREES -DWRITE_READ_PARSE_PLAN_TREES -DRAW_EXPRESSION_COVERAGE_TEST -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS", + "-Dcassert=true", + "-Dtap_tests=enabled", + "--werror" + ], + "problemMatcher": [], + "detail": "meson setup debug configuration" + }, + { + "label": "meson setup release", + "type": "shell", + "command": "meson", + "args": [ + "setup", + "../build", + "--prefix=/home/postgres/pgsql", + "--buildtype=release" + ], + "problemMatcher": [], + "detail": "meson setup release configuration" + }, + { + "label": "clear build directory", + "type": "shell", + "command": "rm -rf ../build", + "problemMatcher": [], + "detail": "clear build directory" + }, + { + "label": "ninja build", + "type": "shell", + "command": "ninja", + "args":[ + "-C", + "../build", + "-j", + "4" + ], + "problemMatcher": [], + "detail": "build postgres" + }, + { + "label": "regression tests", + "type": "shell", + "command": "meson test -C ../build -q --print-errorlogs --suite setup --suite regress", + "problemMatcher": [], + "detail": "run main regression tests" + }, + { + "label": "check-world", + "type": "shell", + "command": "meson test -C ../build -q --print-errorlogs", + "problemMatcher": [], + "detail": "run all tests" + }, + { + "label": "ninja install", + "type": "shell", + "command": "ninja install -C ../build", + "problemMatcher": [], + "detail": "install pgsql" + }, + { + "label": "init cluster", + "type": "shell", + "command": "/home/postgres/pgsql/bin/initdb", + "args": [ + "-D", + "/home/postgres/pgdata" + ], + "problemMatcher": [], + "detail": "init cluster using initdb" + }, + { + "label": "start cluster", + "type": "shell", + "command": "/home/postgres/pgsql/bin/pg_ctl", + "args": [ + "-D", + "/home/postgres/pgdata", + "-l", + "/home/postgres/pgdata/logfile", + "start" + ], + "problemMatcher": [], + "detail": "start db cluster" + }, + { + "label": "restart cluster", + "type": "shell", + "command": "/home/postgres/pgsql/bin/pg_ctl", + "args": [ + "-D", + "/home/postgres/pgdata", + "-l", + "/home/postgres/pgdata/logfile", + "restart" + ], + "problemMatcher": [], + "detail": "restart db cluster" + }, + { + "label": "stop cluster", + "type": "shell", + "command": "/home/postgres/pgsql/bin/pg_ctl", + "args": [ + "-D", + "/home/postgres/pgdata", + "stop" + ], + "problemMatcher": [], + "detail": "stop db cluster" + }, + { + "label": "clear pgdata", + "type": "shell", + "command": "rm -rf /home/postgres/pgdata", + "problemMatcher": [], + "detail": "clear pgdata directory" + }, + { + "label": "install pg_bsd_indent", + "type": "shell", + "command": "sudo cp ../build/src/tools/pg_bsd_indent/pg_bsd_indent /usr/local/bin", + "problemMatcher": [], + "detail": "install pg_bsd_indent to /usr/local/bin" + }, + { + "label": "pgindent", + "type": "shell", + "command": "src/tools/pgindent/pgindent ${input:selectDir}", + "problemMatcher": [], + "detail": "run pgindent on selected directory" + }, + { + "label": "format patch", + "type": "shell", + "command": "git format-patch -o /opt/freedom/patches -${input:formatPatchNumber} -v ${input:formatPatchVersion}", + "problemMatcher": [], + "detail": "generate patches from the topmost commits" + }, + { + "label": "apply patch", + "type": "shell", + "command": "git am /opt/freedom/patches/${input:patchFile}", + "problemMatcher": [], + "detail": "apply patch" + }, + { + "label": "generate flamegraph", + "type": "shell", + "command": "perf record -o ../perf_${input:processId}.data -g -F 99 -p ${input:processId} -- sleep 60 && perf script -i ../perf_${input:processId}.data | /opt/freedom/tools/FlameGraph/stackcollapse-perf.pl | /opt/freedom/tools/FlameGraph/flamegraph.pl > /opt/freedom/perf_${input:processId}.svg", + "problemMatcher": [], + "detail": "generate flamegraph" + } + ], + "inputs": [ + { + "id": "selectDir", + "type": "command", + "command": "extension.commandvariable.pickStringRemember", + "args": { + "description": "Which directory to run for pgindent?", + "options": [ + ["Use previous directory", "${remember:srcSubDir}"], + ["Pick directory", "${pickFile:srcSubDir}"] + ], + "default": null, + "pickFile": { + "srcSubDir": { + "description": "Which directory?", + "include": "src/**", + "showDirs": true, + "keyRemember": "srcSubDir" + } + } + } + }, + { + "id": "formatPatchNumber", + "description": "patches from the topmost commits", + "type": "promptString", + "default": "1" + }, + { + "id": "formatPatchVersion", + "description": "-th iteration of the topic", + "type": "promptString", + "default": "1" + }, + { + "id": "patchFile", + "description": "Which patch file to apply?", + "type": "promptString", + "default": "*" + }, + { + "id": "processId", + "description": "Which process to perf?", + "type": "promptString", + "default": "" + } + ] +} diff --git a/.gitignore b/.gitignore index 4e911395fe..952e4c93ef 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ win32ver.rc *.exe lib*dll.def lib*.pc +.DS_Store # Local excludes in root directory /GNUmakefile @@ -43,3 +44,5 @@ lib*.pc /Release/ /tmp_install/ /portlock/ +/.devcontainer/ +/.vscode/ -- 2.39.2