| /*------------------------------------------------------------------------- |
| * |
| * segmentinfo.c |
| * Send segment information to perfmon |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include <unistd.h> |
| #include <signal.h> |
| |
| #include "postmaster/perfmon_segmentinfo.h" |
| #include "postmaster/fork_process.h" |
| #include "postmaster/postmaster.h" |
| |
| #include "storage/ipc.h" |
| #include "storage/proc.h" |
| #include "storage/backendid.h" |
| #include "storage/pmsignal.h" /* PostmasterIsAlive */ |
| |
| #include "utils/resowner.h" |
| #include "utils/ps_status.h" |
| |
| #include "miscadmin.h" |
| #include "libpq/pqsignal.h" |
| #include "tcop/tcopprot.h" |
| #include "cdb/cdbvars.h" |
| #include "utils/vmem_tracker.h" |
| |
| /* Sender-related routines */ |
| static void SegmentInfoSender(void); |
| static void SegmentInfoSenderLoop(void); |
| NON_EXEC_STATIC void SegmentInfoSenderMain(int argc, char *argv[]); |
| static void SegmentInfoRequestShutdown(SIGNAL_ARGS); |
| |
| static volatile bool senderShutdownRequested = false; |
| static volatile bool isSenderProcess = false; |
| |
| /* Static gpmon_seginfo_t item, (re)used for sending UDP packets. */ |
| static gpmon_packet_t seginfopkt; |
| |
| /* Maximum dynamic memory allocation in bytes */ |
| static uint64 mem_alloc_max; |
| |
| /* GpmonPkt-related routines */ |
| static void InitSegmentInfoGpmonPkt(gpmon_packet_t *gpmon_pkt); |
| static void UpdateSegmentInfoGpmonPkt(gpmon_packet_t *gpmon_pkt); |
| |
| /** |
| * Main entry point for segment info process. This forks off a sender process |
| * and calls SegmentInfoSenderMain(), which does all the setup. |
| * |
| * This code is heavily based on pgarch.c, q.v. |
| */ |
| int |
| perfmon_segmentinfo_start(void) |
| { |
| pid_t segmentInfoId = -1; |
| |
| switch ((segmentInfoId = fork_process())) |
| { |
| case -1: |
| ereport(LOG, |
| (errmsg("could not fork stats sender process: %m"))); |
| return 0; |
| |
| case 0: |
| /* in postmaster child ... */ |
| /* Close the postmaster's sockets */ |
| ClosePostmasterPorts(false); |
| |
| SegmentInfoSenderMain(0, NULL); |
| break; |
| default: |
| return (int)segmentInfoId; |
| } |
| |
| /* shouldn't get here */ |
| Assert(false); |
| return 0; |
| } |
| |
| |
| /** |
| * This method is called after fork of the stats sender process. It sets up signal |
| * handlers and does initialization that is required by a postgres backend. |
| */ |
| NON_EXEC_STATIC void SegmentInfoSenderMain(int argc, char *argv[]) |
| { |
| sigjmp_buf local_sigjmp_buf; |
| |
| IsUnderPostmaster = true; |
| isSenderProcess = true; |
| |
| /* reset MyProcPid */ |
| MyProcPid = getpid(); |
| |
| /* Lose the postmaster's on-exit routines */ |
| on_exit_reset(); |
| |
| /* Identify myself via ps */ |
| init_ps_display("stats sender process", "", "", ""); |
| |
| SetProcessingMode(InitProcessing); |
| |
| /* Set up signal handlers, see equivalent code in tcop/postgres.c. */ |
| pqsignal(SIGHUP, SIG_IGN); |
| pqsignal(SIGINT, SIG_IGN); |
| pqsignal(SIGALRM, SIG_IGN); |
| pqsignal(SIGPIPE, SIG_IGN); |
| pqsignal(SIGUSR1, SIG_IGN); |
| |
| pqsignal(SIGTERM, die); |
| pqsignal(SIGQUIT, die); |
| pqsignal(SIGUSR2, SegmentInfoRequestShutdown); |
| |
| pqsignal(SIGFPE, FloatExceptionHandler); |
| pqsignal(SIGCHLD, SIG_DFL); |
| |
| /* Copied from bgwriter */ |
| CurrentResourceOwner = ResourceOwnerCreate(NULL, "Segment info sender process"); |
| |
| /* Early initialization */ |
| BaseInit(); |
| |
| /* See InitPostgres()... */ |
| InitProcess(); |
| |
| SetProcessingMode(NormalProcessing); |
| |
| /* |
| * If an exception is encountered, processing resumes here. |
| * |
| * See notes in postgres.c about the design of this coding. |
| */ |
| if (sigsetjmp(local_sigjmp_buf, 1) != 0) |
| { |
| /* Prevents interrupts while cleaning up */ |
| HOLD_INTERRUPTS(); |
| |
| /* Report the error to the server log */ |
| EmitErrorReport(); |
| |
| /* |
| * We can now go away. Note that because we'll call InitProcess, a |
| * callback will be registered to do ProcKill, which will clean up |
| * necessary state. |
| */ |
| proc_exit(0); |
| } |
| |
| /* We can now handle ereport(ERROR) */ |
| PG_exception_stack = &local_sigjmp_buf; |
| |
| PG_SETMASK(&UnBlockSig); |
| |
| MyBackendId = InvalidBackendId; |
| |
| /* |
| * Save hawq_re_memory_overcommit_max value in bytes. |
| * This value cannot change after starting the server. |
| */ |
| mem_alloc_max = VmemTracker_GetVmemLimitBytes(); |
| |
| /* Init gpmon connection */ |
| gpmon_init(); |
| |
| /* Create and initialize gpmon_pkt */ |
| InitSegmentInfoGpmonPkt(&seginfopkt); |
| |
| /* main loop */ |
| SegmentInfoSenderLoop(); |
| |
| /* One iteration done, go away */ |
| proc_exit(0); |
| } |
| |
| /** |
| * Main loop of the sender process. It wakes up every |
| * gp_perfmon_segment_interval ms to send segment |
| * information to perfmon |
| */ |
| static void |
| SegmentInfoSenderLoop(void) |
| { |
| |
| for (;;) |
| { |
| CHECK_FOR_INTERRUPTS(); |
| |
| if (senderShutdownRequested) |
| { |
| break; |
| } |
| |
| /* no need to live on if postmaster has died */ |
| if (!PostmasterIsAlive(true)) |
| exit(1); |
| |
| SegmentInfoSender(); |
| |
| /* Sleep a while. */ |
| Assert(gp_perfmon_segment_interval > 0); |
| pg_usleep(gp_perfmon_segment_interval * 1000); |
| } /* end server loop */ |
| |
| return; |
| } |
| |
| /** |
| * Note the request to shut down. |
| */ |
| static void |
| SegmentInfoRequestShutdown(SIGNAL_ARGS) |
| { |
| senderShutdownRequested = true; |
| } |
| |
| /** |
| * Sends a UDP packet to perfmon containing current segment statistics. |
| */ |
| static void |
| SegmentInfoSender() |
| { |
| UpdateSegmentInfoGpmonPkt(&seginfopkt); |
| gpmon_send(&seginfopkt); |
| } |
| |
| /** |
| * InitSegmentInfoGpmonPkt -- initialize the gpmon packet. |
| */ |
| static void |
| InitSegmentInfoGpmonPkt(gpmon_packet_t *gpmon_pkt) |
| { |
| Assert(gpmon_pkt); |
| memset(gpmon_pkt, 0, sizeof(gpmon_packet_t)); |
| |
| gpmon_pkt->magic = GPMON_MAGIC; |
| gpmon_pkt->version = GPMON_PACKET_VERSION; |
| gpmon_pkt->pkttype = GPMON_PKTTYPE_SEGINFO; |
| |
| gpmon_pkt->u.seginfo.dbid = MASTER_DBID; |
| UpdateSegmentInfoGpmonPkt(gpmon_pkt); |
| } |
| |
| /** |
| * UpdateSegmentInfoGpmonPkt -- update segment info |
| */ |
| static void |
| UpdateSegmentInfoGpmonPkt(gpmon_packet_t *gpmon_pkt) |
| { |
| Assert(gpmon_pkt); |
| Assert(GPMON_PKTTYPE_SEGINFO == gpmon_pkt->pkttype); |
| |
| uint64 mem_alloc_used = VmemTracker_GetVmemLimitBytes() - VmemTracker_GetAvailableVmemBytes(); |
| uint64 mem_alloc_available = mem_alloc_max - mem_alloc_used; |
| gpmon_pkt->u.seginfo.dynamic_memory_used = mem_alloc_used; |
| gpmon_pkt->u.seginfo.dynamic_memory_available = mem_alloc_available; |
| } |